aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeffrey Wilcke <jeffrey@ethereum.org>2015-06-23 22:44:03 +0800
committerJeffrey Wilcke <jeffrey@ethereum.org>2015-06-23 22:44:03 +0800
commit6b5532ab0d0e88f27af539840c58cbd6de13de90 (patch)
tree84f1856e1b3fb2fd927e6ef9c59983e4fb347a8c
parent139439dcdc14e448bfdbd509bb135faa92337a28 (diff)
parent2b3957f3737b56622e38425a52caea2a836f8b63 (diff)
downloaddexon-6b5532ab0d0e88f27af539840c58cbd6de13de90.tar.gz
dexon-6b5532ab0d0e88f27af539840c58cbd6de13de90.tar.zst
dexon-6b5532ab0d0e88f27af539840c58cbd6de13de90.zip
Merge pull request #1279 from bas-vk/rpc-http
Integrate console and remove old rpc package structure
-rw-r--r--Makefile5
-rw-r--r--cmd/console/admin.go9
-rw-r--r--cmd/console/contracts.go6
-rw-r--r--cmd/console/js.go454
-rw-r--r--cmd/console/main.go103
-rw-r--r--cmd/geth/admin.go937
-rw-r--r--cmd/geth/js.go225
-rw-r--r--cmd/geth/js_test.go19
-rw-r--r--cmd/geth/main.go71
-rw-r--r--cmd/utils/flags.go27
-rw-r--r--core/transaction_pool.go2
-rw-r--r--rpc/api.go646
-rw-r--r--rpc/api/admin.go95
-rw-r--r--rpc/api/admin_args.go52
-rw-r--r--rpc/api/admin_js.go14
-rw-r--r--rpc/api/api.go40
-rw-r--r--rpc/api/api_test.go111
-rw-r--r--rpc/api/args.go58
-rw-r--r--rpc/api/args_test.go (renamed from rpc/args_test.go)12
-rw-r--r--rpc/api/db.go128
-rw-r--r--rpc/api/db_args.go110
-rw-r--r--rpc/api/db_js.go41
-rw-r--r--rpc/api/debug.go2
-rw-r--r--rpc/api/eth.go93
-rw-r--r--rpc/api/eth_args.go48
-rw-r--r--rpc/api/eth_js.go17
-rw-r--r--rpc/api/mergedapi.go12
-rw-r--r--rpc/api/mergedapi_js.go1
-rw-r--r--rpc/api/miner.go2
-rw-r--r--rpc/api/net.go4
-rw-r--r--rpc/api/personal.go2
-rw-r--r--rpc/api/shh.go2
-rw-r--r--rpc/api/txpool.go2
-rw-r--r--rpc/api/utils.go51
-rw-r--r--rpc/api/web3.go2
-rw-r--r--rpc/api/web3_args.go24
-rw-r--r--rpc/api_test.go205
-rw-r--r--rpc/args.go1230
-rw-r--r--rpc/comms/comms.go104
-rw-r--r--rpc/comms/http.go190
-rw-r--r--rpc/comms/http_net.go166
-rw-r--r--rpc/comms/inproc.go66
-rw-r--r--rpc/comms/ipc.go73
-rw-r--r--rpc/comms/ipc_unix.go43
-rw-r--r--rpc/comms/ipc_windows.go13
-rw-r--r--rpc/http.go163
-rw-r--r--rpc/jeth.go114
-rw-r--r--rpc/responses.go316
-rw-r--r--rpc/responses_test.go295
-rw-r--r--rpc/shared/types.go27
-rw-r--r--rpc/shared/utils.go27
-rw-r--r--rpc/types.go381
-rw-r--r--rpc/types_test.go204
53 files changed, 1725 insertions, 5319 deletions
diff --git a/Makefile b/Makefile
index b243d2679..03e3bf4c6 100644
--- a/Makefile
+++ b/Makefile
@@ -10,11 +10,6 @@ geth:
@echo "Done building."
@echo "Run \"$(GOBIN)/geth\" to launch geth."
-console:
- build/env.sh go install -v $(shell build/ldflags.sh) ./cmd/console
- @echo "Done building."
- @echo "Run \"$(GOBIN)/console\" to launch the console."
-
mist:
build/env.sh go install -v $(shell build/ldflags.sh) ./cmd/mist
@echo "Done building."
diff --git a/cmd/console/admin.go b/cmd/console/admin.go
deleted file mode 100644
index dee88e3a0..000000000
--- a/cmd/console/admin.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package main
-
-/*
-node admin bindings
-*/
-
-func (js *jsre) adminBindings() {
-
-}
diff --git a/cmd/console/contracts.go b/cmd/console/contracts.go
deleted file mode 100644
index 1f27838d1..000000000
--- a/cmd/console/contracts.go
+++ /dev/null
@@ -1,6 +0,0 @@
-package main
-
-var (
- globalRegistrar = `var GlobalRegistrar = web3.eth.contract([{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"name","outputs":[{"name":"o_name","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"content","outputs":[{"name":"","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"addr","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserve","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"subRegistrar","outputs":[{"name":"o_subRegistrar","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_newOwner","type":"address"}],"name":"transfer","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_registrar","type":"address"}],"name":"setSubRegistrar","outputs":[],"type":"function"},{"constant":false,"inputs":[],"name":"Registrar","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_a","type":"address"},{"name":"_primary","type":"bool"}],"name":"setAddress","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_content","type":"bytes32"}],"name":"setContent","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"disown","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"register","outputs":[{"name":"","type":"address"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"}],"name":"Changed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"addr","type":"address"}],"name":"PrimaryChanged","type":"event"}]);`
- globalRegistrarAddr = "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
-)
diff --git a/cmd/console/js.go b/cmd/console/js.go
deleted file mode 100644
index 15ea9bedd..000000000
--- a/cmd/console/js.go
+++ /dev/null
@@ -1,454 +0,0 @@
-// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
-//
-// This library 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 2.1 of the License, or (at your option) any later version.
-//
-// This 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
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this library; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-// MA 02110-1301 USA
-
-package main
-
-import (
- "bufio"
- "fmt"
- "math/big"
- "os"
- "os/signal"
- "path/filepath"
- "strings"
-
- "encoding/json"
-
- "sort"
-
- "github.com/codegangsta/cli"
- "github.com/ethereum/go-ethereum/cmd/utils"
- "github.com/ethereum/go-ethereum/common/docserver"
- re "github.com/ethereum/go-ethereum/jsre"
- "github.com/ethereum/go-ethereum/rpc"
- "github.com/ethereum/go-ethereum/rpc/api"
- "github.com/ethereum/go-ethereum/rpc/codec"
- "github.com/ethereum/go-ethereum/rpc/comms"
- "github.com/ethereum/go-ethereum/rpc/shared"
- "github.com/peterh/liner"
- "github.com/robertkrimen/otto"
-)
-
-type prompter interface {
- AppendHistory(string)
- Prompt(p string) (string, error)
- PasswordPrompt(p string) (string, error)
-}
-
-type dumbterm struct{ r *bufio.Reader }
-
-func (r dumbterm) Prompt(p string) (string, error) {
- fmt.Print(p)
- line, err := r.r.ReadString('\n')
- return strings.TrimSuffix(line, "\n"), err
-}
-
-func (r dumbterm) PasswordPrompt(p string) (string, error) {
- fmt.Println("!! Unsupported terminal, password will echo.")
- fmt.Print(p)
- input, err := bufio.NewReader(os.Stdin).ReadString('\n')
- fmt.Println()
- return input, err
-}
-
-func (r dumbterm) AppendHistory(string) {}
-
-type jsre struct {
- re *re.JSRE
- wait chan *big.Int
- ps1 string
- atexit func()
- datadir string
- prompter
-}
-
-var (
- loadedModulesMethods map[string][]string
-)
-
-func loadAutoCompletion(js *jsre, ipcpath string) {
- modules, err := js.suportedApis(ipcpath)
- if err != nil {
- utils.Fatalf("Unable to determine supported modules - %v", err)
- }
-
- loadedModulesMethods = make(map[string][]string)
- for module, _ := range modules {
- loadedModulesMethods[module] = api.AutoCompletion[module]
- }
-}
-
-func keywordCompleter(line string) []string {
- results := make([]string, 0)
-
- if strings.Contains(line, ".") {
- elements := strings.Split(line, ".")
- if len(elements) == 2 {
- module := elements[0]
- partialMethod := elements[1]
- if methods, found := loadedModulesMethods[module]; found {
- for _, method := range methods {
- if strings.HasPrefix(method, partialMethod) { // e.g. debug.se
- results = append(results, module+"."+method)
- }
- }
- }
- }
- } else {
- for module, methods := range loadedModulesMethods {
- if line == module { // user typed in full module name, show all methods
- for _, method := range methods {
- results = append(results, module+"."+method)
- }
- } else if strings.HasPrefix(module, line) { // partial method name, e.g. admi
- results = append(results, module)
- }
- }
- }
- return results
-}
-
-func apiWordCompleter(line string, pos int) (head string, completions []string, tail string) {
- if len(line) == 0 {
- return "", nil, ""
- }
-
- i := 0
- for i = pos - 1; i > 0; i-- {
- if line[i] == '.' || (line[i] >= 'a' && line[i] <= 'z') || (line[i] >= 'A' && line[i] <= 'Z') {
- continue
- }
- if i >= 3 && line[i] == '3' && line[i-3] == 'w' && line[i-2] == 'e' && line[i-1] == 'b' {
- continue
- }
- i += 1
- break
- }
-
- begin := line[:i]
- keyword := line[i:pos]
- end := line[pos:]
-
- completionWords := keywordCompleter(keyword)
- return begin, completionWords, end
-}
-
-func newJSRE(libPath, ipcpath string) *jsre {
- js := &jsre{ps1: "> "}
- js.wait = make(chan *big.Int)
-
- // update state in separare forever blocks
- js.re = re.New(libPath)
- js.apiBindings(ipcpath)
-
- if !liner.TerminalSupported() {
- js.prompter = dumbterm{bufio.NewReader(os.Stdin)}
- } else {
- lr := liner.NewLiner()
- js.withHistory(func(hist *os.File) { lr.ReadHistory(hist) })
- lr.SetCtrlCAborts(true)
- loadAutoCompletion(js, ipcpath)
- lr.SetWordCompleter(apiWordCompleter)
- lr.SetTabCompletionStyle(liner.TabPrints)
- js.prompter = lr
- js.atexit = func() {
- js.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) })
- lr.Close()
- close(js.wait)
- }
- }
- return js
-}
-
-func (js *jsre) apiBindings(ipcpath string) {
- ethApi := rpc.NewEthereumApi(nil)
- jeth := rpc.NewJeth(ethApi, js.re, ipcpath)
-
- js.re.Set("jeth", struct{}{})
- t, _ := js.re.Get("jeth")
- jethObj := t.Object()
- jethObj.Set("send", jeth.SendIpc)
- jethObj.Set("sendAsync", jeth.SendIpc)
-
- err := js.re.Compile("bignumber.js", re.BigNumber_JS)
- if err != nil {
- utils.Fatalf("Error loading bignumber.js: %v", err)
- }
-
- err = js.re.Compile("ethereum.js", re.Web3_JS)
- if err != nil {
- utils.Fatalf("Error loading web3.js: %v", err)
- }
-
- _, err = js.re.Eval("var web3 = require('web3');")
- if err != nil {
- utils.Fatalf("Error requiring web3: %v", err)
- }
-
- _, err = js.re.Eval("web3.setProvider(jeth)")
- if err != nil {
- utils.Fatalf("Error setting web3 provider: %v", err)
- }
-
- apis, err := js.suportedApis(ipcpath)
- if err != nil {
- utils.Fatalf("Unable to determine supported api's: %v", err)
- }
-
- // load only supported API's in javascript runtime
- shortcuts := "var eth = web3.eth; "
- for apiName, _ := range apis {
- if apiName == api.Web3ApiName || apiName == api.EthApiName {
- continue // manually mapped
- }
-
- if err = js.re.Compile(fmt.Sprintf("%s.js", apiName), api.Javascript(apiName)); err == nil {
- shortcuts += fmt.Sprintf("var %s = web3.%s; ", apiName, apiName)
- } else {
- utils.Fatalf("Error loading %s.js: %v", apiName, err)
- }
- }
-
- _, err = js.re.Eval(shortcuts)
-
- if err != nil {
- utils.Fatalf("Error setting namespaces: %v", err)
- }
-
- js.re.Eval(globalRegistrar + "registrar = GlobalRegistrar.at(\"" + globalRegistrarAddr + "\");")
-}
-
-var ds, _ = docserver.New("/")
-
-/*
-func (self *jsre) ConfirmTransaction(tx string) bool {
- if self.ethereum.NatSpec {
- notice := natspec.GetNotice(self.xeth, tx, ds)
- fmt.Println(notice)
- answer, _ := self.Prompt("Confirm Transaction [y/n]")
- return strings.HasPrefix(strings.Trim(answer, " "), "y")
- } else {
- return true
- }
-}
-
-func (self *jsre) UnlockAccount(addr []byte) bool {
- fmt.Printf("Please unlock account %x.\n", addr)
- pass, err := self.PasswordPrompt("Passphrase: ")
- if err != nil {
- return false
- }
- // TODO: allow retry
- if err := self.ethereum.AccountManager().Unlock(common.BytesToAddress(addr), pass); err != nil {
- return false
- } else {
- fmt.Println("Account is now unlocked for this session.")
- return true
- }
-}
-*/
-
-func (self *jsre) exec(filename string) error {
- if err := self.re.Exec(filename); err != nil {
- self.re.Stop(false)
- return fmt.Errorf("Javascript Error: %v", err)
- }
- self.re.Stop(true)
- return nil
-}
-
-func (self *jsre) suportedApis(ipcpath string) (map[string]string, error) {
- config := comms.IpcConfig{
- Endpoint: ipcpath,
- }
-
- client, err := comms.NewIpcClient(config, codec.JSON)
- if err != nil {
- return nil, err
- }
-
- req := shared.Request{
- Id: 1,
- Jsonrpc: "2.0",
- Method: "modules",
- }
-
- err = client.Send(req)
- if err != nil {
- return nil, err
- }
-
- res, err := client.Recv()
- if err != nil {
- return nil, err
- }
-
- if sucRes, ok := res.(shared.SuccessResponse); ok {
- data, _ := json.Marshal(sucRes.Result)
- apis := make(map[string]string)
- err = json.Unmarshal(data, &apis)
- if err == nil {
- return apis, nil
- }
- }
-
- return nil, fmt.Errorf("Unable to determine supported API's")
-}
-
-// show summary of current geth instance
-func (self *jsre) welcome(ipcpath string) {
- self.re.Eval(`console.log('instance: ' + web3.version.client);`)
- self.re.Eval(`console.log(' datadir: ' + admin.datadir);`)
- self.re.Eval(`console.log("coinbase: " + eth.coinbase);`)
- self.re.Eval(`var lastBlockTimestamp = 1000 * eth.getBlock(eth.blockNumber).timestamp`)
- self.re.Eval(`console.log("at block: " + eth.blockNumber + " (" + new Date(lastBlockTimestamp).toLocaleDateString()
- + " " + new Date(lastBlockTimestamp).toLocaleTimeString() + ")");`)
-
- if modules, err := self.suportedApis(ipcpath); err == nil {
- loadedModules := make([]string, 0)
- for api, version := range modules {
- loadedModules = append(loadedModules, fmt.Sprintf("%s:%s", api, version))
- }
- sort.Strings(loadedModules)
-
- self.re.Eval(fmt.Sprintf("var modules = '%s';", strings.Join(loadedModules, " ")))
- self.re.Eval(`console.log(" modules: " + modules);`)
- }
-}
-
-func (self *jsre) batch(args cli.Args) {
- statement := strings.Join(args, " ")
- val, err := self.re.Run(statement)
-
- if err != nil {
- fmt.Printf("error: %v", err)
- } else if val.IsDefined() && val.IsObject() {
- obj, _ := self.re.Get("ret_result")
- fmt.Printf("%v", obj)
- } else if val.IsDefined() {
- fmt.Printf("%v", val)
- }
-
- if self.atexit != nil {
- self.atexit()
- }
-
- self.re.Stop(false)
-}
-
-func (self *jsre) interactive(ipcpath string) {
- self.welcome(ipcpath)
-
- // Read input lines.
- prompt := make(chan string)
- inputln := make(chan string)
- go func() {
- defer close(inputln)
- for {
- line, err := self.Prompt(<-prompt)
- if err != nil {
- return
- }
- inputln <- line
- }
- }()
- // Wait for Ctrl-C, too.
- sig := make(chan os.Signal, 1)
- signal.Notify(sig, os.Interrupt)
-
- defer func() {
- if self.atexit != nil {
- self.atexit()
- }
- self.re.Stop(false)
- }()
- for {
- prompt <- self.ps1
- select {
- case <-sig:
- fmt.Println("caught interrupt, exiting")
- return
- case input, ok := <-inputln:
- if !ok || indentCount <= 0 && input == "exit" {
- return
- }
- if input == "" {
- continue
- }
- str += input + "\n"
- self.setIndent()
- if indentCount <= 0 {
- hist := str[:len(str)-1]
- self.AppendHistory(hist)
- self.parseInput(str)
- str = ""
- }
- }
- }
-}
-
-func (self *jsre) withHistory(op func(*os.File)) {
- hist, err := os.OpenFile(filepath.Join(self.datadir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm)
- if err != nil {
- fmt.Printf("unable to open history file: %v\n", err)
- return
- }
- op(hist)
- hist.Close()
-}
-
-func (self *jsre) parseInput(code string) {
- defer func() {
- if r := recover(); r != nil {
- fmt.Println("[native] error", r)
- }
- }()
- value, err := self.re.Run(code)
- if err != nil {
- if ottoErr, ok := err.(*otto.Error); ok {
- fmt.Println(ottoErr.String())
- } else {
- fmt.Println(err)
- }
- return
- }
- self.printValue(value)
-}
-
-var indentCount = 0
-var str = ""
-
-func (self *jsre) setIndent() {
- open := strings.Count(str, "{")
- open += strings.Count(str, "(")
- closed := strings.Count(str, "}")
- closed += strings.Count(str, ")")
- indentCount = open - closed
- if indentCount <= 0 {
- self.ps1 = "> "
- } else {
- self.ps1 = strings.Join(make([]string, indentCount*2), "..")
- self.ps1 += " "
- }
-}
-
-func (self *jsre) printValue(v interface{}) {
- val, err := self.re.PrettyPrint(v)
- if err == nil {
- fmt.Printf("%v", val)
- }
-}
diff --git a/cmd/console/main.go b/cmd/console/main.go
deleted file mode 100644
index 00a9ca9c4..000000000
--- a/cmd/console/main.go
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- 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/>.
-*/
-/**
- * @authors
- * Jeffrey Wilcke <i@jev.io>
- */
-package main
-
-import (
- "fmt"
- "io"
- "os"
-
- "github.com/codegangsta/cli"
- "github.com/ethereum/go-ethereum/cmd/utils"
- "github.com/ethereum/go-ethereum/logger"
- "github.com/mattn/go-colorable"
- "github.com/mattn/go-isatty"
-)
-
-const (
- ClientIdentifier = "Geth console"
- Version = "0.9.27"
-)
-
-var (
- gitCommit string // set via linker flag
- nodeNameVersion string
- app = utils.NewApp(Version, "the geth console")
-)
-
-func init() {
- if gitCommit == "" {
- nodeNameVersion = Version
- } else {
- nodeNameVersion = Version + "-" + gitCommit[:8]
- }
-
- app.Action = run
- app.Flags = []cli.Flag{
- utils.IPCPathFlag,
- utils.VerbosityFlag,
- utils.JSpathFlag,
- }
-
- app.Before = func(ctx *cli.Context) error {
- utils.SetupLogger(ctx)
- return nil
- }
-}
-
-func main() {
- // Wrap the standard output with a colorified stream (windows)
- if isatty.IsTerminal(os.Stdout.Fd()) {
- if pr, pw, err := os.Pipe(); err == nil {
- go io.Copy(colorable.NewColorableStdout(), pr)
- os.Stdout = pw
- }
- }
-
- var interrupted = false
- utils.RegisterInterrupt(func(os.Signal) {
- interrupted = true
- })
- utils.HandleInterrupt()
-
- if err := app.Run(os.Args); err != nil {
- fmt.Fprintln(os.Stderr, "Error: ", err)
- }
-
- // we need to run the interrupt callbacks in case gui is closed
- // this skips if we got here by actual interrupt stopping the GUI
- if !interrupted {
- utils.RunInterruptCallbacks(os.Interrupt)
- }
- logger.Flush()
-}
-
-func run(ctx *cli.Context) {
- jspath := ctx.GlobalString(utils.JSpathFlag.Name)
- ipcpath := utils.IpcSocketPath(ctx)
- repl := newJSRE(jspath, ipcpath)
-
- if ctx.Args().Present() {
- repl.batch(ctx.Args())
- } else {
- repl.interactive(ipcpath)
- }
-}
diff --git a/cmd/geth/admin.go b/cmd/geth/admin.go
deleted file mode 100644
index 33ef69792..000000000
--- a/cmd/geth/admin.go
+++ /dev/null
@@ -1,937 +0,0 @@
-package main
-
-import (
- "encoding/json"
- "errors"
- "fmt"
- "math/big"
- "strconv"
- "time"
-
- "github.com/ethereum/ethash"
- "github.com/ethereum/go-ethereum/accounts"
- "github.com/ethereum/go-ethereum/cmd/utils"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/compiler"
- "github.com/ethereum/go-ethereum/common/natspec"
- "github.com/ethereum/go-ethereum/common/resolver"
- "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/logger/glog"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/ethereum/go-ethereum/rpc"
- "github.com/ethereum/go-ethereum/xeth"
- "github.com/robertkrimen/otto"
- "gopkg.in/fatih/set.v0"
-)
-
-/*
-node admin bindings
-*/
-
-func (js *jsre) adminBindings() {
- ethO, _ := js.re.Get("eth")
- eth := ethO.Object()
- eth.Set("pendingTransactions", js.pendingTransactions)
- eth.Set("resend", js.resend)
- eth.Set("sign", js.sign)
-
- js.re.Set("admin", struct{}{})
- t, _ := js.re.Get("admin")
- admin := t.Object()
- admin.Set("addPeer", js.addPeer)
- admin.Set("startRPC", js.startRPC)
- admin.Set("stopRPC", js.stopRPC)
- admin.Set("nodeInfo", js.nodeInfo)
- admin.Set("peers", js.peers)
- admin.Set("newAccount", js.newAccount)
- admin.Set("unlock", js.unlock)
- admin.Set("import", js.importChain)
- admin.Set("export", js.exportChain)
- admin.Set("verbosity", js.verbosity)
- admin.Set("progress", js.syncProgress)
- admin.Set("setSolc", js.setSolc)
-
- admin.Set("contractInfo", struct{}{})
- t, _ = admin.Get("contractInfo")
- cinfo := t.Object()
- // newRegistry officially not documented temporary option
- cinfo.Set("start", js.startNatSpec)
- cinfo.Set("stop", js.stopNatSpec)
- cinfo.Set("newRegistry", js.newRegistry)
- cinfo.Set("get", js.getContractInfo)
- cinfo.Set("register", js.register)
- cinfo.Set("registerUrl", js.registerUrl)
- // cinfo.Set("verify", js.verify)
-
- admin.Set("miner", struct{}{})
- t, _ = admin.Get("miner")
- miner := t.Object()
- miner.Set("start", js.startMining)
- miner.Set("stop", js.stopMining)
- miner.Set("hashrate", js.hashrate)
- miner.Set("setExtra", js.setExtra)
- miner.Set("setGasPrice", js.setGasPrice)
- miner.Set("startAutoDAG", js.startAutoDAG)
- miner.Set("stopAutoDAG", js.stopAutoDAG)
- miner.Set("makeDAG", js.makeDAG)
-
- admin.Set("txPool", struct{}{})
- t, _ = admin.Get("txPool")
- txPool := t.Object()
- txPool.Set("pending", js.allPendingTransactions)
- txPool.Set("queued", js.allQueuedTransactions)
-
- admin.Set("debug", struct{}{})
- t, _ = admin.Get("debug")
- debug := t.Object()
- js.re.Set("sleep", js.sleep)
- debug.Set("backtrace", js.backtrace)
- debug.Set("printBlock", js.printBlock)
- debug.Set("dumpBlock", js.dumpBlock)
- debug.Set("getBlockRlp", js.getBlockRlp)
- debug.Set("setHead", js.setHead)
- debug.Set("processBlock", js.debugBlock)
- debug.Set("seedhash", js.seedHash)
- debug.Set("insertBlock", js.insertBlockRlp)
- // undocumented temporary
- debug.Set("waitForBlocks", js.waitForBlocks)
-}
-
-// generic helper to getBlock by Number/Height or Hex depending on autodetected input
-// if argument is missing the current block is returned
-// if block is not found or there is problem with decoding
-// the appropriate value is returned and block is guaranteed to be nil
-func (js *jsre) getBlock(call otto.FunctionCall) (*types.Block, error) {
- var block *types.Block
- if len(call.ArgumentList) > 0 {
- if call.Argument(0).IsNumber() {
- num, _ := call.Argument(0).ToInteger()
- block = js.ethereum.ChainManager().GetBlockByNumber(uint64(num))
- } else if call.Argument(0).IsString() {
- hash, _ := call.Argument(0).ToString()
- block = js.ethereum.ChainManager().GetBlock(common.HexToHash(hash))
- } else {
- return nil, errors.New("invalid argument for dump. Either hex string or number")
- }
- } else {
- block = js.ethereum.ChainManager().CurrentBlock()
- }
-
- if block == nil {
- return nil, errors.New("block not found")
- }
- return block, nil
-}
-
-func (js *jsre) seedHash(call otto.FunctionCall) otto.Value {
- if len(call.ArgumentList) > 0 {
- if call.Argument(0).IsNumber() {
- num, _ := call.Argument(0).ToInteger()
- hash, err := ethash.GetSeedHash(uint64(num))
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
- v, _ := call.Otto.ToValue(fmt.Sprintf("0x%x", hash))
- return v
- } else {
- fmt.Println("arg not a number")
- }
- } else {
- fmt.Println("requires number argument")
- }
-
- return otto.UndefinedValue()
-}
-
-func (js *jsre) allPendingTransactions(call otto.FunctionCall) otto.Value {
- txs := js.ethereum.TxPool().GetTransactions()
-
- ltxs := make([]*tx, len(txs))
- for i, tx := range txs {
- // no need to check err
- ltxs[i] = newTx(tx)
- }
-
- v, _ := call.Otto.ToValue(ltxs)
- return v
-}
-
-func (js *jsre) allQueuedTransactions(call otto.FunctionCall) otto.Value {
- txs := js.ethereum.TxPool().GetQueuedTransactions()
-
- ltxs := make([]*tx, len(txs))
- for i, tx := range txs {
- // no need to check err
- ltxs[i] = newTx(tx)
- }
-
- v, _ := call.Otto.ToValue(ltxs)
- return v
-}
-
-func (js *jsre) pendingTransactions(call otto.FunctionCall) otto.Value {
- txs := js.ethereum.TxPool().GetTransactions()
-
- // grab the accounts from the account manager. This will help with determening which
- // transactions should be returned.
- accounts, err := js.ethereum.AccountManager().Accounts()
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
-
- // Add the accouns to a new set
- accountSet := set.New()
- for _, account := range accounts {
- accountSet.Add(account.Address)
- }
-
- //ltxs := make([]*tx, len(txs))
- var ltxs []*tx
- for _, tx := range txs {
- if from, _ := tx.From(); accountSet.Has(from) {
- ltxs = append(ltxs, newTx(tx))
- }
- }
-
- v, _ := call.Otto.ToValue(ltxs)
- return v
-}
-
-func (js *jsre) resend(call otto.FunctionCall) otto.Value {
- if len(call.ArgumentList) == 0 {
- fmt.Println("first argument must be a transaction")
- return otto.FalseValue()
- }
-
- v, err := call.Argument(0).Export()
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
-
- if tx, ok := v.(*tx); ok {
- gl, gp := tx.GasLimit, tx.GasPrice
- if len(call.ArgumentList) > 1 {
- gp = call.Argument(1).String()
- }
- if len(call.ArgumentList) > 2 {
- gl = call.Argument(2).String()
- }
-
- ret, err := js.xeth.Transact(tx.From, tx.To, tx.Nonce, tx.Value, gl, gp, tx.Data)
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
- js.ethereum.TxPool().RemoveTransactions(types.Transactions{tx.tx})
-
- v, _ := call.Otto.ToValue(ret)
- return v
- }
-
- fmt.Println("first argument must be a transaction")
- return otto.FalseValue()
-}
-
-func (js *jsre) sign(call otto.FunctionCall) otto.Value {
- if len(call.ArgumentList) != 2 {
- fmt.Println("requires 2 arguments: eth.sign(signer, data)")
- return otto.UndefinedValue()
- }
- signer, err := call.Argument(0).ToString()
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
-
- data, err := call.Argument(1).ToString()
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
- signed, err := js.xeth.Sign(signer, data, false)
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
- v, _ := call.Otto.ToValue(signed)
- return v
-}
-
-func (js *jsre) debugBlock(call otto.FunctionCall) otto.Value {
- block, err := js.getBlock(call)
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
-
- tstart := time.Now()
- old := vm.Debug
-
- if len(call.ArgumentList) > 1 {
- vm.Debug, _ = call.Argument(1).ToBoolean()
- }
-
- _, err = js.ethereum.BlockProcessor().RetryProcess(block)
- if err != nil {
- fmt.Println(err)
- r, _ := call.Otto.ToValue(map[string]interface{}{"success": false, "time": time.Since(tstart).Seconds()})
- return r
- }
- vm.Debug = old
-
- r, _ := call.Otto.ToValue(map[string]interface{}{"success": true, "time": time.Since(tstart).Seconds()})
- return r
-}
-
-func (js *jsre) insertBlockRlp(call otto.FunctionCall) otto.Value {
- tstart := time.Now()
-
- var block types.Block
- if call.Argument(0).IsString() {
- blockRlp, _ := call.Argument(0).ToString()
- err := rlp.DecodeBytes(common.Hex2Bytes(blockRlp), &block)
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
- }
-
- old := vm.Debug
- vm.Debug = true
- _, err := js.ethereum.BlockProcessor().RetryProcess(&block)
- if err != nil {
- fmt.Println(err)
- r, _ := call.Otto.ToValue(map[string]interface{}{"success": false, "time": time.Since(tstart).Seconds()})
- return r
- }
- vm.Debug = old
-
- r, _ := call.Otto.ToValue(map[string]interface{}{"success": true, "time": time.Since(tstart).Seconds()})
- return r
-}
-
-func (js *jsre) setHead(call otto.FunctionCall) otto.Value {
- block, err := js.getBlock(call)
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
-
- js.ethereum.ChainManager().SetHead(block)
- return otto.UndefinedValue()
-}
-
-func (js *jsre) syncProgress(call otto.FunctionCall) otto.Value {
- pending, cached, importing, eta := js.ethereum.Downloader().Stats()
- v, _ := call.Otto.ToValue(map[string]interface{}{
- "pending": pending,
- "cached": cached,
- "importing": importing,
- "estimate": (eta / time.Second * time.Second).String(),
- })
- return v
-}
-
-func (js *jsre) getBlockRlp(call otto.FunctionCall) otto.Value {
- block, err := js.getBlock(call)
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
- encoded, _ := rlp.EncodeToBytes(block)
- v, _ := call.Otto.ToValue(fmt.Sprintf("%x", encoded))
- return v
-}
-
-func (js *jsre) setExtra(call otto.FunctionCall) otto.Value {
- extra, err := call.Argument(0).ToString()
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
-
- if len(extra) > 1024 {
- fmt.Println("error: cannot exceed 1024 bytes")
- return otto.UndefinedValue()
- }
-
- js.ethereum.Miner().SetExtra([]byte(extra))
- return otto.UndefinedValue()
-}
-
-func (js *jsre) setGasPrice(call otto.FunctionCall) otto.Value {
- gasPrice, err := call.Argument(0).ToString()
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
-
- js.ethereum.Miner().SetGasPrice(common.String2Big(gasPrice))
- return otto.UndefinedValue()
-}
-
-func (js *jsre) hashrate(call otto.FunctionCall) otto.Value {
- v, _ := call.Otto.ToValue(js.ethereum.Miner().HashRate())
- return v
-}
-
-func (js *jsre) makeDAG(call otto.FunctionCall) otto.Value {
- blockNumber, err := call.Argument(1).ToInteger()
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
-
- err = ethash.MakeDAG(uint64(blockNumber), "")
- if err != nil {
- return otto.FalseValue()
- }
- return otto.TrueValue()
-}
-
-func (js *jsre) startAutoDAG(otto.FunctionCall) otto.Value {
- js.ethereum.StartAutoDAG()
- return otto.TrueValue()
-}
-
-func (js *jsre) stopAutoDAG(otto.FunctionCall) otto.Value {
- js.ethereum.StopAutoDAG()
- return otto.TrueValue()
-}
-
-func (js *jsre) backtrace(call otto.FunctionCall) otto.Value {
- tracestr, err := call.Argument(0).ToString()
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
- glog.GetTraceLocation().Set(tracestr)
-
- return otto.UndefinedValue()
-}
-
-func (js *jsre) verbosity(call otto.FunctionCall) otto.Value {
- v, err := call.Argument(0).ToInteger()
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
-
- glog.SetV(int(v))
- return otto.UndefinedValue()
-}
-
-func (js *jsre) startMining(call otto.FunctionCall) otto.Value {
- var (
- threads int64
- err error
- )
-
- if len(call.ArgumentList) > 0 {
- threads, err = call.Argument(0).ToInteger()
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
- } else {
- threads = int64(js.ethereum.MinerThreads)
- }
-
- // switch on DAG autogeneration when miner starts
- js.ethereum.StartAutoDAG()
-
- err = js.ethereum.StartMining(int(threads))
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
-
- return otto.TrueValue()
-}
-
-func (js *jsre) stopMining(call otto.FunctionCall) otto.Value {
- js.ethereum.StopMining()
- js.ethereum.StopAutoDAG()
- return otto.TrueValue()
-}
-
-func (js *jsre) startRPC(call otto.FunctionCall) otto.Value {
- addr, err := call.Argument(0).ToString()
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
-
- port, err := call.Argument(1).ToInteger()
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
-
- corsDomain := js.corsDomain
- if len(call.ArgumentList) > 2 {
- corsDomain, err = call.Argument(2).ToString()
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
- }
-
- config := rpc.RpcConfig{
- ListenAddress: addr,
- ListenPort: uint(port),
- CorsDomain: corsDomain,
- }
-
- xeth := xeth.New(js.ethereum, nil)
- err = rpc.Start(xeth, config)
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
-
- return otto.TrueValue()
-}
-
-func (js *jsre) stopRPC(call otto.FunctionCall) otto.Value {
- if rpc.Stop() == nil {
- return otto.TrueValue()
- }
- return otto.FalseValue()
-}
-
-func (js *jsre) addPeer(call otto.FunctionCall) otto.Value {
- nodeURL, err := call.Argument(0).ToString()
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
- err = js.ethereum.AddPeer(nodeURL)
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
- return otto.TrueValue()
-}
-
-func (js *jsre) unlock(call otto.FunctionCall) otto.Value {
- addr, err := call.Argument(0).ToString()
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
- seconds, err := call.Argument(2).ToInteger()
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
- if seconds == 0 {
- seconds = accounts.DefaultAccountUnlockDuration
- }
-
- arg := call.Argument(1)
- var passphrase string
- if arg.IsUndefined() {
- fmt.Println("Please enter a passphrase now.")
- passphrase, err = utils.PromptPassword("Passphrase: ", true)
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
- } else {
- passphrase, err = arg.ToString()
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
- }
- am := js.ethereum.AccountManager()
- err = am.TimedUnlock(common.HexToAddress(addr), passphrase, time.Duration(seconds)*time.Second)
- if err != nil {
- fmt.Printf("Unlock account failed '%v'\n", err)
- return otto.FalseValue()
- }
- return otto.TrueValue()
-}
-
-func (js *jsre) newAccount(call otto.FunctionCall) otto.Value {
- arg := call.Argument(0)
- var passphrase string
- if arg.IsUndefined() {
- fmt.Println("The new account will be encrypted with a passphrase.")
- fmt.Println("Please enter a passphrase now.")
- auth, err := utils.PromptPassword("Passphrase: ", true)
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
- confirm, err := utils.PromptPassword("Repeat Passphrase: ", false)
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
- if auth != confirm {
- fmt.Println("Passphrases did not match.")
- return otto.FalseValue()
- }
- passphrase = auth
- } else {
- var err error
- passphrase, err = arg.ToString()
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
- }
- acct, err := js.ethereum.AccountManager().NewAccount(passphrase)
- if err != nil {
- fmt.Printf("Could not create the account: %v", err)
- return otto.UndefinedValue()
- }
- v, _ := call.Otto.ToValue(acct.Address.Hex())
- return v
-}
-
-func (js *jsre) nodeInfo(call otto.FunctionCall) otto.Value {
- v, _ := call.Otto.ToValue(js.ethereum.NodeInfo())
- return v
-}
-
-func (js *jsre) peers(call otto.FunctionCall) otto.Value {
- v, _ := call.Otto.ToValue(js.ethereum.PeersInfo())
- return v
-}
-
-func (js *jsre) importChain(call otto.FunctionCall) otto.Value {
- if len(call.ArgumentList) == 0 {
- fmt.Println("require file name. admin.importChain(filename)")
- return otto.FalseValue()
- }
- fn, err := call.Argument(0).ToString()
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
- if err := utils.ImportChain(js.ethereum.ChainManager(), fn); err != nil {
- fmt.Println("Import error: ", err)
- return otto.FalseValue()
- }
- return otto.TrueValue()
-}
-
-func (js *jsre) exportChain(call otto.FunctionCall) otto.Value {
- if len(call.ArgumentList) == 0 {
- fmt.Println("require file name: admin.exportChain(filename)")
- return otto.FalseValue()
- }
-
- fn, err := call.Argument(0).ToString()
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
- if err := utils.ExportChain(js.ethereum.ChainManager(), fn); err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
- return otto.TrueValue()
-}
-
-func (js *jsre) printBlock(call otto.FunctionCall) otto.Value {
- block, err := js.getBlock(call)
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
-
- fmt.Println(block)
-
- return otto.UndefinedValue()
-}
-
-func (js *jsre) dumpBlock(call otto.FunctionCall) otto.Value {
- block, err := js.getBlock(call)
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
-
- statedb := state.New(block.Root(), js.ethereum.StateDb())
- dump := statedb.RawDump()
- v, _ := call.Otto.ToValue(dump)
- return v
-}
-
-func (js *jsre) waitForBlocks(call otto.FunctionCall) otto.Value {
- if len(call.ArgumentList) > 2 {
- fmt.Println("requires 0, 1 or 2 arguments: admin.debug.waitForBlock(minHeight, timeout)")
- return otto.FalseValue()
- }
- var n, timeout int64
- var timer <-chan time.Time
- var height *big.Int
- var err error
- args := len(call.ArgumentList)
- if args == 2 {
- timeout, err = call.Argument(1).ToInteger()
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
- timer = time.NewTimer(time.Duration(timeout) * time.Second).C
- }
- if args >= 1 {
- n, err = call.Argument(0).ToInteger()
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
- height = big.NewInt(n)
- }
-
- if args == 0 {
- height = js.xeth.CurrentBlock().Number()
- height.Add(height, common.Big1)
- }
-
- wait := js.wait
- js.wait <- height
- select {
- case <-timer:
- // if times out make sure the xeth loop does not block
- go func() {
- select {
- case wait <- nil:
- case <-wait:
- }
- }()
- return otto.UndefinedValue()
- case height = <-wait:
- }
- v, _ := call.Otto.ToValue(height.Uint64())
- return v
-}
-
-func (js *jsre) sleep(call otto.FunctionCall) otto.Value {
- sec, err := call.Argument(0).ToInteger()
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
- time.Sleep(time.Duration(sec) * time.Second)
- return otto.UndefinedValue()
-}
-
-func (js *jsre) setSolc(call otto.FunctionCall) otto.Value {
- if len(call.ArgumentList) != 1 {
- fmt.Println("needs 1 argument: admin.contractInfo.setSolc(solcPath)")
- return otto.FalseValue()
- }
- solcPath, err := call.Argument(0).ToString()
- if err != nil {
- return otto.FalseValue()
- }
- solc, err := js.xeth.SetSolc(solcPath)
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
- fmt.Println(solc.Info())
- return otto.TrueValue()
-}
-
-func (js *jsre) register(call otto.FunctionCall) otto.Value {
- if len(call.ArgumentList) != 4 {
- fmt.Println("requires 4 arguments: admin.contractInfo.register(fromaddress, contractaddress, contract, filename)")
- return otto.UndefinedValue()
- }
- sender, err := call.Argument(0).ToString()
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
-
- address, err := call.Argument(1).ToString()
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
-
- raw, err := call.Argument(2).Export()
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
- jsonraw, err := json.Marshal(raw)
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
- var contract compiler.Contract
- err = json.Unmarshal(jsonraw, &contract)
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
-
- filename, err := call.Argument(3).ToString()
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
-
- contenthash, err := compiler.ExtractInfo(&contract, filename)
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
- // sender and contract address are passed as hex strings
- codeb := js.xeth.CodeAtBytes(address)
- codehash := common.BytesToHash(crypto.Sha3(codeb))
-
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
-
- registry := resolver.New(js.xeth)
-
- _, err = registry.RegisterContentHash(common.HexToAddress(sender), codehash, contenthash)
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
-
- v, _ := call.Otto.ToValue(contenthash.Hex())
- return v
-}
-
-func (js *jsre) registerUrl(call otto.FunctionCall) otto.Value {
- if len(call.ArgumentList) != 3 {
- fmt.Println("requires 3 arguments: admin.contractInfo.register(fromaddress, contenthash, filename)")
- return otto.FalseValue()
- }
- sender, err := call.Argument(0).ToString()
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
-
- contenthash, err := call.Argument(1).ToString()
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
-
- url, err := call.Argument(2).ToString()
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
-
- registry := resolver.New(js.xeth)
-
- _, err = registry.RegisterUrl(common.HexToAddress(sender), common.HexToHash(contenthash), url)
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
-
- return otto.TrueValue()
-}
-
-func (js *jsre) getContractInfo(call otto.FunctionCall) otto.Value {
- if len(call.ArgumentList) != 1 {
- fmt.Println("requires 1 argument: admin.contractInfo.register(contractaddress)")
- return otto.FalseValue()
- }
- addr, err := call.Argument(0).ToString()
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
-
- infoDoc, err := natspec.FetchDocsForContract(addr, js.xeth, ds)
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
- var info compiler.ContractInfo
- err = json.Unmarshal(infoDoc, &info)
- if err != nil {
- fmt.Println(err)
- return otto.UndefinedValue()
- }
- v, _ := call.Otto.ToValue(info)
- return v
-}
-
-func (js *jsre) startNatSpec(call otto.FunctionCall) otto.Value {
- js.ethereum.NatSpec = true
- return otto.TrueValue()
-}
-
-func (js *jsre) stopNatSpec(call otto.FunctionCall) otto.Value {
- js.ethereum.NatSpec = false
- return otto.TrueValue()
-}
-
-func (js *jsre) newRegistry(call otto.FunctionCall) otto.Value {
-
- if len(call.ArgumentList) != 1 {
- fmt.Println("requires 1 argument: admin.contractInfo.newRegistry(adminaddress)")
- return otto.FalseValue()
- }
- addr, err := call.Argument(0).ToString()
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
-
- registry := resolver.New(js.xeth)
- err = registry.CreateContracts(common.HexToAddress(addr))
- if err != nil {
- fmt.Println(err)
- return otto.FalseValue()
- }
-
- return otto.TrueValue()
-}
-
-// internal transaction type which will allow us to resend transactions using `eth.resend`
-type tx struct {
- tx *types.Transaction
-
- To string
- From string
- Nonce string
- Value string
- Data string
- GasLimit string
- GasPrice string
-}
-
-func newTx(t *types.Transaction) *tx {
- from, _ := t.From()
- var to string
- if t := t.To(); t != nil {
- to = t.Hex()
- }
-
- return &tx{
- tx: t,
- To: to,
- From: from.Hex(),
- Value: t.Amount.String(),
- Nonce: strconv.Itoa(int(t.Nonce())),
- Data: "0x" + common.Bytes2Hex(t.Data()),
- GasLimit: t.GasLimit.String(),
- GasPrice: t.GasPrice().String(),
- }
-}
diff --git a/cmd/geth/js.go b/cmd/geth/js.go
index 7e6e10ca9..01840ebd9 100644
--- a/cmd/geth/js.go
+++ b/cmd/geth/js.go
@@ -26,6 +26,8 @@ import (
"path/filepath"
"strings"
+ "sort"
+
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/docserver"
@@ -33,9 +35,13 @@ import (
"github.com/ethereum/go-ethereum/eth"
re "github.com/ethereum/go-ethereum/jsre"
"github.com/ethereum/go-ethereum/rpc"
+ "github.com/ethereum/go-ethereum/rpc/api"
+ "github.com/ethereum/go-ethereum/rpc/codec"
+ "github.com/ethereum/go-ethereum/rpc/comms"
"github.com/ethereum/go-ethereum/xeth"
"github.com/peterh/liner"
"github.com/robertkrimen/otto"
+ "github.com/ethereum/go-ethereum/rpc/shared"
)
type prompter interface {
@@ -70,10 +76,104 @@ type jsre struct {
ps1 string
atexit func()
corsDomain string
+ client comms.EthereumClient
prompter
}
-func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain, ipcpath string, interactive bool, f xeth.Frontend) *jsre {
+var (
+ loadedModulesMethods map[string][]string
+)
+
+func keywordCompleter(line string) []string {
+ results := make([]string, 0)
+
+ if strings.Contains(line, ".") {
+ elements := strings.Split(line, ".")
+ if len(elements) == 2 {
+ module := elements[0]
+ partialMethod := elements[1]
+ if methods, found := loadedModulesMethods[module]; found {
+ for _, method := range methods {
+ if strings.HasPrefix(method, partialMethod) { // e.g. debug.se
+ results = append(results, module+"."+method)
+ }
+ }
+ }
+ }
+ } else {
+ for module, methods := range loadedModulesMethods {
+ if line == module { // user typed in full module name, show all methods
+ for _, method := range methods {
+ results = append(results, module+"."+method)
+ }
+ } else if strings.HasPrefix(module, line) { // partial method name, e.g. admi
+ results = append(results, module)
+ }
+ }
+ }
+ return results
+}
+
+func apiWordCompleter(line string, pos int) (head string, completions []string, tail string) {
+ if len(line) == 0 {
+ return "", nil, ""
+ }
+
+ i := 0
+ for i = pos - 1; i > 0; i-- {
+ if line[i] == '.' || (line[i] >= 'a' && line[i] <= 'z') || (line[i] >= 'A' && line[i] <= 'Z') {
+ continue
+ }
+ if i >= 3 && line[i] == '3' && line[i-3] == 'w' && line[i-2] == 'e' && line[i-1] == 'b' {
+ continue
+ }
+ i += 1
+ break
+ }
+
+ begin := line[:i]
+ keyword := line[i:pos]
+ end := line[pos:]
+
+ completionWords := keywordCompleter(keyword)
+ return begin, completionWords, end
+}
+
+func newLightweightJSRE(libPath string, client comms.EthereumClient, interactive bool, f xeth.Frontend) *jsre {
+ js := &jsre{ps1: "> "}
+ js.wait = make(chan *big.Int)
+ js.client = client
+
+ if f == nil {
+ f = js
+ }
+
+ // update state in separare forever blocks
+ js.re = re.New(libPath)
+ if err := js.apiBindings(f); err != nil {
+ utils.Fatalf("Unable to initialize console - %v", err)
+ }
+
+ if !liner.TerminalSupported() || !interactive {
+ js.prompter = dumbterm{bufio.NewReader(os.Stdin)}
+ } else {
+ lr := liner.NewLiner()
+ js.withHistory(func(hist *os.File) { lr.ReadHistory(hist) })
+ lr.SetCtrlCAborts(true)
+ js.loadAutoCompletion()
+ lr.SetWordCompleter(apiWordCompleter)
+ lr.SetTabCompletionStyle(liner.TabPrints)
+ js.prompter = lr
+ js.atexit = func() {
+ js.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) })
+ lr.Close()
+ close(js.wait)
+ }
+ }
+ return js
+}
+
+func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain string, client comms.EthereumClient, interactive bool, f xeth.Frontend) *jsre {
js := &jsre{ethereum: ethereum, ps1: "> "}
// set default cors domain used by startRpc from CLI flag
js.corsDomain = corsDomain
@@ -82,10 +182,18 @@ func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain, ipcpath string, intera
}
js.xeth = xeth.New(ethereum, f)
js.wait = js.xeth.UpdateState()
+ js.client = client
+ if clt, ok := js.client.(*comms.InProcClient); ok {
+ if offeredApis, err := api.ParseApiString(shared.AllApis, codec.JSON, js.xeth, ethereum); err == nil {
+ clt.Initialize(api.Merge(offeredApis...))
+ }
+ }
+
// update state in separare forever blocks
js.re = re.New(libPath)
- js.apiBindings(ipcpath, f)
- js.adminBindings()
+ if err := js.apiBindings(f); err != nil {
+ utils.Fatalf("Unable to connect - %v", err)
+ }
if !liner.TerminalSupported() || !interactive {
js.prompter = dumbterm{bufio.NewReader(os.Stdin)}
@@ -93,6 +201,9 @@ func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain, ipcpath string, intera
lr := liner.NewLiner()
js.withHistory(func(hist *os.File) { lr.ReadHistory(hist) })
lr.SetCtrlCAborts(true)
+ js.loadAutoCompletion()
+ lr.SetWordCompleter(apiWordCompleter)
+ lr.SetTabCompletionStyle(liner.TabPrints)
js.prompter = lr
js.atexit = func() {
js.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) })
@@ -103,11 +214,76 @@ func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain, ipcpath string, intera
return js
}
-func (js *jsre) apiBindings(ipcpath string, f xeth.Frontend) {
- xe := xeth.New(js.ethereum, f)
- ethApi := rpc.NewEthereumApi(xe)
- jeth := rpc.NewJeth(ethApi, js.re, ipcpath)
+func (self *jsre) loadAutoCompletion() {
+ if modules, err := self.supportedApis(); err == nil {
+ loadedModulesMethods = make(map[string][]string)
+ for module, _ := range modules {
+ loadedModulesMethods[module] = api.AutoCompletion[module]
+ }
+ }
+}
+
+func (self *jsre) batch(statement string) {
+ val, err := self.re.Run(statement)
+
+ if err != nil {
+ fmt.Printf("error: %v", err)
+ } else if val.IsDefined() && val.IsObject() {
+ obj, _ := self.re.Get("ret_result")
+ fmt.Printf("%v", obj)
+ } else if val.IsDefined() {
+ fmt.Printf("%v", val)
+ }
+
+ if self.atexit != nil {
+ self.atexit()
+ }
+
+ self.re.Stop(false)
+}
+
+// show summary of current geth instance
+func (self *jsre) welcome() {
+ self.re.Eval(`console.log('instance: ' + web3.version.client);`)
+ self.re.Eval(`console.log(' datadir: ' + admin.datadir);`)
+ self.re.Eval(`console.log("coinbase: " + eth.coinbase);`)
+ self.re.Eval(`var lastBlockTimestamp = 1000 * eth.getBlock(eth.blockNumber).timestamp`)
+ self.re.Eval(`console.log("at block: " + eth.blockNumber + " (" + new Date(lastBlockTimestamp).toLocaleDateString()
+ + " " + new Date(lastBlockTimestamp).toLocaleTimeString() + ")");`)
+
+ if modules, err := self.supportedApis(); err == nil {
+ loadedModules := make([]string, 0)
+ for api, version := range modules {
+ loadedModules = append(loadedModules, fmt.Sprintf("%s:%s", api, version))
+ }
+ sort.Strings(loadedModules)
+
+ self.re.Eval(fmt.Sprintf("var modules = '%s';", strings.Join(loadedModules, " ")))
+ self.re.Eval(`console.log(" modules: " + modules);`)
+ }
+}
+
+func (self *jsre) supportedApis() (map[string]string, error) {
+ return self.client.SupportedModules()
+}
+
+func (js *jsre) apiBindings(f xeth.Frontend) error {
+ apis, err := js.supportedApis()
+ if err != nil {
+ return err
+ }
+
+ apiNames := make([]string, 0, len(apis))
+ for a, _ := range apis {
+ apiNames = append(apiNames, a)
+ }
+
+ apiImpl, err := api.ParseApiString(strings.Join(apiNames, ","), codec.JSON, js.xeth, js.ethereum)
+ if err != nil {
+ utils.Fatalf("Unable to determine supported api's: %v", err)
+ }
+ jeth := rpc.NewJeth(api.Merge(apiImpl...), js.re, js.client)
js.re.Set("jeth", struct{}{})
t, _ := js.re.Get("jeth")
jethObj := t.Object()
@@ -115,14 +291,14 @@ func (js *jsre) apiBindings(ipcpath string, f xeth.Frontend) {
jethObj.Set("send", jeth.Send)
jethObj.Set("sendAsync", jeth.Send)
- err := js.re.Compile("bignumber.js", re.BigNumber_JS)
+ err = js.re.Compile("bignumber.js", re.BigNumber_JS)
if err != nil {
utils.Fatalf("Error loading bignumber.js: %v", err)
}
err = js.re.Compile("ethereum.js", re.Web3_JS)
if err != nil {
- utils.Fatalf("Error loading ethereum.js: %v", err)
+ utils.Fatalf("Error loading web3.js: %v", err)
}
_, err = js.re.Eval("var web3 = require('web3');")
@@ -134,17 +310,29 @@ func (js *jsre) apiBindings(ipcpath string, f xeth.Frontend) {
if err != nil {
utils.Fatalf("Error setting web3 provider: %v", err)
}
- _, err = js.re.Eval(`
-var eth = web3.eth;
-var shh = web3.shh;
-var db = web3.db;
-var net = web3.net;
- `)
+
+ // load only supported API's in javascript runtime
+ shortcuts := "var eth = web3.eth; "
+ for _, apiName := range apiNames {
+ if apiName == shared.Web3ApiName {
+ continue // manually mapped
+ }
+
+ if err = js.re.Compile(fmt.Sprintf("%s.js", apiName), api.Javascript(apiName)); err == nil {
+ shortcuts += fmt.Sprintf("var %s = web3.%s; ", apiName, apiName)
+ } else {
+ utils.Fatalf("Error loading %s.js: %v", apiName, err)
+ }
+ }
+
+ _, err = js.re.Eval(shortcuts)
+
if err != nil {
utils.Fatalf("Error setting namespaces: %v", err)
}
js.re.Eval(globalRegistrar + "registrar = GlobalRegistrar.at(\"" + globalRegistrarAddr + "\");")
+ return nil
}
var ds, _ = docserver.New("/")
@@ -234,7 +422,12 @@ func (self *jsre) interactive() {
}
func (self *jsre) withHistory(op func(*os.File)) {
- hist, err := os.OpenFile(filepath.Join(self.ethereum.DataDir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm)
+ datadir := common.DefaultDataDir()
+ if self.ethereum != nil {
+ datadir = self.ethereum.DataDir
+ }
+
+ hist, err := os.OpenFile(filepath.Join(datadir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
fmt.Printf("unable to open history file: %v\n", err)
return
diff --git a/cmd/geth/js_test.go b/cmd/geth/js_test.go
index 20bde01f3..cfbe26bee 100644
--- a/cmd/geth/js_test.go
+++ b/cmd/geth/js_test.go
@@ -20,6 +20,8 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
+ "github.com/ethereum/go-ethereum/rpc/comms"
+ "github.com/ethereum/go-ethereum/rpc/codec"
)
const (
@@ -105,7 +107,8 @@ func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) {
t.Errorf("Error creating DocServer: %v", err)
}
tf := &testjethre{ds: ds, stateDb: ethereum.ChainManager().State().Copy()}
- repl := newJSRE(ethereum, assetPath, "", "", false, tf)
+ client := comms.NewInProcClient(codec.JSON)
+ repl := newJSRE(ethereum, assetPath, "", client, false, tf)
tf.jsre = repl
return tmp, tf, ethereum
}
@@ -125,7 +128,7 @@ func TestNodeInfo(t *testing.T) {
defer ethereum.Stop()
defer os.RemoveAll(tmp)
want := `{"DiscPort":0,"IP":"0.0.0.0","ListenAddr":"","Name":"test","NodeID":"4cb2fc32924e94277bf94b5e4c983beedb2eabd5a0bc941db32202735c6625d020ca14a5963d1738af43b6ac0a711d61b1a06de931a499fe2aa0b1a132a902b5","NodeUrl":"enode://4cb2fc32924e94277bf94b5e4c983beedb2eabd5a0bc941db32202735c6625d020ca14a5963d1738af43b6ac0a711d61b1a06de931a499fe2aa0b1a132a902b5@0.0.0.0:0","TCPPort":0,"Td":"131072"}`
- checkEvalJSON(t, repl, `admin.nodeInfo()`, want)
+ checkEvalJSON(t, repl, `admin.nodeInfo`, want)
}
func TestAccounts(t *testing.T) {
@@ -139,7 +142,7 @@ func TestAccounts(t *testing.T) {
checkEvalJSON(t, repl, `eth.accounts`, `["`+testAddress+`"]`)
checkEvalJSON(t, repl, `eth.coinbase`, `"`+testAddress+`"`)
- val, err := repl.re.Run(`admin.newAccount("password")`)
+ val, err := repl.re.Run(`personal.newAccount("password")`)
if err != nil {
t.Errorf("expected no error, got %v", err)
}
@@ -161,7 +164,7 @@ func TestBlockChain(t *testing.T) {
defer ethereum.Stop()
defer os.RemoveAll(tmp)
// get current block dump before export/import.
- val, err := repl.re.Run("JSON.stringify(admin.debug.dumpBlock())")
+ val, err := repl.re.Run("JSON.stringify(debug.dumpBlock(eth.blockNumber))")
if err != nil {
t.Errorf("expected no error, got %v", err)
}
@@ -178,14 +181,14 @@ func TestBlockChain(t *testing.T) {
ethereum.ChainManager().Reset()
- checkEvalJSON(t, repl, `admin.export(`+tmpfileq+`)`, `true`)
+ checkEvalJSON(t, repl, `admin.exportChain(`+tmpfileq+`)`, `true`)
if _, err := os.Stat(tmpfile); err != nil {
t.Fatal(err)
}
// check import, verify that dumpBlock gives the same result.
- checkEvalJSON(t, repl, `admin.import(`+tmpfileq+`)`, `true`)
- checkEvalJSON(t, repl, `admin.debug.dumpBlock()`, beforeExport)
+ checkEvalJSON(t, repl, `admin.importChain(`+tmpfileq+`)`, `true`)
+ checkEvalJSON(t, repl, `debug.dumpBlock(eth.blockNumber)`, beforeExport)
}
func TestMining(t *testing.T) {
@@ -207,7 +210,7 @@ func TestRPC(t *testing.T) {
defer ethereum.Stop()
defer os.RemoveAll(tmp)
- checkEvalJSON(t, repl, `admin.startRPC("127.0.0.1", 5004)`, `true`)
+ checkEvalJSON(t, repl, `admin.startRPC("127.0.0.1", 5004, "*", "web3,eth,net")`, `true`)
}
func TestCheckTestAccountBalance(t *testing.T) {
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index 89aae43e5..02ec63300 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -38,6 +38,8 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/rpc/codec"
+ "github.com/ethereum/go-ethereum/rpc/comms"
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
)
@@ -202,6 +204,16 @@ nodes.
The Geth console is an interactive shell for the JavaScript runtime environment
which exposes a node admin interface as well as the Ðapp JavaScript API.
See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console
+`},
+ {
+ Action: attach,
+ Name: "attach",
+ Usage: `Geth Console: interactive JavaScript environment (connect to node)`,
+ Description: `
+The Geth console is an interactive shell for the JavaScript runtime environment
+which exposes a node admin interface as well as the Ðapp JavaScript API.
+See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console.
+This command allows to open a console on a running geth node.
`,
},
{
@@ -239,9 +251,11 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
utils.RPCEnabledFlag,
utils.RPCListenAddrFlag,
utils.RPCPortFlag,
+ utils.RpcApiFlag,
utils.IPCDisabledFlag,
utils.IPCApiFlag,
utils.IPCPathFlag,
+ utils.ExecFlag,
utils.WhisperEnabledFlag,
utils.VMDebugFlag,
utils.ProtocolVersionFlag,
@@ -294,6 +308,44 @@ func run(ctx *cli.Context) {
ethereum.WaitForShutdown()
}
+func attach(ctx *cli.Context) {
+ // Wrap the standard output with a colorified stream (windows)
+ if isatty.IsTerminal(os.Stdout.Fd()) {
+ if pr, pw, err := os.Pipe(); err == nil {
+ go io.Copy(colorable.NewColorableStdout(), pr)
+ os.Stdout = pw
+ }
+ }
+
+ var client comms.EthereumClient
+ var err error
+ if ctx.Args().Present() {
+ client, err = comms.ClientFromEndpoint(ctx.Args().First(), codec.JSON)
+ } else {
+ cfg := comms.IpcConfig{
+ Endpoint: ctx.GlobalString(utils.IPCPathFlag.Name),
+ }
+ client, err = comms.NewIpcClient(cfg, codec.JSON)
+ }
+
+ if err != nil {
+ utils.Fatalf("Unable to attach to geth node - %v", err)
+ }
+
+ repl := newLightweightJSRE(
+ ctx.GlobalString(utils.JSpathFlag.Name),
+ client,
+ true,
+ nil)
+
+ if ctx.GlobalString(utils.ExecFlag.Name) != "" {
+ repl.batch(ctx.GlobalString(utils.ExecFlag.Name))
+ } else {
+ repl.welcome()
+ repl.interactive()
+ }
+}
+
func console(ctx *cli.Context) {
// Wrap the standard output with a colorified stream (windows)
if isatty.IsTerminal(os.Stdout.Fd()) {
@@ -309,16 +361,24 @@ func console(ctx *cli.Context) {
utils.Fatalf("%v", err)
}
+ client := comms.NewInProcClient(codec.JSON)
+
startEth(ctx, ethereum)
repl := newJSRE(
ethereum,
- ctx.String(utils.JSpathFlag.Name),
+ ctx.GlobalString(utils.JSpathFlag.Name),
ctx.GlobalString(utils.RPCCORSDomainFlag.Name),
- utils.IpcSocketPath(ctx),
+ client,
true,
nil,
)
- repl.interactive()
+
+ if ctx.GlobalString(utils.ExecFlag.Name) != "" {
+ repl.batch(ctx.GlobalString(utils.ExecFlag.Name))
+ } else {
+ repl.welcome()
+ repl.interactive()
+ }
ethereum.Stop()
ethereum.WaitForShutdown()
@@ -331,12 +391,13 @@ func execJSFiles(ctx *cli.Context) {
utils.Fatalf("%v", err)
}
+ client := comms.NewInProcClient(codec.JSON)
startEth(ctx, ethereum)
repl := newJSRE(
ethereum,
- ctx.String(utils.JSpathFlag.Name),
+ ctx.GlobalString(utils.JSpathFlag.Name),
ctx.GlobalString(utils.RPCCORSDomainFlag.Name),
- utils.IpcSocketPath(ctx),
+ client,
false,
nil,
)
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 696dbd142..15a577a07 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -22,7 +22,6 @@ import (
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/p2p/nat"
- "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/rpc/api"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/comms"
@@ -209,20 +208,29 @@ var (
Usage: "Domain on which to send Access-Control-Allow-Origin header",
Value: "",
}
+ RpcApiFlag = cli.StringFlag{
+ Name: "rpcapi",
+ Usage: "Specify the API's which are offered over the HTTP RPC interface",
+ Value: comms.DefaultHttpRpcApis,
+ }
IPCDisabledFlag = cli.BoolFlag{
Name: "ipcdisable",
Usage: "Disable the IPC-RPC server",
}
IPCApiFlag = cli.StringFlag{
Name: "ipcapi",
- Usage: "Specify the API's which are offered over this interface",
- Value: api.DefaultIpcApis,
+ Usage: "Specify the API's which are offered over the IPC interface",
+ Value: comms.DefaultIpcApis,
}
IPCPathFlag = DirectoryFlag{
Name: "ipcpath",
Usage: "Filename for IPC socket/pipe",
Value: DirectoryString{common.DefaultIpcPath()},
}
+ ExecFlag = cli.StringFlag{
+ Name: "exec",
+ Usage: "Execute javascript statement (only in combination with console/attach)",
+ }
// Network Settings
MaxPeersFlag = cli.IntFlag{
Name: "maxpeers",
@@ -453,18 +461,25 @@ func StartIPC(eth *eth.Ethereum, ctx *cli.Context) error {
return err
}
- return comms.StartIpc(config, codec, apis...)
+ return comms.StartIpc(config, codec, api.Merge(apis...))
}
func StartRPC(eth *eth.Ethereum, ctx *cli.Context) error {
- config := rpc.RpcConfig{
+ config := comms.HttpConfig{
ListenAddress: ctx.GlobalString(RPCListenAddrFlag.Name),
ListenPort: uint(ctx.GlobalInt(RPCPortFlag.Name)),
CorsDomain: ctx.GlobalString(RPCCORSDomainFlag.Name),
}
xeth := xeth.New(eth, nil)
- return rpc.Start(xeth, config)
+ codec := codec.JSON
+
+ apis, err := api.ParseApiString(ctx.GlobalString(RpcApiFlag.Name), codec, xeth, eth)
+ if err != nil {
+ return err
+ }
+
+ return comms.StartHttp(config, codec, api.Merge(apis...))
}
func StartPProf(ctx *cli.Context) {
diff --git a/core/transaction_pool.go b/core/transaction_pool.go
index 5ebe3576b..34a1733d7 100644
--- a/core/transaction_pool.go
+++ b/core/transaction_pool.go
@@ -149,7 +149,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
return ErrInvalidSender
}
- // Make sure the account exist. Non existant accounts
+ // Make sure the account exist. Non existent accounts
// haven't got funds and well therefor never pass.
if !pool.currentState().HasAccount(from) {
return ErrNonExistentAccount
diff --git a/rpc/api.go b/rpc/api.go
deleted file mode 100644
index a344a8294..000000000
--- a/rpc/api.go
+++ /dev/null
@@ -1,646 +0,0 @@
-package rpc
-
-import (
- "bytes"
- "encoding/json"
- "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/logger"
- "github.com/ethereum/go-ethereum/logger/glog"
- "github.com/ethereum/go-ethereum/xeth"
-)
-
-type EthereumApi struct {
- eth *xeth.XEth
-}
-
-func NewEthereumApi(xeth *xeth.XEth) *EthereumApi {
- api := &EthereumApi{
- eth: xeth,
- }
-
- return api
-}
-
-func (api *EthereumApi) xeth() *xeth.XEth {
- return api.eth
-}
-
-func (api *EthereumApi) xethAtStateNum(num int64) *xeth.XEth {
- return api.xeth().AtStateNum(num)
-}
-
-func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error {
- // Spec at https://github.com/ethereum/wiki/wiki/JSON-RPC
- glog.V(logger.Debug).Infof("%s %s", req.Method, req.Params)
-
- switch req.Method {
- case "web3_sha3":
- args := new(Sha3Args)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
- *reply = common.ToHex(crypto.Sha3(common.FromHex(args.Data)))
- case "web3_clientVersion":
- *reply = api.xeth().ClientVersion()
- case "net_version":
- *reply = api.xeth().NetworkVersion()
- case "net_listening":
- *reply = api.xeth().IsListening()
- case "net_peerCount":
- *reply = newHexNum(api.xeth().PeerCount())
- case "eth_protocolVersion":
- *reply = api.xeth().EthVersion()
- case "eth_coinbase":
- *reply = newHexData(api.xeth().Coinbase())
- case "eth_mining":
- *reply = api.xeth().IsMining()
- case "eth_gasPrice":
- v := api.xeth().DefaultGasPrice()
- *reply = newHexNum(v.Bytes())
- case "eth_accounts":
- *reply = api.xeth().Accounts()
- case "eth_blockNumber":
- v := api.xeth().CurrentBlock().Number()
- *reply = newHexNum(v.Bytes())
- case "eth_getBalance":
- args := new(GetBalanceArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- *reply = api.xethAtStateNum(args.BlockNumber).BalanceAt(args.Address)
- //v := api.xethAtStateNum(args.BlockNumber).State().SafeGet(args.Address).Balance()
- //*reply = common.ToHex(v.Bytes())
- case "eth_getStorage", "eth_storageAt":
- args := new(GetStorageArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- *reply = api.xethAtStateNum(args.BlockNumber).State().SafeGet(args.Address).Storage()
- case "eth_getStorageAt":
- args := new(GetStorageAtArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- *reply = api.xethAtStateNum(args.BlockNumber).StorageAt(args.Address, args.Key)
- case "eth_getTransactionCount":
- args := new(GetTxCountArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- count := api.xethAtStateNum(args.BlockNumber).TxCountAt(args.Address)
- *reply = newHexNum(big.NewInt(int64(count)).Bytes())
- case "eth_getBlockTransactionCountByHash":
- args := new(HashArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- block := NewBlockRes(api.xeth().EthBlockByHash(args.Hash), false)
- if block == nil {
- *reply = nil
- } else {
- *reply = newHexNum(big.NewInt(int64(len(block.Transactions))).Bytes())
- }
- case "eth_getBlockTransactionCountByNumber":
- args := new(BlockNumArg)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- block := NewBlockRes(api.xeth().EthBlockByNumber(args.BlockNumber), false)
- if block == nil {
- *reply = nil
- break
- }
-
- *reply = newHexNum(big.NewInt(int64(len(block.Transactions))).Bytes())
- case "eth_getUncleCountByBlockHash":
- args := new(HashArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- block := api.xeth().EthBlockByHash(args.Hash)
- br := NewBlockRes(block, false)
- if br == nil {
- *reply = nil
- break
- }
-
- *reply = newHexNum(big.NewInt(int64(len(br.Uncles))).Bytes())
- case "eth_getUncleCountByBlockNumber":
- args := new(BlockNumArg)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- block := api.xeth().EthBlockByNumber(args.BlockNumber)
- br := NewBlockRes(block, false)
- if br == nil {
- *reply = nil
- break
- }
-
- *reply = newHexNum(big.NewInt(int64(len(br.Uncles))).Bytes())
-
- case "eth_getData", "eth_getCode":
- args := new(GetDataArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
- v := api.xethAtStateNum(args.BlockNumber).CodeAtBytes(args.Address)
- *reply = newHexData(v)
-
- case "eth_sign":
- args := new(NewSigArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
- v, err := api.xeth().Sign(args.From, args.Data, false)
- if err != nil {
- return err
- }
- *reply = v
-
- case "eth_sendRawTransaction":
- args := new(NewDataArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
- v, err := api.xeth().PushTx(args.Data)
- if err != nil {
- return err
- }
- *reply = v
-
- case "eth_sendTransaction", "eth_transact":
- args := new(NewTxArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- // nonce may be nil ("guess" mode)
- var nonce string
- if args.Nonce != nil {
- nonce = args.Nonce.String()
- }
-
- var gas string
- if args.Gas == nil {
- gas = ""
- } else {
- gas = args.Gas.String()
- }
-
- var gasprice string
- if args.GasPrice == nil {
- gasprice = ""
- } else {
- gasprice = args.GasPrice.String()
- }
-
- v, err := api.xeth().Transact(args.From, args.To, nonce, args.Value.String(), gas, gasprice, args.Data)
- if err != nil {
- return err
- }
- *reply = v
- case "eth_estimateGas":
- _, gas, err := api.doCall(req.Params)
- if err != nil {
- return err
- }
-
- // TODO unwrap the parent method's ToHex call
- if len(gas) == 0 {
- *reply = newHexNum(0)
- } else {
- *reply = newHexNum(gas)
- }
- case "eth_call":
- v, _, err := api.doCall(req.Params)
- if err != nil {
- return err
- }
-
- // TODO unwrap the parent method's ToHex call
- if v == "0x0" {
- *reply = newHexData([]byte{})
- } else {
- *reply = newHexData(common.FromHex(v))
- }
- case "eth_flush":
- return NewNotImplementedError(req.Method)
- case "eth_getBlockByHash":
- args := new(GetBlockByHashArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- block := api.xeth().EthBlockByHash(args.BlockHash)
- br := NewBlockRes(block, args.IncludeTxs)
-
- *reply = br
- case "eth_getBlockByNumber":
- args := new(GetBlockByNumberArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- block := api.xeth().EthBlockByNumber(args.BlockNumber)
- br := NewBlockRes(block, args.IncludeTxs)
- // If request was for "pending", nil nonsensical fields
- if args.BlockNumber == -2 {
- br.BlockHash = nil
- br.BlockNumber = nil
- br.Miner = nil
- br.Nonce = nil
- br.LogsBloom = nil
- }
- *reply = br
- case "eth_getTransactionByHash":
- args := new(HashArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
- tx, bhash, bnum, txi := api.xeth().EthTransactionByHash(args.Hash)
- if tx != nil {
- v := NewTransactionRes(tx)
- // if the blockhash is 0, assume this is a pending transaction
- if bytes.Compare(bhash.Bytes(), bytes.Repeat([]byte{0}, 32)) != 0 {
- v.BlockHash = newHexData(bhash)
- v.BlockNumber = newHexNum(bnum)
- v.TxIndex = newHexNum(txi)
- }
- *reply = v
- }
- case "eth_getTransactionByBlockHashAndIndex":
- args := new(HashIndexArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- block := api.xeth().EthBlockByHash(args.Hash)
- br := NewBlockRes(block, true)
- if br == nil {
- *reply = nil
- break
- }
-
- if args.Index >= int64(len(br.Transactions)) || args.Index < 0 {
- // return NewValidationError("Index", "does not exist")
- *reply = nil
- } else {
- *reply = br.Transactions[args.Index]
- }
- case "eth_getTransactionByBlockNumberAndIndex":
- args := new(BlockNumIndexArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- block := api.xeth().EthBlockByNumber(args.BlockNumber)
- v := NewBlockRes(block, true)
- if v == nil {
- *reply = nil
- break
- }
-
- if args.Index >= int64(len(v.Transactions)) || args.Index < 0 {
- // return NewValidationError("Index", "does not exist")
- *reply = nil
- } else {
- *reply = v.Transactions[args.Index]
- }
- case "eth_getUncleByBlockHashAndIndex":
- args := new(HashIndexArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- br := NewBlockRes(api.xeth().EthBlockByHash(args.Hash), false)
- if br == nil {
- *reply = nil
- return nil
- }
-
- if args.Index >= int64(len(br.Uncles)) || args.Index < 0 {
- // return NewValidationError("Index", "does not exist")
- *reply = nil
- } else {
- *reply = br.Uncles[args.Index]
- }
- case "eth_getUncleByBlockNumberAndIndex":
- args := new(BlockNumIndexArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- block := api.xeth().EthBlockByNumber(args.BlockNumber)
- v := NewBlockRes(block, true)
-
- if v == nil {
- *reply = nil
- return nil
- }
-
- if args.Index >= int64(len(v.Uncles)) || args.Index < 0 {
- // return NewValidationError("Index", "does not exist")
- *reply = nil
- } else {
- *reply = v.Uncles[args.Index]
- }
-
- case "eth_getCompilers":
- var lang string
- if solc, _ := api.xeth().Solc(); solc != nil {
- lang = "Solidity"
- }
- c := []string{lang}
- *reply = c
-
- case "eth_compileLLL", "eth_compileSerpent":
- return NewNotImplementedError(req.Method)
-
- case "eth_compileSolidity":
- solc, _ := api.xeth().Solc()
- if solc == nil {
- return NewNotAvailableError(req.Method, "solc (solidity compiler) not found")
- }
-
- args := new(SourceArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- contracts, err := solc.Compile(args.Source)
- if err != nil {
- return err
- }
- *reply = contracts
-
- case "eth_newFilter":
- args := new(BlockFilterArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- id := api.xeth().NewLogFilter(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics)
- *reply = newHexNum(big.NewInt(int64(id)).Bytes())
-
- case "eth_newBlockFilter":
- *reply = newHexNum(api.xeth().NewBlockFilter())
- case "eth_newPendingTransactionFilter":
- *reply = newHexNum(api.xeth().NewTransactionFilter())
- case "eth_uninstallFilter":
- args := new(FilterIdArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
- *reply = api.xeth().UninstallFilter(args.Id)
- case "eth_getFilterChanges":
- args := new(FilterIdArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- switch api.xeth().GetFilterType(args.Id) {
- case xeth.BlockFilterTy:
- *reply = NewHashesRes(api.xeth().BlockFilterChanged(args.Id))
- case xeth.TransactionFilterTy:
- *reply = NewHashesRes(api.xeth().TransactionFilterChanged(args.Id))
- case xeth.LogFilterTy:
- *reply = NewLogsRes(api.xeth().LogFilterChanged(args.Id))
- default:
- *reply = []string{} // reply empty string slice
- }
- case "eth_getFilterLogs":
- args := new(FilterIdArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
- *reply = NewLogsRes(api.xeth().Logs(args.Id))
- case "eth_getLogs":
- args := new(BlockFilterArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
- *reply = NewLogsRes(api.xeth().AllLogs(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics))
- case "eth_getWork":
- api.xeth().SetMining(true, 0)
- *reply = api.xeth().RemoteMining().GetWork()
- case "eth_submitWork":
- args := new(SubmitWorkArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
- *reply = api.xeth().RemoteMining().SubmitWork(args.Nonce, common.HexToHash(args.Digest), common.HexToHash(args.Header))
- case "db_putString":
- args := new(DbArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- if err := args.requirements(); err != nil {
- return err
- }
-
- api.xeth().DbPut([]byte(args.Database+args.Key), args.Value)
-
- *reply = true
- case "db_getString":
- args := new(DbArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- if err := args.requirements(); err != nil {
- return err
- }
-
- res, _ := api.xeth().DbGet([]byte(args.Database + args.Key))
- *reply = string(res)
- case "db_putHex":
- args := new(DbHexArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- if err := args.requirements(); err != nil {
- return err
- }
-
- api.xeth().DbPut([]byte(args.Database+args.Key), args.Value)
- *reply = true
- case "db_getHex":
- args := new(DbHexArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- if err := args.requirements(); err != nil {
- return err
- }
-
- res, _ := api.xeth().DbGet([]byte(args.Database + args.Key))
- *reply = newHexData(res)
-
- case "shh_version":
- // Short circuit if whisper is not running
- if api.xeth().Whisper() == nil {
- return NewNotAvailableError(req.Method, "whisper offline")
- }
- // Retrieves the currently running whisper protocol version
- *reply = api.xeth().WhisperVersion()
-
- case "shh_post":
- // Short circuit if whisper is not running
- if api.xeth().Whisper() == nil {
- return NewNotAvailableError(req.Method, "whisper offline")
- }
- // Injects a new message into the whisper network
- args := new(WhisperMessageArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
- err := api.xeth().Whisper().Post(args.Payload, args.To, args.From, args.Topics, args.Priority, args.Ttl)
- if err != nil {
- return err
- }
- *reply = true
-
- case "shh_newIdentity":
- // Short circuit if whisper is not running
- if api.xeth().Whisper() == nil {
- return NewNotAvailableError(req.Method, "whisper offline")
- }
- // Creates a new whisper identity to use for sending/receiving messages
- *reply = api.xeth().Whisper().NewIdentity()
-
- case "shh_hasIdentity":
- // Short circuit if whisper is not running
- if api.xeth().Whisper() == nil {
- return NewNotAvailableError(req.Method, "whisper offline")
- }
- // Checks if an identity if owned or not
- args := new(WhisperIdentityArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
- *reply = api.xeth().Whisper().HasIdentity(args.Identity)
-
- case "shh_newFilter":
- // Short circuit if whisper is not running
- if api.xeth().Whisper() == nil {
- return NewNotAvailableError(req.Method, "whisper offline")
- }
- // Create a new filter to watch and match messages with
- args := new(WhisperFilterArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
- id := api.xeth().NewWhisperFilter(args.To, args.From, args.Topics)
- *reply = newHexNum(big.NewInt(int64(id)).Bytes())
-
- case "shh_uninstallFilter":
- // Short circuit if whisper is not running
- if api.xeth().Whisper() == nil {
- return NewNotAvailableError(req.Method, "whisper offline")
- }
- // Remove an existing filter watching messages
- args := new(FilterIdArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
- *reply = api.xeth().UninstallWhisperFilter(args.Id)
-
- case "shh_getFilterChanges":
- // Short circuit if whisper is not running
- if api.xeth().Whisper() == nil {
- return NewNotAvailableError(req.Method, "whisper offline")
- }
- // Retrieve all the new messages arrived since the last request
- args := new(FilterIdArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
- *reply = api.xeth().WhisperMessagesChanged(args.Id)
-
- case "shh_getMessages":
- // Short circuit if whisper is not running
- if api.xeth().Whisper() == nil {
- return NewNotAvailableError(req.Method, "whisper offline")
- }
- // Retrieve all the cached messages matching a specific, existing filter
- args := new(FilterIdArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
- *reply = api.xeth().WhisperMessages(args.Id)
-
- case "eth_hashrate":
- *reply = newHexNum(api.xeth().HashRate())
- case "ext_disasm":
- args := new(SourceArgs)
- if err := json.Unmarshal(req.Params, &args); err != nil {
- return err
- }
-
- *reply = vm.Disasm(common.FromHex(args.Source))
-
- // case "eth_register":
- // // Placeholder for actual type
- // args := new(HashIndexArgs)
- // if err := json.Unmarshal(req.Params, &args); err != nil {
- // return err
- // }
- // *reply = api.xeth().Register(args.Hash)
- // case "eth_unregister":
- // args := new(HashIndexArgs)
- // if err := json.Unmarshal(req.Params, &args); err != nil {
- // return err
- // }
- // *reply = api.xeth().Unregister(args.Hash)
- // case "eth_watchTx":
- // args := new(HashIndexArgs)
- // if err := json.Unmarshal(req.Params, &args); err != nil {
- // return err
- // }
- // *reply = api.xeth().PullWatchTx(args.Hash)
- default:
- return NewNotImplementedError(req.Method)
- }
-
- // glog.V(logger.Detail).Infof("Reply: %v\n", reply)
- return nil
-}
-
-func (api *EthereumApi) doCall(params json.RawMessage) (string, string, error) {
- args := new(CallArgs)
- if err := json.Unmarshal(params, &args); err != nil {
- return "", "", err
- }
-
- var gas string
- if args.Gas == nil {
- gas = ""
- } else {
- gas = args.Gas.String()
- }
-
- var gasprice string
- if args.GasPrice == nil {
- gasprice = ""
- } else {
- gasprice = args.GasPrice.String()
- }
-
- return api.xethAtStateNum(args.BlockNumber).Call(args.From, args.To, args.Value.String(), gas, gasprice, args.Data)
-}
diff --git a/rpc/api/admin.go b/rpc/api/admin.go
index a6b9cf050..b27482cfe 100644
--- a/rpc/api/admin.go
+++ b/rpc/api/admin.go
@@ -11,6 +11,7 @@ import (
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc/codec"
+ "github.com/ethereum/go-ethereum/rpc/comms"
"github.com/ethereum/go-ethereum/rpc/shared"
"github.com/ethereum/go-ethereum/xeth"
)
@@ -23,8 +24,6 @@ const (
var (
// mapping between methods and handlers
AdminMapping = map[string]adminhandler{
- // "admin_startRPC": (*adminApi).StartRPC,
- // "admin_stopRPC": (*adminApi).StopRPC,
"admin_addPeer": (*adminApi).AddPeer,
"admin_peers": (*adminApi).Peers,
"admin_nodeInfo": (*adminApi).NodeInfo,
@@ -34,6 +33,8 @@ var (
"admin_chainSyncStatus": (*adminApi).ChainSyncStatus,
"admin_setSolc": (*adminApi).SetSolc,
"admin_datadir": (*adminApi).DataDir,
+ "admin_startRPC": (*adminApi).StartRPC,
+ "admin_stopRPC": (*adminApi).StopRPC,
}
)
@@ -44,25 +45,25 @@ type adminhandler func(*adminApi, *shared.Request) (interface{}, error)
type adminApi struct {
xeth *xeth.XEth
ethereum *eth.Ethereum
- methods map[string]adminhandler
- codec codec.ApiCoder
+ codec codec.Codec
+ coder codec.ApiCoder
}
// create a new admin api instance
-func NewAdminApi(xeth *xeth.XEth, ethereum *eth.Ethereum, coder codec.Codec) *adminApi {
+func NewAdminApi(xeth *xeth.XEth, ethereum *eth.Ethereum, codec codec.Codec) *adminApi {
return &adminApi{
xeth: xeth,
ethereum: ethereum,
- methods: AdminMapping,
- codec: coder.New(nil),
+ codec: codec,
+ coder: codec.New(nil),
}
}
// collection with supported methods
func (self *adminApi) Methods() []string {
- methods := make([]string, len(self.methods))
+ methods := make([]string, len(AdminMapping))
i := 0
- for k := range self.methods {
+ for k := range AdminMapping {
methods[i] = k
i++
}
@@ -71,7 +72,7 @@ func (self *adminApi) Methods() []string {
// Execute given request
func (self *adminApi) Execute(req *shared.Request) (interface{}, error) {
- if callback, ok := self.methods[req.Method]; ok {
+ if callback, ok := AdminMapping[req.Method]; ok {
return callback(self, req)
}
@@ -79,7 +80,7 @@ func (self *adminApi) Execute(req *shared.Request) (interface{}, error) {
}
func (self *adminApi) Name() string {
- return AdminApiName
+ return shared.AdminApiName
}
func (self *adminApi) ApiVersion() string {
@@ -88,7 +89,7 @@ func (self *adminApi) ApiVersion() string {
func (self *adminApi) AddPeer(req *shared.Request) (interface{}, error) {
args := new(AddPeerArgs)
- if err := self.codec.Decode(req.Params, &args); err != nil {
+ if err := self.coder.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
@@ -103,33 +104,6 @@ func (self *adminApi) Peers(req *shared.Request) (interface{}, error) {
return self.ethereum.PeersInfo(), nil
}
-func (self *adminApi) StartRPC(req *shared.Request) (interface{}, error) {
- return false, nil
- // Enable when http rpc interface is refactored to prevent import cycles
- // args := new(StartRpcArgs)
- // if err := self.codec.Decode(req.Params, &args); err != nil {
- // return nil, shared.NewDecodeParamError(err.Error())
- // }
- //
- // cfg := rpc.RpcConfig{
- // ListenAddress: args.Address,
- // ListenPort: args.Port,
- // }
- //
- // err := rpc.Start(self.xeth, cfg)
- // if err == nil {
- // return true, nil
- // }
- // return false, err
-}
-
-func (self *adminApi) StopRPC(req *shared.Request) (interface{}, error) {
- return false, nil
- // Enable when http rpc interface is refactored to prevent import cycles
- // rpc.Stop()
- // return true, nil
-}
-
func (self *adminApi) NodeInfo(req *shared.Request) (interface{}, error) {
return self.ethereum.NodeInfo(), nil
}
@@ -149,7 +123,7 @@ func hasAllBlocks(chain *core.ChainManager, bs []*types.Block) bool {
func (self *adminApi) ImportChain(req *shared.Request) (interface{}, error) {
args := new(ImportExportChainArgs)
- if err := self.codec.Decode(req.Params, &args); err != nil {
+ if err := self.coder.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
@@ -192,7 +166,7 @@ func (self *adminApi) ImportChain(req *shared.Request) (interface{}, error) {
func (self *adminApi) ExportChain(req *shared.Request) (interface{}, error) {
args := new(ImportExportChainArgs)
- if err := self.codec.Decode(req.Params, &args); err != nil {
+ if err := self.coder.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
@@ -210,7 +184,7 @@ func (self *adminApi) ExportChain(req *shared.Request) (interface{}, error) {
func (self *adminApi) Verbosity(req *shared.Request) (interface{}, error) {
args := new(VerbosityArgs)
- if err := self.codec.Decode(req.Params, &args); err != nil {
+ if err := self.coder.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
@@ -222,16 +196,16 @@ func (self *adminApi) ChainSyncStatus(req *shared.Request) (interface{}, error)
pending, cached, importing, estimate := self.ethereum.Downloader().Stats()
return map[string]interface{}{
- "blocksAvailable": pending,
+ "blocksAvailable": pending,
"blocksWaitingForImport": cached,
- "importing": importing,
- "estimate": estimate.String(),
+ "importing": importing,
+ "estimate": estimate.String(),
}, nil
}
func (self *adminApi) SetSolc(req *shared.Request) (interface{}, error) {
args := new(SetSolcArgs)
- if err := self.codec.Decode(req.Params, &args); err != nil {
+ if err := self.coder.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
@@ -241,3 +215,32 @@ func (self *adminApi) SetSolc(req *shared.Request) (interface{}, error) {
}
return solc.Info(), nil
}
+
+func (self *adminApi) StartRPC(req *shared.Request) (interface{}, error) {
+ args := new(StartRPCArgs)
+ if err := self.coder.Decode(req.Params, &args); err != nil {
+ return nil, shared.NewDecodeParamError(err.Error())
+ }
+
+ cfg := comms.HttpConfig{
+ ListenAddress: args.ListenAddress,
+ ListenPort: args.ListenPort,
+ CorsDomain: args.CorsDomain,
+ }
+
+ apis, err := ParseApiString(args.Apis, self.codec, self.xeth, self.ethereum)
+ if err != nil {
+ return false, err
+ }
+
+ err = comms.StartHttp(cfg, self.codec, Merge(apis...))
+ if err == nil {
+ return true, nil
+ }
+ return false, err
+}
+
+func (self *adminApi) StopRPC(req *shared.Request) (interface{}, error) {
+ comms.StopHttp()
+ return true, nil
+}
diff --git a/rpc/api/admin_args.go b/rpc/api/admin_args.go
index 56bb57e20..5437971ca 100644
--- a/rpc/api/admin_args.go
+++ b/rpc/api/admin_args.go
@@ -95,3 +95,55 @@ func (args *SetSolcArgs) UnmarshalJSON(b []byte) (err error) {
return shared.NewInvalidTypeError("path", "not a string")
}
+
+type StartRPCArgs struct {
+ ListenAddress string
+ ListenPort uint
+ CorsDomain string
+ Apis string
+}
+
+func (args *StartRPCArgs) UnmarshalJSON(b []byte) (err error) {
+ var obj []interface{}
+ if err := json.Unmarshal(b, &obj); err != nil {
+ return shared.NewDecodeParamError(err.Error())
+ }
+
+ args.ListenAddress = "127.0.0.1"
+ args.ListenPort = 8545
+ args.Apis = "net,eth,web3"
+
+ if len(obj) >= 1 {
+ if addr, ok := obj[0].(string); ok {
+ args.ListenAddress = addr
+ } else {
+ return shared.NewInvalidTypeError("listenAddress", "not a string")
+ }
+ }
+
+ if len(obj) >= 2 {
+ if port, ok := obj[1].(float64); ok && port >= 0 && port <= 64*1024 {
+ args.ListenPort = uint(port)
+ } else {
+ return shared.NewInvalidTypeError("listenPort", "not a valid port number")
+ }
+ }
+
+ if len(obj) >= 3 {
+ if corsDomain, ok := obj[2].(string); ok {
+ args.CorsDomain = corsDomain
+ } else {
+ return shared.NewInvalidTypeError("corsDomain", "not a string")
+ }
+ }
+
+ if len(obj) >= 4 {
+ if apis, ok := obj[3].(string); ok {
+ args.Apis = apis
+ } else {
+ return shared.NewInvalidTypeError("apis", "not a string")
+ }
+ }
+
+ return nil
+}
diff --git a/rpc/api/admin_js.go b/rpc/api/admin_js.go
index c3e713c67..97642ade7 100644
--- a/rpc/api/admin_js.go
+++ b/rpc/api/admin_js.go
@@ -39,6 +39,20 @@ web3._extend({
params: 1,
inputFormatter: [web3._extend.utils.formatInputString],
outputFormatter: web3._extend.formatters.formatOutputString
+ }),
+ new web3._extend.Method({
+ name: 'startRPC',
+ call: 'admin_startRPC',
+ params: 4,
+ inputFormatter: [web3._extend.utils.formatInputString,web3._extend.utils.formatInputInteger,web3._extend.utils.formatInputString,web3._extend.utils.formatInputString],
+ outputFormatter: web3._extend.formatters.formatOutputBool
+ }),
+ new web3._extend.Method({
+ name: 'stopRPC',
+ call: 'admin_stopRPC',
+ params: 0,
+ inputFormatter: [],
+ outputFormatter: web3._extend.formatters.formatOutputBool
})
],
properties:
diff --git a/rpc/api/api.go b/rpc/api/api.go
index e431e5c1e..ca1ccb9a5 100644
--- a/rpc/api/api.go
+++ b/rpc/api/api.go
@@ -1,48 +1,10 @@
package api
import (
- "strings"
-
"github.com/ethereum/go-ethereum/rpc/shared"
)
-const (
- AdminApiName = "admin"
- EthApiName = "eth"
- DebugApiName = "debug"
- MergedApiName = "merged"
- MinerApiName = "miner"
- NetApiName = "net"
- ShhApiName = "shh"
- TxPoolApiName = "txpool"
- PersonalApiName = "personal"
- Web3ApiName = "web3"
-)
-
-var (
- // List with all API's which are offered over the IPC interface by default
- DefaultIpcApis = strings.Join([]string{
- AdminApiName, EthApiName, DebugApiName, MinerApiName, NetApiName,
- ShhApiName, TxPoolApiName, PersonalApiName, Web3ApiName,
- }, ",")
-)
-
-// Ethereum RPC API interface
-type EthereumApi interface {
- // API identifier
- Name() string
-
- // API version
- ApiVersion() string
-
- // Execute the given request and returns the response or an error
- Execute(*shared.Request) (interface{}, error)
-
- // List of supported RCP methods this API provides
- Methods() []string
-}
-
// Merge multiple API's to a single API instance
-func Merge(apis ...EthereumApi) EthereumApi {
+func Merge(apis ...shared.EthereumApi) shared.EthereumApi {
return newMergedApi(apis...)
}
diff --git a/rpc/api/api_test.go b/rpc/api/api_test.go
index f1a47944d..7e273ef28 100644
--- a/rpc/api/api_test.go
+++ b/rpc/api/api_test.go
@@ -3,7 +3,14 @@ package api
import (
"testing"
+ "encoding/json"
+ "strconv"
+
+ "github.com/ethereum/go-ethereum/common/compiler"
+ "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/rpc/codec"
+ "github.com/ethereum/go-ethereum/rpc/shared"
+ "github.com/ethereum/go-ethereum/xeth"
)
func TestParseApiString(t *testing.T) {
@@ -40,3 +47,107 @@ func TestParseApiString(t *testing.T) {
}
}
+
+const solcVersion = "0.9.23"
+
+func TestCompileSolidity(t *testing.T) {
+
+ solc, err := compiler.New("")
+ if solc == nil {
+ t.Skip("no solc found: skip")
+ } else if solc.Version() != solcVersion {
+ t.Skip("WARNING: skipping test because of solc different version (%v, test written for %v, may need to update)", solc.Version(), solcVersion)
+ }
+ source := `contract test {\n` +
+ " /// @notice Will multiply `a` by 7." + `\n` +
+ ` function multiply(uint a) returns(uint d) {\n` +
+ ` return a * 7;\n` +
+ ` }\n` +
+ `}\n`
+
+ jsonstr := `{"jsonrpc":"2.0","method":"eth_compileSolidity","params":["` + source + `"],"id":64}`
+
+ expCode := "0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056"
+ expAbiDefinition := `[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]`
+ expUserDoc := `{"methods":{"multiply(uint256)":{"notice":"Will multiply ` + "`a`" + ` by 7."}}}`
+ expDeveloperDoc := `{"methods":{}}`
+ expCompilerVersion := solc.Version()
+ expLanguage := "Solidity"
+ expLanguageVersion := "0"
+ expSource := source
+
+ xeth := xeth.NewTest(&eth.Ethereum{}, nil)
+ api := NewEthApi(xeth, codec.JSON)
+
+ var rpcRequest shared.Request
+ json.Unmarshal([]byte(jsonstr), &rpcRequest)
+
+ response, err := api.CompileSolidity(&rpcRequest)
+ if err != nil {
+ t.Errorf("Execution failed, %v", err)
+ }
+
+ respjson, err := json.Marshal(response)
+ if err != nil {
+ t.Errorf("expected no error, got %v", err)
+ }
+
+ var contracts = make(map[string]*compiler.Contract)
+ err = json.Unmarshal(respjson, &contracts)
+ if err != nil {
+ t.Errorf("expected no error, got %v", err)
+ }
+
+ if len(contracts) != 1 {
+ t.Errorf("expected one contract, got %v", len(contracts))
+ }
+
+ contract := contracts["test"]
+
+ if contract.Code != expCode {
+ t.Errorf("Expected \n%s got \n%s", expCode, contract.Code)
+ }
+
+ if strconv.Quote(contract.Info.Source) != `"`+expSource+`"` {
+ t.Errorf("Expected \n'%s' got \n'%s'", expSource, strconv.Quote(contract.Info.Source))
+ }
+
+ if contract.Info.Language != expLanguage {
+ t.Errorf("Expected %s got %s", expLanguage, contract.Info.Language)
+ }
+
+ if contract.Info.LanguageVersion != expLanguageVersion {
+ t.Errorf("Expected %s got %s", expLanguageVersion, contract.Info.LanguageVersion)
+ }
+
+ if contract.Info.CompilerVersion != expCompilerVersion {
+ t.Errorf("Expected %s got %s", expCompilerVersion, contract.Info.CompilerVersion)
+ }
+
+ userdoc, err := json.Marshal(contract.Info.UserDoc)
+ if err != nil {
+ t.Errorf("expected no error, got %v", err)
+ }
+
+ devdoc, err := json.Marshal(contract.Info.DeveloperDoc)
+ if err != nil {
+ t.Errorf("expected no error, got %v", err)
+ }
+
+ abidef, err := json.Marshal(contract.Info.AbiDefinition)
+ if err != nil {
+ t.Errorf("expected no error, got %v", err)
+ }
+
+ if string(abidef) != expAbiDefinition {
+ t.Errorf("Expected \n'%s' got \n'%s'", expAbiDefinition, string(abidef))
+ }
+
+ if string(userdoc) != expUserDoc {
+ t.Errorf("Expected \n'%s' got \n'%s'", expUserDoc, string(userdoc))
+ }
+
+ if string(devdoc) != expDeveloperDoc {
+ t.Errorf("Expected %s got %s", expDeveloperDoc, string(devdoc))
+ }
+}
diff --git a/rpc/api/args.go b/rpc/api/args.go
new file mode 100644
index 000000000..fc85448e6
--- /dev/null
+++ b/rpc/api/args.go
@@ -0,0 +1,58 @@
+package api
+
+import (
+ "encoding/json"
+
+ "github.com/ethereum/go-ethereum/rpc/shared"
+)
+
+type CompileArgs struct {
+ Source string
+}
+
+func (args *CompileArgs) UnmarshalJSON(b []byte) (err error) {
+ var obj []interface{}
+ if err := json.Unmarshal(b, &obj); err != nil {
+ return shared.NewDecodeParamError(err.Error())
+ }
+
+ if len(obj) < 1 {
+ return shared.NewInsufficientParamsError(len(obj), 1)
+ }
+ argstr, ok := obj[0].(string)
+ if !ok {
+ return shared.NewInvalidTypeError("arg0", "is not a string")
+ }
+ args.Source = argstr
+
+ return nil
+}
+
+type FilterStringArgs struct {
+ Word string
+}
+
+func (args *FilterStringArgs) UnmarshalJSON(b []byte) (err error) {
+ var obj []interface{}
+ if err := json.Unmarshal(b, &obj); err != nil {
+ return shared.NewDecodeParamError(err.Error())
+ }
+
+ if len(obj) < 1 {
+ return shared.NewInsufficientParamsError(len(obj), 1)
+ }
+
+ var argstr string
+ argstr, ok := obj[0].(string)
+ if !ok {
+ return shared.NewInvalidTypeError("filter", "not a string")
+ }
+ switch argstr {
+ case "latest", "pending":
+ break
+ default:
+ return shared.NewValidationError("Word", "Must be `latest` or `pending`")
+ }
+ args.Word = argstr
+ return nil
+}
diff --git a/rpc/args_test.go b/rpc/api/args_test.go
index fd20dbab8..a30f247bc 100644
--- a/rpc/args_test.go
+++ b/rpc/api/args_test.go
@@ -1,4 +1,4 @@
-package rpc
+package api
import (
"bytes"
@@ -6,6 +6,8 @@ import (
"fmt"
"math/big"
"testing"
+
+ "github.com/ethereum/go-ethereum/rpc/shared"
)
func TestBlockheightInvalidString(t *testing.T) {
@@ -68,7 +70,7 @@ func ExpectValidationError(err error) string {
switch err.(type) {
case nil:
str = "Expected error but didn't get one"
- case *ValidationError:
+ case *shared.ValidationError:
break
default:
str = fmt.Sprintf("Expected *rpc.ValidationError but got %T with message `%s`", err, err.Error())
@@ -81,7 +83,7 @@ func ExpectInvalidTypeError(err error) string {
switch err.(type) {
case nil:
str = "Expected error but didn't get one"
- case *InvalidTypeError:
+ case *shared.InvalidTypeError:
break
default:
str = fmt.Sprintf("Expected *rpc.InvalidTypeError but got %T with message `%s`", err, err.Error())
@@ -94,7 +96,7 @@ func ExpectInsufficientParamsError(err error) string {
switch err.(type) {
case nil:
str = "Expected error but didn't get one"
- case *InsufficientParamsError:
+ case *shared.InsufficientParamsError:
break
default:
str = fmt.Sprintf("Expected *rpc.InsufficientParamsError but got %T with message %s", err, err.Error())
@@ -107,7 +109,7 @@ func ExpectDecodeParamError(err error) string {
switch err.(type) {
case nil:
str = "Expected error but didn't get one"
- case *DecodeParamError:
+ case *shared.DecodeParamError:
break
default:
str = fmt.Sprintf("Expected *rpc.DecodeParamError but got %T with message `%s`", err, err.Error())
diff --git a/rpc/api/db.go b/rpc/api/db.go
new file mode 100644
index 000000000..6f10d6447
--- /dev/null
+++ b/rpc/api/db.go
@@ -0,0 +1,128 @@
+package api
+
+import (
+ "github.com/ethereum/go-ethereum/eth"
+ "github.com/ethereum/go-ethereum/rpc/codec"
+ "github.com/ethereum/go-ethereum/rpc/shared"
+ "github.com/ethereum/go-ethereum/xeth"
+)
+
+const (
+ DbApiversion = "1.0"
+)
+
+var (
+ // mapping between methods and handlers
+ DbMapping = map[string]dbhandler{
+ "db_getString": (*dbApi).GetString,
+ "db_putString": (*dbApi).PutString,
+ "db_getHex": (*dbApi).GetHex,
+ "db_putHex": (*dbApi).PutHex,
+ }
+)
+
+// db callback handler
+type dbhandler func(*dbApi, *shared.Request) (interface{}, error)
+
+// db api provider
+type dbApi struct {
+ xeth *xeth.XEth
+ ethereum *eth.Ethereum
+ methods map[string]dbhandler
+ codec codec.ApiCoder
+}
+
+// create a new db api instance
+func NewDbApi(xeth *xeth.XEth, ethereum *eth.Ethereum, coder codec.Codec) *dbApi {
+ return &dbApi{
+ xeth: xeth,
+ ethereum: ethereum,
+ methods: DbMapping,
+ codec: coder.New(nil),
+ }
+}
+
+// collection with supported methods
+func (self *dbApi) Methods() []string {
+ methods := make([]string, len(self.methods))
+ i := 0
+ for k := range self.methods {
+ methods[i] = k
+ i++
+ }
+ return methods
+}
+
+// Execute given request
+func (self *dbApi) Execute(req *shared.Request) (interface{}, error) {
+ if callback, ok := self.methods[req.Method]; ok {
+ return callback(self, req)
+ }
+
+ return nil, &shared.NotImplementedError{req.Method}
+}
+
+func (self *dbApi) Name() string {
+ return shared.DbApiName
+}
+
+func (self *dbApi) ApiVersion() string {
+ return DbApiversion
+}
+
+func (self *dbApi) GetString(req *shared.Request) (interface{}, error) {
+ args := new(DbArgs)
+ if err := self.codec.Decode(req.Params, &args); err != nil {
+ return nil, shared.NewDecodeParamError(err.Error())
+ }
+
+ if err := args.requirements(); err != nil {
+ return nil, err
+ }
+
+ ret, err := self.xeth.DbGet([]byte(args.Database + args.Key))
+ return string(ret), err
+}
+
+func (self *dbApi) PutString(req *shared.Request) (interface{}, error) {
+ args := new(DbArgs)
+ if err := self.codec.Decode(req.Params, &args); err != nil {
+ return nil, shared.NewDecodeParamError(err.Error())
+ }
+
+ if err := args.requirements(); err != nil {
+ return nil, err
+ }
+
+ return self.xeth.DbPut([]byte(args.Database+args.Key), args.Value), nil
+}
+
+func (self *dbApi) GetHex(req *shared.Request) (interface{}, error) {
+ args := new(DbHexArgs)
+ if err := self.codec.Decode(req.Params, &args); err != nil {
+ return nil, shared.NewDecodeParamError(err.Error())
+ }
+
+ if err := args.requirements(); err != nil {
+ return nil, err
+ }
+
+ if res, err := self.xeth.DbGet([]byte(args.Database + args.Key)); err == nil {
+ return newHexData(res), nil
+ } else {
+ return nil, err
+ }
+}
+
+func (self *dbApi) PutHex(req *shared.Request) (interface{}, error) {
+ args := new(DbHexArgs)
+ if err := self.codec.Decode(req.Params, &args); err != nil {
+ return nil, shared.NewDecodeParamError(err.Error())
+ }
+
+ if err := args.requirements(); err != nil {
+ return nil, err
+ }
+
+ return self.xeth.DbPut([]byte(args.Database+args.Key), args.Value), nil
+}
diff --git a/rpc/api/db_args.go b/rpc/api/db_args.go
new file mode 100644
index 000000000..459616d87
--- /dev/null
+++ b/rpc/api/db_args.go
@@ -0,0 +1,110 @@
+package api
+
+import (
+ "encoding/json"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/rpc/shared"
+)
+
+type DbArgs struct {
+ Database string
+ Key string
+ Value []byte
+}
+
+func (args *DbArgs) UnmarshalJSON(b []byte) (err error) {
+ var obj []interface{}
+ if err := json.Unmarshal(b, &obj); err != nil {
+ return shared.NewDecodeParamError(err.Error())
+ }
+
+ if len(obj) < 2 {
+ return shared.NewInsufficientParamsError(len(obj), 2)
+ }
+
+ var objstr string
+ var ok bool
+
+ if objstr, ok = obj[0].(string); !ok {
+ return shared.NewInvalidTypeError("database", "not a string")
+ }
+ args.Database = objstr
+
+ if objstr, ok = obj[1].(string); !ok {
+ return shared.NewInvalidTypeError("key", "not a string")
+ }
+ args.Key = objstr
+
+ if len(obj) > 2 {
+ objstr, ok = obj[2].(string)
+ if !ok {
+ return shared.NewInvalidTypeError("value", "not a string")
+ }
+
+ args.Value = []byte(objstr)
+ }
+
+ return nil
+}
+
+func (a *DbArgs) requirements() error {
+ if len(a.Database) == 0 {
+ return shared.NewValidationError("Database", "cannot be blank")
+ }
+ if len(a.Key) == 0 {
+ return shared.NewValidationError("Key", "cannot be blank")
+ }
+ return nil
+}
+
+type DbHexArgs struct {
+ Database string
+ Key string
+ Value []byte
+}
+
+func (args *DbHexArgs) UnmarshalJSON(b []byte) (err error) {
+ var obj []interface{}
+ if err := json.Unmarshal(b, &obj); err != nil {
+ return shared.NewDecodeParamError(err.Error())
+ }
+
+ if len(obj) < 2 {
+ return shared.NewInsufficientParamsError(len(obj), 2)
+ }
+
+ var objstr string
+ var ok bool
+
+ if objstr, ok = obj[0].(string); !ok {
+ return shared.NewInvalidTypeError("database", "not a string")
+ }
+ args.Database = objstr
+
+ if objstr, ok = obj[1].(string); !ok {
+ return shared.NewInvalidTypeError("key", "not a string")
+ }
+ args.Key = objstr
+
+ if len(obj) > 2 {
+ objstr, ok = obj[2].(string)
+ if !ok {
+ return shared.NewInvalidTypeError("value", "not a string")
+ }
+
+ args.Value = common.FromHex(objstr)
+ }
+
+ return nil
+}
+
+func (a *DbHexArgs) requirements() error {
+ if len(a.Database) == 0 {
+ return shared.NewValidationError("Database", "cannot be blank")
+ }
+ if len(a.Key) == 0 {
+ return shared.NewValidationError("Key", "cannot be blank")
+ }
+ return nil
+}
diff --git a/rpc/api/db_js.go b/rpc/api/db_js.go
new file mode 100644
index 000000000..62cdcd20e
--- /dev/null
+++ b/rpc/api/db_js.go
@@ -0,0 +1,41 @@
+package api
+
+const Db_JS = `
+web3._extend({
+ property: 'db',
+ methods:
+ [
+ new web3._extend.Method({
+ name: 'getString',
+ call: 'db_getString',
+ params: 2,
+ inputFormatter: [web3._extend.formatters.formatInputString, web3._extend.formatters.formatInputString],
+ outputFormatter: web3._extend.formatters.formatOutputString
+ }),
+ new web3._extend.Method({
+ name: 'putString',
+ call: 'db_putString',
+ params: 3,
+ inputFormatter: [web3._extend.formatters.formatInputString, web3._extend.formatters.formatInputString, web3._extend.formatters.formatInputString],
+ outputFormatter: web3._extend.formatters.formatOutputBool
+ }),
+ new web3._extend.Method({
+ name: 'getHex',
+ call: 'db_getHex',
+ params: 2,
+ inputFormatter: [web3._extend.formatters.formatInputString, web3._extend.formatters.formatInputString],
+ outputFormatter: web3._extend.formatters.formatOutputString
+ }),
+ new web3._extend.Method({
+ name: 'putHex',
+ call: 'db_putHex',
+ params: 3,
+ inputFormatter: [web3._extend.formatters.formatInputString, web3._extend.formatters.formatInputString, web3._extend.formatters.formatInputString],
+ outputFormatter: web3._extend.formatters.formatOutputBool
+ }),
+ ],
+ properties:
+ [
+ ]
+});
+`
diff --git a/rpc/api/debug.go b/rpc/api/debug.go
index 5b6a449dc..b451d8662 100644
--- a/rpc/api/debug.go
+++ b/rpc/api/debug.go
@@ -71,7 +71,7 @@ func (self *debugApi) Execute(req *shared.Request) (interface{}, error) {
}
func (self *debugApi) Name() string {
- return DebugApiName
+ return shared.DebugApiName
}
func (self *debugApi) ApiVersion() string {
diff --git a/rpc/api/eth.go b/rpc/api/eth.go
index cb678922b..0dff138c6 100644
--- a/rpc/api/eth.go
+++ b/rpc/api/eth.go
@@ -28,48 +28,49 @@ type ethhandler func(*ethApi, *shared.Request) (interface{}, error)
var (
ethMapping = map[string]ethhandler{
- "eth_accounts": (*ethApi).Accounts,
- "eth_blockNumber": (*ethApi).BlockNumber,
- "eth_getBalance": (*ethApi).GetBalance,
- "eth_protocolVersion": (*ethApi).ProtocolVersion,
- "eth_coinbase": (*ethApi).Coinbase,
- "eth_mining": (*ethApi).IsMining,
- "eth_gasPrice": (*ethApi).GasPrice,
- "eth_getStorage": (*ethApi).GetStorage,
- "eth_storageAt": (*ethApi).GetStorage,
- "eth_getStorageAt": (*ethApi).GetStorageAt,
- "eth_getTransactionCount": (*ethApi).GetTransactionCount,
- "eth_getBlockTransactionCountByHash": (*ethApi).GetBlockTransactionCountByHash,
- "eth_getBlockTransactionCountByNumber": (*ethApi).GetBlockTransactionCountByNumber,
- "eth_getUncleCountByBlockHash": (*ethApi).GetUncleCountByBlockHash,
- "eth_getUncleCountByBlockNumber": (*ethApi).GetUncleCountByBlockNumber,
- "eth_getData": (*ethApi).GetData,
- "eth_getCode": (*ethApi).GetData,
- "eth_sign": (*ethApi).Sign,
- "eth_sendRawTransaction": (*ethApi).PushTx,
- "eth_sendTransaction": (*ethApi).SendTransaction,
- "eth_transact": (*ethApi).SendTransaction,
- "eth_estimateGas": (*ethApi).EstimateGas,
- "eth_call": (*ethApi).Call,
- "eth_flush": (*ethApi).Flush,
- "eth_getBlockByHash": (*ethApi).GetBlockByHash,
- "eth_getBlockByNumber": (*ethApi).GetBlockByNumber,
- "eth_getTransactionByHash": (*ethApi).GetTransactionByHash,
- "eth_getTransactionByBlockHashAndIndex": (*ethApi).GetTransactionByBlockHashAndIndex,
- "eth_getUncleByBlockHashAndIndex": (*ethApi).GetUncleByBlockHashAndIndex,
- "eth_getUncleByBlockNumberAndIndex": (*ethApi).GetUncleByBlockNumberAndIndex,
- "eth_getCompilers": (*ethApi).GetCompilers,
- "eth_compileSolidity": (*ethApi).CompileSolidity,
- "eth_newFilter": (*ethApi).NewFilter,
- "eth_newBlockFilter": (*ethApi).NewBlockFilter,
- "eth_newPendingTransactionFilter": (*ethApi).NewPendingTransactionFilter,
- "eth_uninstallFilter": (*ethApi).UninstallFilter,
- "eth_getFilterChanges": (*ethApi).GetFilterChanges,
- "eth_getFilterLogs": (*ethApi).GetFilterLogs,
- "eth_getLogs": (*ethApi).GetLogs,
- "eth_hashrate": (*ethApi).Hashrate,
- "eth_getWork": (*ethApi).GetWork,
- "eth_submitWork": (*ethApi).SubmitWork,
+ "eth_accounts": (*ethApi).Accounts,
+ "eth_blockNumber": (*ethApi).BlockNumber,
+ "eth_getBalance": (*ethApi).GetBalance,
+ "eth_protocolVersion": (*ethApi).ProtocolVersion,
+ "eth_coinbase": (*ethApi).Coinbase,
+ "eth_mining": (*ethApi).IsMining,
+ "eth_gasPrice": (*ethApi).GasPrice,
+ "eth_getStorage": (*ethApi).GetStorage,
+ "eth_storageAt": (*ethApi).GetStorage,
+ "eth_getStorageAt": (*ethApi).GetStorageAt,
+ "eth_getTransactionCount": (*ethApi).GetTransactionCount,
+ "eth_getBlockTransactionCountByHash": (*ethApi).GetBlockTransactionCountByHash,
+ "eth_getBlockTransactionCountByNumber": (*ethApi).GetBlockTransactionCountByNumber,
+ "eth_getUncleCountByBlockHash": (*ethApi).GetUncleCountByBlockHash,
+ "eth_getUncleCountByBlockNumber": (*ethApi).GetUncleCountByBlockNumber,
+ "eth_getData": (*ethApi).GetData,
+ "eth_getCode": (*ethApi).GetData,
+ "eth_sign": (*ethApi).Sign,
+ "eth_sendRawTransaction": (*ethApi).SendRawTransaction,
+ "eth_sendTransaction": (*ethApi).SendTransaction,
+ "eth_transact": (*ethApi).SendTransaction,
+ "eth_estimateGas": (*ethApi).EstimateGas,
+ "eth_call": (*ethApi).Call,
+ "eth_flush": (*ethApi).Flush,
+ "eth_getBlockByHash": (*ethApi).GetBlockByHash,
+ "eth_getBlockByNumber": (*ethApi).GetBlockByNumber,
+ "eth_getTransactionByHash": (*ethApi).GetTransactionByHash,
+ "eth_getTransactionByBlockNumberAndIndex": (*ethApi).GetTransactionByBlockNumberAndIndex,
+ "eth_getTransactionByBlockHashAndIndex": (*ethApi).GetTransactionByBlockHashAndIndex,
+ "eth_getUncleByBlockHashAndIndex": (*ethApi).GetUncleByBlockHashAndIndex,
+ "eth_getUncleByBlockNumberAndIndex": (*ethApi).GetUncleByBlockNumberAndIndex,
+ "eth_getCompilers": (*ethApi).GetCompilers,
+ "eth_compileSolidity": (*ethApi).CompileSolidity,
+ "eth_newFilter": (*ethApi).NewFilter,
+ "eth_newBlockFilter": (*ethApi).NewBlockFilter,
+ "eth_newPendingTransactionFilter": (*ethApi).NewPendingTransactionFilter,
+ "eth_uninstallFilter": (*ethApi).UninstallFilter,
+ "eth_getFilterChanges": (*ethApi).GetFilterChanges,
+ "eth_getFilterLogs": (*ethApi).GetFilterLogs,
+ "eth_getLogs": (*ethApi).GetLogs,
+ "eth_hashrate": (*ethApi).Hashrate,
+ "eth_getWork": (*ethApi).GetWork,
+ "eth_submitWork": (*ethApi).SubmitWork,
}
)
@@ -99,7 +100,7 @@ func (self *ethApi) Execute(req *shared.Request) (interface{}, error) {
}
func (self *ethApi) Name() string {
- return EthApiName
+ return shared.EthApiName
}
func (self *ethApi) ApiVersion() string {
@@ -115,7 +116,8 @@ func (self *ethApi) Hashrate(req *shared.Request) (interface{}, error) {
}
func (self *ethApi) BlockNumber(req *shared.Request) (interface{}, error) {
- return self.xeth.CurrentBlock().Number(), nil
+ num := self.xeth.CurrentBlock().Number()
+ return newHexNum(num.Bytes()), nil
}
func (self *ethApi) GetBalance(req *shared.Request) (interface{}, error) {
@@ -248,8 +250,7 @@ func (self *ethApi) Sign(req *shared.Request) (interface{}, error) {
return v, nil
}
-
-func (self *ethApi) PushTx(req *shared.Request) (interface{}, error) {
+func (self *ethApi) SendRawTransaction(req *shared.Request) (interface{}, error) {
args := new(NewDataArgs)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
diff --git a/rpc/api/eth_args.go b/rpc/api/eth_args.go
index 54eb7201d..bf8ffead6 100644
--- a/rpc/api/eth_args.go
+++ b/rpc/api/eth_args.go
@@ -227,32 +227,32 @@ func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) {
}
type NewDataArgs struct {
- Data string
+ Data string
}
func (args *NewDataArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
+ var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return shared.NewDecodeParamError(err.Error())
- }
+ if err := json.Unmarshal(b, &obj); err != nil {
+ return shared.NewDecodeParamError(err.Error())
+ }
- // Check for sufficient params
- if len(obj) < 1 {
- return shared.NewInsufficientParamsError(len(obj), 1)
- }
+ // Check for sufficient params
+ if len(obj) < 1 {
+ return shared.NewInsufficientParamsError(len(obj), 1)
+ }
- data, ok := obj[0].(string)
- if !ok {
- return shared.NewInvalidTypeError("data", "not a string")
- }
- args.Data = data
+ data, ok := obj[0].(string)
+ if !ok {
+ return shared.NewInvalidTypeError("data", "not a string")
+ }
+ args.Data = data
- if len(args.Data) == 0 {
- return shared.NewValidationError("data", "is required")
- }
+ if len(args.Data) == 0 {
+ return shared.NewValidationError("data", "is required")
+ }
- return nil
+ return nil
}
type NewSigArgs struct {
@@ -466,21 +466,21 @@ func (args *CallArgs) UnmarshalJSON(b []byte) (err error) {
}
args.Value = num
- if ext.Gas == nil {
- num = big.NewInt(0)
- } else {
+ if ext.Gas != nil {
if num, err = numString(ext.Gas); err != nil {
return err
}
+ } else {
+ num = nil
}
args.Gas = num
- if ext.GasPrice == nil {
- num = big.NewInt(0)
- } else {
+ if ext.GasPrice != nil {
if num, err = numString(ext.GasPrice); err != nil {
return err
}
+ } else {
+ num = nil
}
args.GasPrice = num
diff --git a/rpc/api/eth_js.go b/rpc/api/eth_js.go
index f7630bdd5..e1268eb76 100644
--- a/rpc/api/eth_js.go
+++ b/rpc/api/eth_js.go
@@ -1,3 +1,20 @@
package api
// JS api provided by web3.js
+// eth_sign not standard
+
+const Eth_JS = `
+web3._extend({
+ property: 'eth',
+ methods:
+ [
+ new web3._extend.Method({
+ name: 'sign',
+ call: 'eth_sign',
+ params: 2,
+ inputFormatter: [web3._extend.formatters.formatInputString,web3._extend.formatters.formatInputString],
+ outputFormatter: web3._extend.formatters.formatOutputString
+ })
+ ]
+});
+`
diff --git a/rpc/api/mergedapi.go b/rpc/api/mergedapi.go
index b62477a14..bc4fa32e8 100644
--- a/rpc/api/mergedapi.go
+++ b/rpc/api/mergedapi.go
@@ -1,6 +1,8 @@
package api
import (
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rpc/shared"
)
@@ -11,14 +13,14 @@ const (
// combines multiple API's
type MergedApi struct {
apis map[string]string
- methods map[string]EthereumApi
+ methods map[string]shared.EthereumApi
}
// create new merged api instance
-func newMergedApi(apis ...EthereumApi) *MergedApi {
+func newMergedApi(apis ...shared.EthereumApi) *MergedApi {
mergedApi := new(MergedApi)
mergedApi.apis = make(map[string]string, len(apis))
- mergedApi.methods = make(map[string]EthereumApi)
+ mergedApi.methods = make(map[string]shared.EthereumApi)
for _, api := range apis {
mergedApi.apis[api.Name()] = api.ApiVersion()
@@ -40,6 +42,8 @@ func (self *MergedApi) Methods() []string {
// Call the correct API's Execute method for the given request
func (self *MergedApi) Execute(req *shared.Request) (interface{}, error) {
+ glog.V(logger.Detail).Infof("rpc method: %s", req.Method)
+
if res, _ := self.handle(req); res != nil {
return res, nil
}
@@ -50,7 +54,7 @@ func (self *MergedApi) Execute(req *shared.Request) (interface{}, error) {
}
func (self *MergedApi) Name() string {
- return MergedApiName
+ return shared.MergedApiName
}
func (self *MergedApi) ApiVersion() string {
diff --git a/rpc/api/mergedapi_js.go b/rpc/api/mergedapi_js.go
deleted file mode 100644
index 778f64ec1..000000000
--- a/rpc/api/mergedapi_js.go
+++ /dev/null
@@ -1 +0,0 @@
-package api
diff --git a/rpc/api/miner.go b/rpc/api/miner.go
index 0b5e74f52..7a84cb9ae 100644
--- a/rpc/api/miner.go
+++ b/rpc/api/miner.go
@@ -66,7 +66,7 @@ func (self *minerApi) Methods() []string {
}
func (self *minerApi) Name() string {
- return MinerApiName
+ return shared.MinerApiName
}
func (self *minerApi) ApiVersion() string {
diff --git a/rpc/api/net.go b/rpc/api/net.go
index d6888ee4a..761654661 100644
--- a/rpc/api/net.go
+++ b/rpc/api/net.go
@@ -63,7 +63,7 @@ func (self *netApi) Execute(req *shared.Request) (interface{}, error) {
}
func (self *netApi) Name() string {
- return NetApiName
+ return shared.NetApiName
}
func (self *netApi) ApiVersion() string {
@@ -77,7 +77,7 @@ func (self *netApi) Version(req *shared.Request) (interface{}, error) {
// Number of connected peers
func (self *netApi) PeerCount(req *shared.Request) (interface{}, error) {
- return self.xeth.PeerCount(), nil
+ return newHexNum(self.xeth.PeerCount()), nil
}
func (self *netApi) IsListening(req *shared.Request) (interface{}, error) {
diff --git a/rpc/api/personal.go b/rpc/api/personal.go
index 7a6c91c82..b4a63ea7a 100644
--- a/rpc/api/personal.go
+++ b/rpc/api/personal.go
@@ -66,7 +66,7 @@ func (self *personalApi) Execute(req *shared.Request) (interface{}, error) {
}
func (self *personalApi) Name() string {
- return PersonalApiName
+ return shared.PersonalApiName
}
func (self *personalApi) ApiVersion() string {
diff --git a/rpc/api/shh.go b/rpc/api/shh.go
index e83a7b22e..18a8fd15d 100644
--- a/rpc/api/shh.go
+++ b/rpc/api/shh.go
@@ -72,7 +72,7 @@ func (self *shhApi) Execute(req *shared.Request) (interface{}, error) {
}
func (self *shhApi) Name() string {
- return ShhApiName
+ return shared.ShhApiName
}
func (self *shhApi) ApiVersion() string {
diff --git a/rpc/api/txpool.go b/rpc/api/txpool.go
index 64550bdaf..25ad6e9b2 100644
--- a/rpc/api/txpool.go
+++ b/rpc/api/txpool.go
@@ -60,7 +60,7 @@ func (self *txPoolApi) Execute(req *shared.Request) (interface{}, error) {
}
func (self *txPoolApi) Name() string {
- return TxPoolApiName
+ return shared.TxPoolApiName
}
func (self *txPoolApi) ApiVersion() string {
diff --git a/rpc/api/utils.go b/rpc/api/utils.go
index 40bcae52f..6e4835de6 100644
--- a/rpc/api/utils.go
+++ b/rpc/api/utils.go
@@ -7,6 +7,7 @@ import (
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/rpc/codec"
+ "github.com/ethereum/go-ethereum/rpc/shared"
"github.com/ethereum/go-ethereum/xeth"
)
@@ -23,6 +24,14 @@ var (
"chainSyncStatus",
"setSolc",
"datadir",
+ "startRPC",
+ "stopRPC",
+ },
+ "db": []string{
+ "getString",
+ "putString",
+ "getHex",
+ "putHex",
},
"debug": []string{
"dumpBlock",
@@ -123,33 +132,35 @@ var (
)
// Parse a comma separated API string to individual api's
-func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth.Ethereum) ([]EthereumApi, error) {
+func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth.Ethereum) ([]shared.EthereumApi, error) {
if len(strings.TrimSpace(apistr)) == 0 {
return nil, fmt.Errorf("Empty apistr provided")
}
names := strings.Split(apistr, ",")
- apis := make([]EthereumApi, len(names))
+ apis := make([]shared.EthereumApi, len(names))
for i, name := range names {
switch strings.ToLower(strings.TrimSpace(name)) {
- case AdminApiName:
+ case shared.AdminApiName:
apis[i] = NewAdminApi(xeth, eth, codec)
- case DebugApiName:
+ case shared.DebugApiName:
apis[i] = NewDebugApi(xeth, eth, codec)
- case EthApiName:
+ case shared.DbApiName:
+ apis[i] = NewDbApi(xeth, eth, codec)
+ case shared.EthApiName:
apis[i] = NewEthApi(xeth, codec)
- case MinerApiName:
+ case shared.MinerApiName:
apis[i] = NewMinerApi(eth, codec)
- case NetApiName:
+ case shared.NetApiName:
apis[i] = NewNetApi(xeth, eth, codec)
- case ShhApiName:
+ case shared.ShhApiName:
apis[i] = NewShhApi(xeth, eth, codec)
- case TxPoolApiName:
+ case shared.TxPoolApiName:
apis[i] = NewTxPoolApi(xeth, eth, codec)
- case PersonalApiName:
+ case shared.PersonalApiName:
apis[i] = NewPersonalApi(xeth, eth, codec)
- case Web3ApiName:
+ case shared.Web3ApiName:
apis[i] = NewWeb3Api(xeth, codec)
default:
return nil, fmt.Errorf("Unknown API '%s'", name)
@@ -161,19 +172,23 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth.
func Javascript(name string) string {
switch strings.ToLower(strings.TrimSpace(name)) {
- case AdminApiName:
+ case shared.AdminApiName:
return Admin_JS
- case DebugApiName:
+ case shared.DebugApiName:
return Debug_JS
- case MinerApiName:
+ case shared.DbApiName:
+ return Db_JS
+ case shared.EthApiName:
+ return Eth_JS
+ case shared.MinerApiName:
return Miner_JS
- case NetApiName:
+ case shared.NetApiName:
return Net_JS
- case ShhApiName:
+ case shared.ShhApiName:
return Shh_JS
- case TxPoolApiName:
+ case shared.TxPoolApiName:
return TxPool_JS
- case PersonalApiName:
+ case shared.PersonalApiName:
return Personal_JS
}
diff --git a/rpc/api/web3.go b/rpc/api/web3.go
index ed5008446..4c20baa25 100644
--- a/rpc/api/web3.go
+++ b/rpc/api/web3.go
@@ -60,7 +60,7 @@ func (self *web3Api) Execute(req *shared.Request) (interface{}, error) {
}
func (self *web3Api) Name() string {
- return Web3ApiName
+ return shared.Web3ApiName
}
func (self *web3Api) ApiVersion() string {
diff --git a/rpc/api/web3_args.go b/rpc/api/web3_args.go
index 5455a6c8e..38af7191e 100644
--- a/rpc/api/web3_args.go
+++ b/rpc/api/web3_args.go
@@ -1,5 +1,29 @@
package api
+import (
+ "encoding/json"
+
+ "github.com/ethereum/go-ethereum/rpc/shared"
+)
+
type Sha3Args struct {
Data string
}
+
+func (args *Sha3Args) UnmarshalJSON(b []byte) (err error) {
+ var obj []interface{}
+ if err := json.Unmarshal(b, &obj); err != nil {
+ return shared.NewDecodeParamError(err.Error())
+ }
+
+ if len(obj) < 1 {
+ return shared.NewInsufficientParamsError(len(obj), 1)
+ }
+
+ argstr, ok := obj[0].(string)
+ if !ok {
+ return shared.NewInvalidTypeError("data", "is not a string")
+ }
+ args.Data = argstr
+ return nil
+}
diff --git a/rpc/api_test.go b/rpc/api_test.go
deleted file mode 100644
index c3546e98f..000000000
--- a/rpc/api_test.go
+++ /dev/null
@@ -1,205 +0,0 @@
-package rpc
-
-import (
- "encoding/json"
- "strconv"
- "testing"
-
- "github.com/ethereum/go-ethereum/common/compiler"
- "github.com/ethereum/go-ethereum/eth"
- "github.com/ethereum/go-ethereum/xeth"
-)
-
-func TestWeb3Sha3(t *testing.T) {
- jsonstr := `{"jsonrpc":"2.0","method":"web3_sha3","params":["0x68656c6c6f20776f726c64"],"id":64}`
- expected := "0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad"
-
- api := &EthereumApi{}
-
- var req RpcRequest
- json.Unmarshal([]byte(jsonstr), &req)
-
- var response interface{}
- _ = api.GetRequestReply(&req, &response)
-
- if response.(string) != expected {
- t.Errorf("Expected %s got %s", expected, response)
- }
-}
-
-const solcVersion = "0.9.23"
-
-func TestCompileSolidity(t *testing.T) {
-
- solc, err := compiler.New("")
- if solc == nil {
- t.Skip("no solc found: skip")
- } else if solc.Version() != solcVersion {
- t.Skip("WARNING: skipping test because of solc different version (%v, test written for %v, may need to update)", solc.Version(), solcVersion)
- }
- source := `contract test {\n` +
- " /// @notice Will multiply `a` by 7." + `\n` +
- ` function multiply(uint a) returns(uint d) {\n` +
- ` return a * 7;\n` +
- ` }\n` +
- `}\n`
-
- jsonstr := `{"jsonrpc":"2.0","method":"eth_compileSolidity","params":["` + source + `"],"id":64}`
-
- expCode := "0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056"
- expAbiDefinition := `[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]`
- expUserDoc := `{"methods":{"multiply(uint256)":{"notice":"Will multiply ` + "`a`" + ` by 7."}}}`
- expDeveloperDoc := `{"methods":{}}`
- expCompilerVersion := solc.Version()
- expLanguage := "Solidity"
- expLanguageVersion := "0"
- expSource := source
-
- api := NewEthereumApi(xeth.NewTest(&eth.Ethereum{}, nil))
-
- var req RpcRequest
- json.Unmarshal([]byte(jsonstr), &req)
-
- var response interface{}
- err = api.GetRequestReply(&req, &response)
- if err != nil {
- t.Errorf("expected no error, got %v", err)
- }
- respjson, err := json.Marshal(response)
- if err != nil {
- t.Errorf("expected no error, got %v", err)
- }
-
- var contracts = make(map[string]*compiler.Contract)
- err = json.Unmarshal(respjson, &contracts)
- if err != nil {
- t.Errorf("expected no error, got %v", err)
- }
-
- if len(contracts) != 1 {
- t.Errorf("expected one contract, got %v", len(contracts))
- }
-
- contract := contracts["test"]
-
- if contract.Code != expCode {
- t.Errorf("Expected \n%s got \n%s", expCode, contract.Code)
- }
-
- if strconv.Quote(contract.Info.Source) != `"`+expSource+`"` {
- t.Errorf("Expected \n'%s' got \n'%s'", expSource, strconv.Quote(contract.Info.Source))
- }
-
- if contract.Info.Language != expLanguage {
- t.Errorf("Expected %s got %s", expLanguage, contract.Info.Language)
- }
-
- if contract.Info.LanguageVersion != expLanguageVersion {
- t.Errorf("Expected %s got %s", expLanguageVersion, contract.Info.LanguageVersion)
- }
-
- if contract.Info.CompilerVersion != expCompilerVersion {
- t.Errorf("Expected %s got %s", expCompilerVersion, contract.Info.CompilerVersion)
- }
-
- userdoc, err := json.Marshal(contract.Info.UserDoc)
- if err != nil {
- t.Errorf("expected no error, got %v", err)
- }
-
- devdoc, err := json.Marshal(contract.Info.DeveloperDoc)
- if err != nil {
- t.Errorf("expected no error, got %v", err)
- }
-
- abidef, err := json.Marshal(contract.Info.AbiDefinition)
- if err != nil {
- t.Errorf("expected no error, got %v", err)
- }
-
- if string(abidef) != expAbiDefinition {
- t.Errorf("Expected \n'%s' got \n'%s'", expAbiDefinition, string(abidef))
- }
-
- if string(userdoc) != expUserDoc {
- t.Errorf("Expected \n'%s' got \n'%s'", expUserDoc, string(userdoc))
- }
-
- if string(devdoc) != expDeveloperDoc {
- t.Errorf("Expected %s got %s", expDeveloperDoc, string(devdoc))
- }
-}
-
-// func TestDbStr(t *testing.T) {
-// jsonput := `{"jsonrpc":"2.0","method":"db_putString","params":["testDB","myKey","myString"],"id":64}`
-// jsonget := `{"jsonrpc":"2.0","method":"db_getString","params":["testDB","myKey"],"id":64}`
-// expected := "myString"
-
-// xeth := &xeth.XEth{}
-// api := NewEthereumApi(xeth)
-// var response interface{}
-
-// var req RpcRequest
-// json.Unmarshal([]byte(jsonput), &req)
-// _ = api.GetRequestReply(&req, &response)
-
-// json.Unmarshal([]byte(jsonget), &req)
-// _ = api.GetRequestReply(&req, &response)
-
-// if response.(string) != expected {
-// t.Errorf("Expected %s got %s", expected, response)
-// }
-// }
-
-// func TestDbHexStr(t *testing.T) {
-// jsonput := `{"jsonrpc":"2.0","method":"db_putHex","params":["testDB","beefKey","0xbeef"],"id":64}`
-// jsonget := `{"jsonrpc":"2.0","method":"db_getHex","params":["testDB","beefKey"],"id":64}`
-// expected := "0xbeef"
-
-// xeth := &xeth.XEth{}
-// api := NewEthereumApi(xeth)
-// defer api.db.Close()
-// var response interface{}
-
-// var req RpcRequest
-// json.Unmarshal([]byte(jsonput), &req)
-// _ = api.GetRequestReply(&req, &response)
-
-// json.Unmarshal([]byte(jsonget), &req)
-// _ = api.GetRequestReply(&req, &response)
-
-// if response.(string) != expected {
-// t.Errorf("Expected %s got %s", expected, response)
-// }
-// }
-
-// func TestFilterClose(t *testing.T) {
-// t.Skip()
-// api := &EthereumApi{
-// logs: make(map[int]*logFilter),
-// messages: make(map[int]*whisperFilter),
-// quit: make(chan struct{}),
-// }
-
-// filterTickerTime = 1
-// api.logs[0] = &logFilter{}
-// api.messages[0] = &whisperFilter{}
-// var wg sync.WaitGroup
-// wg.Add(1)
-// go api.start()
-// go func() {
-// select {
-// case <-time.After(500 * time.Millisecond):
-// api.stop()
-// wg.Done()
-// }
-// }()
-// wg.Wait()
-// if len(api.logs) != 0 {
-// t.Error("expected logs to be empty")
-// }
-
-// if len(api.messages) != 0 {
-// t.Error("expected messages to be empty")
-// }
-// }
diff --git a/rpc/args.go b/rpc/args.go
deleted file mode 100644
index aa7d20549..000000000
--- a/rpc/args.go
+++ /dev/null
@@ -1,1230 +0,0 @@
-package rpc
-
-import (
- "encoding/json"
- "fmt"
- "math/big"
-
- "github.com/ethereum/go-ethereum/common"
-)
-
-const (
- defaultLogLimit = 100
- defaultLogOffset = 0
-)
-
-func blockHeightFromJson(msg json.RawMessage, number *int64) error {
- var raw interface{}
- if err := json.Unmarshal(msg, &raw); err != nil {
- return NewDecodeParamError(err.Error())
- }
- return blockHeight(raw, number)
-}
-
-func blockHeight(raw interface{}, number *int64) error {
- // Parse as integer
- num, ok := raw.(float64)
- if ok {
- *number = int64(num)
- return nil
- }
-
- // Parse as string/hexstring
- str, ok := raw.(string)
- if !ok {
- return NewInvalidTypeError("", "not a number or string")
- }
-
- switch str {
- case "earliest":
- *number = 0
- case "latest":
- *number = -1
- case "pending":
- *number = -2
- default:
- if common.HasHexPrefix(str) {
- *number = common.String2Big(str).Int64()
- } else {
- return NewInvalidTypeError("blockNumber", "is not a valid string")
- }
- }
-
- return nil
-}
-
-func numString(raw interface{}) (*big.Int, error) {
- var number *big.Int
- // Parse as integer
- num, ok := raw.(float64)
- if ok {
- number = big.NewInt(int64(num))
- return number, nil
- }
-
- // Parse as string/hexstring
- str, ok := raw.(string)
- if ok {
- number = common.String2Big(str)
- return number, nil
- }
-
- return nil, NewInvalidTypeError("", "not a number or string")
-}
-
-// func toNumber(v interface{}) (int64, error) {
-// var str string
-// if v != nil {
-// var ok bool
-// str, ok = v.(string)
-// if !ok {
-// return 0, errors.New("is not a string or undefined")
-// }
-// } else {
-// str = "latest"
-// }
-
-// switch str {
-// case "latest":
-// return -1, nil
-// default:
-// return int64(common.Big(v.(string)).Int64()), nil
-// }
-// }
-
-// func hashString(raw interface{}, hash *string) error {
-// argstr, ok := raw.(string)
-// if !ok {
-// return NewInvalidTypeError("", "not a string")
-// }
-// v := common.IsHex(argstr)
-// hash = &argstr
-
-// return nil
-// }
-
-type GetBlockByHashArgs struct {
- BlockHash string
- IncludeTxs bool
-}
-
-func (args *GetBlockByHashArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
-
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 2 {
- return NewInsufficientParamsError(len(obj), 2)
- }
-
- argstr, ok := obj[0].(string)
- if !ok {
- return NewInvalidTypeError("blockHash", "not a string")
- }
- args.BlockHash = argstr
-
- args.IncludeTxs = obj[1].(bool)
-
- return nil
-}
-
-type GetBlockByNumberArgs struct {
- BlockNumber int64
- IncludeTxs bool
-}
-
-func (args *GetBlockByNumberArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 2 {
- return NewInsufficientParamsError(len(obj), 2)
- }
-
- if err := blockHeight(obj[0], &args.BlockNumber); err != nil {
- return err
- }
-
- args.IncludeTxs = obj[1].(bool)
-
- return nil
-}
-
-type NewDataArgs struct {
- Data string
-}
-
-func (args *NewDataArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
-
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- // Check for sufficient params
- if len(obj) < 1 {
- return NewInsufficientParamsError(len(obj), 1)
- }
-
- data, ok := obj[0].(string)
- if !ok {
- return NewInvalidTypeError("data", "not a string")
- }
- args.Data = data
-
- if len(args.Data) == 0 {
- return NewValidationError("data", "is required")
- }
-
- return nil
-}
-
-type NewTxArgs struct {
- From string
- To string
- Nonce *big.Int
- Value *big.Int
- Gas *big.Int
- GasPrice *big.Int
- Data string
-
- BlockNumber int64
-}
-
-type NewSigArgs struct {
- From string
- Data string
-}
-
-func (args *NewSigArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
-
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- // Check for sufficient params
- if len(obj) < 1 {
- return NewInsufficientParamsError(len(obj), 1)
- }
-
- from, ok := obj[0].(string)
- if !ok {
- return NewInvalidTypeError("from", "not a string")
- }
- args.From = from
-
- if len(args.From) == 0 {
- return NewValidationError("from", "is required")
- }
-
- data, ok := obj[1].(string)
- if !ok {
- return NewInvalidTypeError("data", "not a string")
- }
- args.Data = data
-
- if len(args.Data) == 0 {
- return NewValidationError("data", "is required")
- }
-
- return nil
-}
-
-func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []json.RawMessage
- var ext struct {
- From string
- To string
- Nonce interface{}
- Value interface{}
- Gas interface{}
- GasPrice interface{}
- Data string
- }
-
- // Decode byte slice to array of RawMessages
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- // Check for sufficient params
- if len(obj) < 1 {
- return NewInsufficientParamsError(len(obj), 1)
- }
-
- // Decode 0th RawMessage to temporary struct
- if err := json.Unmarshal(obj[0], &ext); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(ext.From) == 0 {
- return NewValidationError("from", "is required")
- }
-
- args.From = ext.From
- args.To = ext.To
- args.Data = ext.Data
-
- var num *big.Int
- if ext.Nonce != nil {
- num, err = numString(ext.Nonce)
- if err != nil {
- return err
- }
- }
- args.Nonce = num
-
- if ext.Value == nil {
- num = big.NewInt(0)
- } else {
- num, err = numString(ext.Value)
- if err != nil {
- return err
- }
- }
- args.Value = num
-
- num = nil
- if ext.Gas != nil {
- if num, err = numString(ext.Gas); err != nil {
- return err
- }
- } else {
- num = nil
- }
- args.Gas = num
-
- num = nil
- if ext.GasPrice != nil {
- if num, err = numString(ext.GasPrice); err != nil {
- return err
- }
- } else {
- num = nil
- }
- args.GasPrice = num
-
- // Check for optional BlockNumber param
- if len(obj) > 1 {
- if err := blockHeightFromJson(obj[1], &args.BlockNumber); err != nil {
- return err
- }
- } else {
- args.BlockNumber = -1
- }
-
- return nil
-}
-
-type CallArgs struct {
- From string
- To string
- Value *big.Int
- Gas *big.Int
- GasPrice *big.Int
- Data string
-
- BlockNumber int64
-}
-
-func (args *CallArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []json.RawMessage
- var ext struct {
- From string
- To string
- Value interface{}
- Gas interface{}
- GasPrice interface{}
- Data string
- }
-
- // Decode byte slice to array of RawMessages
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- // Check for sufficient params
- if len(obj) < 1 {
- return NewInsufficientParamsError(len(obj), 1)
- }
-
- // Decode 0th RawMessage to temporary struct
- if err := json.Unmarshal(obj[0], &ext); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- args.From = ext.From
-
- if len(ext.To) == 0 {
- return NewValidationError("to", "is required")
- }
- args.To = ext.To
-
- var num *big.Int
- if ext.Value == nil {
- num = big.NewInt(0)
- } else {
- if num, err = numString(ext.Value); err != nil {
- return err
- }
- }
- args.Value = num
-
- if ext.Gas != nil {
- if num, err = numString(ext.Gas); err != nil {
- return err
- }
- } else {
- num = nil
- }
- args.Gas = num
-
- if ext.GasPrice != nil {
- if num, err = numString(ext.GasPrice); err != nil {
- return err
- }
- } else {
- num = nil
- }
- args.GasPrice = num
-
- args.Data = ext.Data
-
- // Check for optional BlockNumber param
- if len(obj) > 1 {
- if err := blockHeightFromJson(obj[1], &args.BlockNumber); err != nil {
- return err
- }
- } else {
- args.BlockNumber = -1
- }
-
- return nil
-}
-
-type GetStorageArgs struct {
- Address string
- BlockNumber int64
-}
-
-func (args *GetStorageArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 1 {
- return NewInsufficientParamsError(len(obj), 1)
- }
-
- addstr, ok := obj[0].(string)
- if !ok {
- return NewInvalidTypeError("address", "not a string")
- }
- args.Address = addstr
-
- if len(obj) > 1 {
- if err := blockHeight(obj[1], &args.BlockNumber); err != nil {
- return err
- }
- } else {
- args.BlockNumber = -1
- }
-
- return nil
-}
-
-type GetStorageAtArgs struct {
- Address string
- Key string
- BlockNumber int64
-}
-
-func (args *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 2 {
- return NewInsufficientParamsError(len(obj), 2)
- }
-
- addstr, ok := obj[0].(string)
- if !ok {
- return NewInvalidTypeError("address", "not a string")
- }
- args.Address = addstr
-
- keystr, ok := obj[1].(string)
- if !ok {
- return NewInvalidTypeError("key", "not a string")
- }
- args.Key = keystr
-
- if len(obj) > 2 {
- if err := blockHeight(obj[2], &args.BlockNumber); err != nil {
- return err
- }
- } else {
- args.BlockNumber = -1
- }
-
- return nil
-}
-
-type GetTxCountArgs struct {
- Address string
- BlockNumber int64
-}
-
-func (args *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 1 {
- return NewInsufficientParamsError(len(obj), 1)
- }
-
- addstr, ok := obj[0].(string)
- if !ok {
- return NewInvalidTypeError("address", "not a string")
- }
- args.Address = addstr
-
- if len(obj) > 1 {
- if err := blockHeight(obj[1], &args.BlockNumber); err != nil {
- return err
- }
- } else {
- args.BlockNumber = -1
- }
-
- return nil
-}
-
-type GetBalanceArgs struct {
- Address string
- BlockNumber int64
-}
-
-func (args *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 1 {
- return NewInsufficientParamsError(len(obj), 1)
- }
-
- addstr, ok := obj[0].(string)
- if !ok {
- return NewInvalidTypeError("address", "not a string")
- }
- args.Address = addstr
-
- if len(obj) > 1 {
- if err := blockHeight(obj[1], &args.BlockNumber); err != nil {
- return err
- }
- } else {
- args.BlockNumber = -1
- }
-
- return nil
-}
-
-type GetDataArgs struct {
- Address string
- BlockNumber int64
-}
-
-func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 1 {
- return NewInsufficientParamsError(len(obj), 1)
- }
-
- addstr, ok := obj[0].(string)
- if !ok {
- return NewInvalidTypeError("address", "not a string")
- }
- args.Address = addstr
-
- if len(obj) > 1 {
- if err := blockHeight(obj[1], &args.BlockNumber); err != nil {
- return err
- }
- } else {
- args.BlockNumber = -1
- }
-
- return nil
-}
-
-type BlockNumArg struct {
- BlockNumber int64
-}
-
-func (args *BlockNumArg) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 1 {
- return NewInsufficientParamsError(len(obj), 1)
- }
-
- if err := blockHeight(obj[0], &args.BlockNumber); err != nil {
- return err
- }
-
- return nil
-}
-
-type BlockNumIndexArgs struct {
- BlockNumber int64
- Index int64
-}
-
-func (args *BlockNumIndexArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 2 {
- return NewInsufficientParamsError(len(obj), 2)
- }
-
- if err := blockHeight(obj[0], &args.BlockNumber); err != nil {
- return err
- }
-
- var arg1 *big.Int
- if arg1, err = numString(obj[1]); err != nil {
- return err
- }
- args.Index = arg1.Int64()
-
- return nil
-}
-
-type HashArgs struct {
- Hash string
-}
-
-func (args *HashArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 1 {
- return NewInsufficientParamsError(len(obj), 1)
- }
-
- arg0, ok := obj[0].(string)
- if !ok {
- return NewInvalidTypeError("hash", "not a string")
- }
- args.Hash = arg0
-
- return nil
-}
-
-type HashIndexArgs struct {
- Hash string
- Index int64
-}
-
-func (args *HashIndexArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 2 {
- return NewInsufficientParamsError(len(obj), 2)
- }
-
- arg0, ok := obj[0].(string)
- if !ok {
- return NewInvalidTypeError("hash", "not a string")
- }
- args.Hash = arg0
-
- arg1, ok := obj[1].(string)
- if !ok {
- return NewInvalidTypeError("index", "not a string")
- }
- args.Index = common.Big(arg1).Int64()
-
- return nil
-}
-
-type Sha3Args struct {
- Data string
-}
-
-func (args *Sha3Args) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 1 {
- return NewInsufficientParamsError(len(obj), 1)
- }
-
- argstr, ok := obj[0].(string)
- if !ok {
- return NewInvalidTypeError("data", "is not a string")
- }
- args.Data = argstr
- return nil
-}
-
-type BlockFilterArgs struct {
- Earliest int64
- Latest int64
- Address []string
- Topics [][]string
- Skip int
- Max int
-}
-
-func (args *BlockFilterArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []struct {
- FromBlock interface{} `json:"fromBlock"`
- ToBlock interface{} `json:"toBlock"`
- Limit interface{} `json:"limit"`
- Offset interface{} `json:"offset"`
- Address interface{} `json:"address"`
- Topics interface{} `json:"topics"`
- }
-
- if err = json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 1 {
- return NewInsufficientParamsError(len(obj), 1)
- }
-
- // args.Earliest, err = toNumber(obj[0].ToBlock)
- // if err != nil {
- // return NewDecodeParamError(fmt.Sprintf("FromBlock %v", err))
- // }
- // args.Latest, err = toNumber(obj[0].FromBlock)
- // if err != nil {
- // return NewDecodeParamError(fmt.Sprintf("ToBlock %v", err))
-
- var num int64
- var numBig *big.Int
-
- // if blank then latest
- if obj[0].FromBlock == nil {
- num = -1
- } else {
- if err := blockHeight(obj[0].FromBlock, &num); err != nil {
- return err
- }
- }
- // if -2 or other "silly" number, use latest
- if num < 0 {
- args.Earliest = -1 //latest block
- } else {
- args.Earliest = num
- }
-
- // if blank than latest
- if obj[0].ToBlock == nil {
- num = -1
- } else {
- if err := blockHeight(obj[0].ToBlock, &num); err != nil {
- return err
- }
- }
- args.Latest = num
-
- if obj[0].Limit == nil {
- numBig = big.NewInt(defaultLogLimit)
- } else {
- if numBig, err = numString(obj[0].Limit); err != nil {
- return err
- }
- }
- args.Max = int(numBig.Int64())
-
- if obj[0].Offset == nil {
- numBig = big.NewInt(defaultLogOffset)
- } else {
- if numBig, err = numString(obj[0].Offset); err != nil {
- return err
- }
- }
- args.Skip = int(numBig.Int64())
-
- if obj[0].Address != nil {
- marg, ok := obj[0].Address.([]interface{})
- if ok {
- v := make([]string, len(marg))
- for i, arg := range marg {
- argstr, ok := arg.(string)
- if !ok {
- return NewInvalidTypeError(fmt.Sprintf("address[%d]", i), "is not a string")
- }
- v[i] = argstr
- }
- args.Address = v
- } else {
- argstr, ok := obj[0].Address.(string)
- if ok {
- v := make([]string, 1)
- v[0] = argstr
- args.Address = v
- } else {
- return NewInvalidTypeError("address", "is not a string or array")
- }
- }
- }
-
- if obj[0].Topics != nil {
- other, ok := obj[0].Topics.([]interface{})
- if ok {
- topicdbl := make([][]string, len(other))
- for i, iv := range other {
- if argstr, ok := iv.(string); ok {
- // Found a string, push into first element of array
- topicsgl := make([]string, 1)
- topicsgl[0] = argstr
- topicdbl[i] = topicsgl
- } else if argarray, ok := iv.([]interface{}); ok {
- // Found an array of other
- topicdbl[i] = make([]string, len(argarray))
- for j, jv := range argarray {
- if v, ok := jv.(string); ok {
- topicdbl[i][j] = v
- } else if jv == nil {
- topicdbl[i][j] = ""
- } else {
- return NewInvalidTypeError(fmt.Sprintf("topic[%d][%d]", i, j), "is not a string")
- }
- }
- } else if iv == nil {
- topicdbl[i] = []string{""}
- } else {
- return NewInvalidTypeError(fmt.Sprintf("topic[%d]", i), "not a string or array")
- }
- }
- args.Topics = topicdbl
- return nil
- } else {
- return NewInvalidTypeError("topic", "is not a string or array")
- }
- }
-
- return nil
-}
-
-type DbArgs struct {
- Database string
- Key string
- Value []byte
-}
-
-func (args *DbArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 2 {
- return NewInsufficientParamsError(len(obj), 2)
- }
-
- var objstr string
- var ok bool
-
- if objstr, ok = obj[0].(string); !ok {
- return NewInvalidTypeError("database", "not a string")
- }
- args.Database = objstr
-
- if objstr, ok = obj[1].(string); !ok {
- return NewInvalidTypeError("key", "not a string")
- }
- args.Key = objstr
-
- if len(obj) > 2 {
- objstr, ok = obj[2].(string)
- if !ok {
- return NewInvalidTypeError("value", "not a string")
- }
-
- args.Value = []byte(objstr)
- }
-
- return nil
-}
-
-func (a *DbArgs) requirements() error {
- if len(a.Database) == 0 {
- return NewValidationError("Database", "cannot be blank")
- }
- if len(a.Key) == 0 {
- return NewValidationError("Key", "cannot be blank")
- }
- return nil
-}
-
-type DbHexArgs struct {
- Database string
- Key string
- Value []byte
-}
-
-func (args *DbHexArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 2 {
- return NewInsufficientParamsError(len(obj), 2)
- }
-
- var objstr string
- var ok bool
-
- if objstr, ok = obj[0].(string); !ok {
- return NewInvalidTypeError("database", "not a string")
- }
- args.Database = objstr
-
- if objstr, ok = obj[1].(string); !ok {
- return NewInvalidTypeError("key", "not a string")
- }
- args.Key = objstr
-
- if len(obj) > 2 {
- objstr, ok = obj[2].(string)
- if !ok {
- return NewInvalidTypeError("value", "not a string")
- }
-
- args.Value = common.FromHex(objstr)
- }
-
- return nil
-}
-
-func (a *DbHexArgs) requirements() error {
- if len(a.Database) == 0 {
- return NewValidationError("Database", "cannot be blank")
- }
- if len(a.Key) == 0 {
- return NewValidationError("Key", "cannot be blank")
- }
- return nil
-}
-
-type WhisperMessageArgs struct {
- Payload string
- To string
- From string
- Topics []string
- Priority uint32
- Ttl uint32
-}
-
-func (args *WhisperMessageArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []struct {
- Payload string
- To string
- From string
- Topics []string
- Priority interface{}
- Ttl interface{}
- }
-
- if err = json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 1 {
- return NewInsufficientParamsError(len(obj), 1)
- }
- args.Payload = obj[0].Payload
- args.To = obj[0].To
- args.From = obj[0].From
- args.Topics = obj[0].Topics
-
- var num *big.Int
- if num, err = numString(obj[0].Priority); err != nil {
- return err
- }
- args.Priority = uint32(num.Int64())
-
- if num, err = numString(obj[0].Ttl); err != nil {
- return err
- }
- args.Ttl = uint32(num.Int64())
-
- return nil
-}
-
-type CompileArgs struct {
- Source string
-}
-
-func (args *CompileArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 1 {
- return NewInsufficientParamsError(len(obj), 1)
- }
- argstr, ok := obj[0].(string)
- if !ok {
- return NewInvalidTypeError("arg0", "is not a string")
- }
- args.Source = argstr
-
- return nil
-}
-
-type FilterStringArgs struct {
- Word string
-}
-
-func (args *FilterStringArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 1 {
- return NewInsufficientParamsError(len(obj), 1)
- }
-
- var argstr string
- argstr, ok := obj[0].(string)
- if !ok {
- return NewInvalidTypeError("filter", "not a string")
- }
- switch argstr {
- case "latest", "pending":
- break
- default:
- return NewValidationError("Word", "Must be `latest` or `pending`")
- }
- args.Word = argstr
- return nil
-}
-
-type FilterIdArgs struct {
- Id int
-}
-
-func (args *FilterIdArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 1 {
- return NewInsufficientParamsError(len(obj), 1)
- }
-
- var num *big.Int
- if num, err = numString(obj[0]); err != nil {
- return err
- }
- args.Id = int(num.Int64())
-
- return nil
-}
-
-type WhisperIdentityArgs struct {
- Identity string
-}
-
-func (args *WhisperIdentityArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 1 {
- return NewInsufficientParamsError(len(obj), 1)
- }
-
- argstr, ok := obj[0].(string)
- if !ok {
- return NewInvalidTypeError("arg0", "not a string")
- }
- // if !common.IsHex(argstr) {
- // return NewValidationError("arg0", "not a hexstring")
- // }
- args.Identity = argstr
-
- return nil
-}
-
-type WhisperFilterArgs struct {
- To string
- From string
- Topics [][]string
-}
-
-// UnmarshalJSON implements the json.Unmarshaler interface, invoked to convert a
-// JSON message blob into a WhisperFilterArgs structure.
-func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) {
- // Unmarshal the JSON message and sanity check
- var obj []struct {
- To interface{} `json:"to"`
- From interface{} `json:"from"`
- Topics interface{} `json:"topics"`
- }
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
- if len(obj) < 1 {
- return NewInsufficientParamsError(len(obj), 1)
- }
- // Retrieve the simple data contents of the filter arguments
- if obj[0].To == nil {
- args.To = ""
- } else {
- argstr, ok := obj[0].To.(string)
- if !ok {
- return NewInvalidTypeError("to", "is not a string")
- }
- args.To = argstr
- }
- if obj[0].From == nil {
- args.From = ""
- } else {
- argstr, ok := obj[0].From.(string)
- if !ok {
- return NewInvalidTypeError("from", "is not a string")
- }
- args.From = argstr
- }
- // Construct the nested topic array
- if obj[0].Topics != nil {
- // Make sure we have an actual topic array
- list, ok := obj[0].Topics.([]interface{})
- if !ok {
- return NewInvalidTypeError("topics", "is not an array")
- }
- // Iterate over each topic and handle nil, string or array
- topics := make([][]string, len(list))
- for idx, field := range list {
- switch value := field.(type) {
- case nil:
- topics[idx] = []string{}
-
- case string:
- topics[idx] = []string{value}
-
- case []interface{}:
- topics[idx] = make([]string, len(value))
- for i, nested := range value {
- switch value := nested.(type) {
- case nil:
- topics[idx][i] = ""
-
- case string:
- topics[idx][i] = value
-
- default:
- return NewInvalidTypeError(fmt.Sprintf("topic[%d][%d]", idx, i), "is not a string")
- }
- }
- default:
- return NewInvalidTypeError(fmt.Sprintf("topic[%d]", idx), "not a string or array")
- }
- }
- args.Topics = topics
- }
- return nil
-}
-
-type SubmitWorkArgs struct {
- Nonce uint64
- Header string
- Digest string
-}
-
-func (args *SubmitWorkArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err = json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 3 {
- return NewInsufficientParamsError(len(obj), 3)
- }
-
- var objstr string
- var ok bool
- if objstr, ok = obj[0].(string); !ok {
- return NewInvalidTypeError("nonce", "not a string")
- }
-
- args.Nonce = common.String2Big(objstr).Uint64()
- if objstr, ok = obj[1].(string); !ok {
- return NewInvalidTypeError("header", "not a string")
- }
-
- args.Header = objstr
-
- if objstr, ok = obj[2].(string); !ok {
- return NewInvalidTypeError("digest", "not a string")
- }
-
- args.Digest = objstr
-
- return nil
-}
-
-type SourceArgs struct {
- Source string
-}
-
-func (args *SourceArgs) UnmarshalJSON(b []byte) (err error) {
- var obj []interface{}
- if err := json.Unmarshal(b, &obj); err != nil {
- return NewDecodeParamError(err.Error())
- }
-
- if len(obj) < 1 {
- return NewInsufficientParamsError(len(obj), 1)
- }
-
- arg0, ok := obj[0].(string)
- if !ok {
- return NewInvalidTypeError("source code", "not a string")
- }
- args.Source = arg0
-
- return nil
-}
diff --git a/rpc/comms/comms.go b/rpc/comms/comms.go
index 244f5a7a6..bfe625758 100644
--- a/rpc/comms/comms.go
+++ b/rpc/comms/comms.go
@@ -1,7 +1,111 @@
package comms
+import (
+ "io"
+ "net"
+
+ "fmt"
+ "strings"
+
+ "strconv"
+
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/logger/glog"
+ "github.com/ethereum/go-ethereum/rpc/codec"
+ "github.com/ethereum/go-ethereum/rpc/shared"
+)
+
+const (
+ maxHttpSizeReqLength = 1024 * 1024 // 1MB
+)
+
+var (
+ // List with all API's which are offered over the in proc interface by default
+ DefaultInProcApis = shared.AllApis
+
+ // List with all API's which are offered over the IPC interface by default
+ DefaultIpcApis = shared.AllApis
+
+ // List with API's which are offered over thr HTTP/RPC interface by default
+ DefaultHttpRpcApis = strings.Join([]string{
+ shared.DbApiName, shared.EthApiName, shared.NetApiName, shared.Web3ApiName,
+ }, ",")
+)
+
type EthereumClient interface {
+ // Close underlaying connection
Close()
+ // Send request
Send(interface{}) error
+ // Receive response
Recv() (interface{}, error)
+ // List with modules this client supports
+ SupportedModules() (map[string]string, error)
+}
+
+func handle(conn net.Conn, api shared.EthereumApi, c codec.Codec) {
+ codec := c.New(conn)
+
+ for {
+ req, err := codec.ReadRequest()
+ if err == io.EOF {
+ codec.Close()
+ return
+ } else if err != nil {
+ glog.V(logger.Error).Infof("comms recv err - %v\n", err)
+ codec.Close()
+ return
+ }
+
+ var rpcResponse interface{}
+ res, err := api.Execute(req)
+
+ rpcResponse = shared.NewRpcResponse(req.Id, req.Jsonrpc, res, err)
+ err = codec.WriteResponse(rpcResponse)
+ if err != nil {
+ glog.V(logger.Error).Infof("comms send err - %v\n", err)
+ codec.Close()
+ return
+ }
+ }
+}
+
+// Endpoint must be in the form of:
+// ${protocol}:${path}
+// e.g. ipc:/tmp/geth.ipc
+// rpc:localhost:8545
+func ClientFromEndpoint(endpoint string, c codec.Codec) (EthereumClient, error) {
+ if strings.HasPrefix(endpoint, "ipc:") {
+ cfg := IpcConfig{
+ Endpoint: endpoint[4:],
+ }
+ return NewIpcClient(cfg, codec.JSON)
+ }
+
+ if strings.HasPrefix(endpoint, "rpc:") {
+ parts := strings.Split(endpoint, ":")
+ addr := "http://localhost"
+ port := uint(8545)
+ if len(parts) >= 3 {
+ addr = parts[1] + ":" + parts[2]
+ }
+
+ if len(parts) >= 4 {
+ p, err := strconv.Atoi(parts[3])
+
+ if err != nil {
+ return nil, err
+ }
+ port = uint(p)
+ }
+
+ cfg := HttpConfig{
+ ListenAddress: addr,
+ ListenPort: port,
+ }
+
+ return NewHttpClient(cfg, codec.JSON), nil
+ }
+
+ return nil, fmt.Errorf("Invalid endpoint")
}
diff --git a/rpc/comms/http.go b/rpc/comms/http.go
new file mode 100644
index 000000000..ebee791bd
--- /dev/null
+++ b/rpc/comms/http.go
@@ -0,0 +1,190 @@
+package comms
+
+import (
+ "fmt"
+ "net/http"
+ "strings"
+
+ "bytes"
+ "io/ioutil"
+
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/logger/glog"
+ "github.com/ethereum/go-ethereum/rpc/codec"
+ "github.com/ethereum/go-ethereum/rpc/shared"
+ "github.com/rs/cors"
+)
+
+var (
+ // main HTTP rpc listener
+ httpListener *stoppableTCPListener
+ listenerStoppedError = fmt.Errorf("Listener has stopped")
+)
+
+type HttpConfig struct {
+ ListenAddress string
+ ListenPort uint
+ CorsDomain string
+}
+
+func StartHttp(cfg HttpConfig, codec codec.Codec, api shared.EthereumApi) error {
+ if httpListener != nil {
+ if fmt.Sprintf("%s:%d", cfg.ListenAddress, cfg.ListenPort) != httpListener.Addr().String() {
+ return fmt.Errorf("RPC service already running on %s ", httpListener.Addr().String())
+ }
+ return nil // RPC service already running on given host/port
+ }
+
+ l, err := newStoppableTCPListener(fmt.Sprintf("%s:%d", cfg.ListenAddress, cfg.ListenPort))
+ if err != nil {
+ glog.V(logger.Error).Infof("Can't listen on %s:%d: %v", cfg.ListenAddress, cfg.ListenPort, err)
+ return err
+ }
+ httpListener = l
+
+ var handler http.Handler
+ if len(cfg.CorsDomain) > 0 {
+ var opts cors.Options
+ opts.AllowedMethods = []string{"POST"}
+ opts.AllowedOrigins = strings.Split(cfg.CorsDomain, " ")
+
+ c := cors.New(opts)
+ handler = newStoppableHandler(c.Handler(gethHttpHandler(codec, api)), l.stop)
+ } else {
+ handler = newStoppableHandler(gethHttpHandler(codec, api), l.stop)
+ }
+
+ go http.Serve(l, handler)
+
+ return nil
+}
+
+func StopHttp() {
+ if httpListener != nil {
+ httpListener.Stop()
+ httpListener = nil
+ }
+}
+
+type httpClient struct {
+ address string
+ port uint
+ codec codec.ApiCoder
+ lastRes interface{}
+ lastErr error
+}
+
+// Create a new in process client
+func NewHttpClient(cfg HttpConfig, c codec.Codec) *httpClient {
+ return &httpClient{
+ address: cfg.ListenAddress,
+ port: cfg.ListenPort,
+ codec: c.New(nil),
+ }
+}
+
+func (self *httpClient) Close() {
+ // do nothing
+}
+
+func (self *httpClient) Send(req interface{}) error {
+ var body []byte
+ var err error
+
+ self.lastRes = nil
+ self.lastErr = nil
+
+ if body, err = self.codec.Encode(req); err != nil {
+ return err
+ }
+
+ httpReq, err := http.NewRequest("POST", fmt.Sprintf("%s:%d", self.address, self.port), bytes.NewBuffer(body))
+ if err != nil {
+ return err
+ }
+ httpReq.Header.Set("Content-Type", "application/json")
+
+ client := http.Client{}
+ resp, err := client.Do(httpReq)
+ if err != nil {
+ return err
+ }
+
+ defer resp.Body.Close()
+
+ if resp.Status == "200 OK" {
+ reply, _ := ioutil.ReadAll(resp.Body)
+ var rpcSuccessResponse shared.SuccessResponse
+ if err = self.codec.Decode(reply, &rpcSuccessResponse); err == nil {
+ self.lastRes = rpcSuccessResponse.Result
+ self.lastErr = err
+ return nil
+ } else {
+ var rpcErrorResponse shared.ErrorResponse
+ if err = self.codec.Decode(reply, &rpcErrorResponse); err == nil {
+ self.lastRes = rpcErrorResponse.Error
+ self.lastErr = err
+ return nil
+ } else {
+ return err
+ }
+ }
+ }
+
+ return fmt.Errorf("Not implemented")
+}
+
+func (self *httpClient) Recv() (interface{}, error) {
+ return self.lastRes, self.lastErr
+}
+
+func (self *httpClient) SupportedModules() (map[string]string, error) {
+ var body []byte
+ var err error
+
+ payload := shared.Request{
+ Id: 1,
+ Jsonrpc: "2.0",
+ Method: "modules",
+ }
+
+ if body, err = self.codec.Encode(payload); err != nil {
+ return nil, err
+ }
+
+ req, err := http.NewRequest("POST", fmt.Sprintf("%s:%d", self.address, self.port), bytes.NewBuffer(body))
+ if err != nil {
+ return nil, err
+ }
+ req.Header.Set("Content-Type", "application/json")
+
+ client := http.Client{}
+ resp, err := client.Do(req)
+ if err != nil {
+ return nil, err
+ }
+
+ defer resp.Body.Close()
+
+ if resp.Status == "200 OK" {
+ reply, _ := ioutil.ReadAll(resp.Body)
+ var rpcRes shared.SuccessResponse
+ if err = self.codec.Decode(reply, &rpcRes); err != nil {
+ return nil, err
+ }
+
+ result := make(map[string]string)
+ if modules, ok := rpcRes.Result.(map[string]interface{}); ok {
+ for a, v := range modules {
+ result[a] = fmt.Sprintf("%s", v)
+ }
+ return result, nil
+ }
+ err = fmt.Errorf("Unable to parse module response - %v", rpcRes.Result)
+ } else {
+ fmt.Printf("resp.Status = %s\n", resp.Status)
+ fmt.Printf("err = %v\n", err)
+ }
+
+ return nil, err
+}
diff --git a/rpc/comms/http_net.go b/rpc/comms/http_net.go
new file mode 100644
index 000000000..acc5f99a9
--- /dev/null
+++ b/rpc/comms/http_net.go
@@ -0,0 +1,166 @@
+package comms
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "time"
+
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/logger/glog"
+ "github.com/ethereum/go-ethereum/rpc/codec"
+ "github.com/ethereum/go-ethereum/rpc/shared"
+)
+
+// When https://github.com/golang/go/issues/4674 is implemented this could be replaced
+type stoppableTCPListener struct {
+ *net.TCPListener
+ stop chan struct{} // closed when the listener must stop
+}
+
+func newStoppableTCPListener(addr string) (*stoppableTCPListener, error) {
+ wl, err := net.Listen("tcp", addr)
+ if err != nil {
+ return nil, err
+ }
+
+ if tcpl, ok := wl.(*net.TCPListener); ok {
+ stop := make(chan struct{})
+ return &stoppableTCPListener{tcpl, stop}, nil
+ }
+
+ return nil, fmt.Errorf("Unable to create TCP listener for RPC service")
+}
+
+// Stop the listener and all accepted and still active connections.
+func (self *stoppableTCPListener) Stop() {
+ close(self.stop)
+}
+
+func (self *stoppableTCPListener) Accept() (net.Conn, error) {
+ for {
+ self.SetDeadline(time.Now().Add(time.Duration(1 * time.Second)))
+ c, err := self.TCPListener.AcceptTCP()
+
+ select {
+ case <-self.stop:
+ if c != nil { // accept timeout
+ c.Close()
+ }
+ self.TCPListener.Close()
+ return nil, listenerStoppedError
+ default:
+ }
+
+ if err != nil {
+ if netErr, ok := err.(net.Error); ok && netErr.Timeout() && netErr.Temporary() {
+ continue // regular timeout
+ }
+ }
+
+ return &closableConnection{c, self.stop}, err
+ }
+}
+
+type closableConnection struct {
+ *net.TCPConn
+ closed chan struct{}
+}
+
+func (self *closableConnection) Read(b []byte) (n int, err error) {
+ select {
+ case <-self.closed:
+ self.TCPConn.Close()
+ return 0, io.EOF
+ default:
+ return self.TCPConn.Read(b)
+ }
+}
+
+// Wraps the default handler and checks if the RPC service was stopped. In that case it returns an
+// error indicating that the service was stopped. This will only happen for connections which are
+// kept open (HTTP keep-alive) when the RPC service was shutdown.
+func newStoppableHandler(h http.Handler, stop chan struct{}) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ select {
+ case <-stop:
+ w.Header().Set("Content-Type", "application/json")
+ err := fmt.Errorf("RPC service stopped")
+ response := shared.NewRpcResponse(-1, shared.JsonRpcVersion, nil, err)
+ httpSend(w, response)
+ default:
+ h.ServeHTTP(w, r)
+ }
+ })
+}
+
+func httpSend(writer io.Writer, v interface{}) (n int, err error) {
+ var payload []byte
+ payload, err = json.MarshalIndent(v, "", "\t")
+ if err != nil {
+ glog.V(logger.Error).Infoln("Error marshalling JSON", err)
+ return 0, err
+ }
+ glog.V(logger.Detail).Infof("Sending payload: %s", payload)
+
+ return writer.Write(payload)
+}
+
+func gethHttpHandler(codec codec.Codec, a shared.EthereumApi) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+
+ // Limit request size to resist DoS
+ if req.ContentLength > maxHttpSizeReqLength {
+ err := fmt.Errorf("Request too large")
+ response := shared.NewRpcErrorResponse(-1, shared.JsonRpcVersion, -32700, err)
+ httpSend(w, &response)
+ return
+ }
+
+ defer req.Body.Close()
+ payload, err := ioutil.ReadAll(req.Body)
+ if err != nil {
+ err := fmt.Errorf("Could not read request body")
+ response := shared.NewRpcErrorResponse(-1, shared.JsonRpcVersion, -32700, err)
+ httpSend(w, &response)
+ return
+ }
+
+ c := codec.New(nil)
+ var rpcReq shared.Request
+ if err = c.Decode(payload, &rpcReq); err == nil {
+ reply, err := a.Execute(&rpcReq)
+ res := shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err)
+ httpSend(w, &res)
+ return
+ }
+
+ var reqBatch []shared.Request
+ if err = c.Decode(payload, &reqBatch); err == nil {
+ resBatch := make([]*interface{}, len(reqBatch))
+ resCount := 0
+
+ for i, rpcReq := range reqBatch {
+ reply, err := a.Execute(&rpcReq)
+ if rpcReq.Id != nil { // this leaves nil entries in the response batch for later removal
+ resBatch[i] = shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err)
+ resCount += 1
+ }
+ }
+
+ // make response omitting nil entries
+ resBatch = resBatch[:resCount]
+ httpSend(w, resBatch)
+ return
+ }
+
+ // invalid request
+ err = fmt.Errorf("Could not decode request")
+ res := shared.NewRpcErrorResponse(-1, shared.JsonRpcVersion, -32600, err)
+ httpSend(w, res)
+ })
+}
diff --git a/rpc/comms/inproc.go b/rpc/comms/inproc.go
new file mode 100644
index 000000000..5c84b8fd8
--- /dev/null
+++ b/rpc/comms/inproc.go
@@ -0,0 +1,66 @@
+package comms
+
+import (
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/rpc/codec"
+ "github.com/ethereum/go-ethereum/rpc/shared"
+)
+
+type InProcClient struct {
+ api shared.EthereumApi
+ codec codec.Codec
+ lastId interface{}
+ lastJsonrpc string
+ lastErr error
+ lastRes interface{}
+}
+
+// Create a new in process client
+func NewInProcClient(codec codec.Codec) *InProcClient {
+ return &InProcClient{
+ codec: codec,
+ }
+}
+
+func (self *InProcClient) Close() {
+ // do nothing
+}
+
+// Need to setup api support
+func (self *InProcClient) Initialize(offeredApi shared.EthereumApi) {
+ self.api = offeredApi
+}
+
+func (self *InProcClient) Send(req interface{}) error {
+ if r, ok := req.(*shared.Request); ok {
+ self.lastId = r.Id
+ self.lastJsonrpc = r.Jsonrpc
+ self.lastRes, self.lastErr = self.api.Execute(r)
+ return self.lastErr
+ }
+
+ return fmt.Errorf("Invalid request (%T)", req)
+}
+
+func (self *InProcClient) Recv() (interface{}, error) {
+ return self.lastRes, self.lastErr
+}
+
+func (self *InProcClient) SupportedModules() (map[string]string, error) {
+ req := shared.Request{
+ Id: 1,
+ Jsonrpc: "2.0",
+ Method: "modules",
+ }
+
+ if res, err := self.api.Execute(&req); err == nil {
+ if result, ok := res.(map[string]string); ok {
+ return result, nil
+ }
+ } else {
+ return nil, err
+ }
+
+ return nil, fmt.Errorf("Invalid response")
+}
diff --git a/rpc/comms/ipc.go b/rpc/comms/ipc.go
index a75039d17..068a1288f 100644
--- a/rpc/comms/ipc.go
+++ b/rpc/comms/ipc.go
@@ -1,8 +1,13 @@
package comms
import (
- "github.com/ethereum/go-ethereum/rpc/api"
+ "fmt"
+ "net"
+
+ "encoding/json"
+
"github.com/ethereum/go-ethereum/rpc/codec"
+ "github.com/ethereum/go-ethereum/rpc/shared"
)
type IpcConfig struct {
@@ -10,19 +15,74 @@ type IpcConfig struct {
}
type ipcClient struct {
- c codec.ApiCoder
+ endpoint string
+ codec codec.Codec
+ coder codec.ApiCoder
}
func (self *ipcClient) Close() {
- self.c.Close()
+ self.coder.Close()
}
func (self *ipcClient) Send(req interface{}) error {
- return self.c.WriteResponse(req)
+ var err error
+ if r, ok := req.(*shared.Request); ok {
+ if err = self.coder.WriteResponse(r); err != nil {
+ if _, ok := err.(*net.OpError); ok { // connection lost, retry once
+ if err = self.reconnect(); err == nil {
+ err = self.coder.WriteResponse(r)
+ }
+ }
+ }
+ return err
+ }
+
+ return fmt.Errorf("Invalid request (%T)", req)
}
func (self *ipcClient) Recv() (interface{}, error) {
- return self.c.ReadResponse()
+ res, err := self.coder.ReadResponse()
+ if err != nil {
+ return nil, err
+ }
+
+ if r, ok := res.(shared.SuccessResponse); ok {
+ return r.Result, nil
+ }
+
+ if r, ok := res.(shared.ErrorResponse); ok {
+ return r.Error, nil
+ }
+
+ return res, err
+}
+
+func (self *ipcClient) SupportedModules() (map[string]string, error) {
+ req := shared.Request{
+ Id: 1,
+ Jsonrpc: "2.0",
+ Method: "modules",
+ }
+
+ if err := self.coder.WriteResponse(req); err != nil {
+ return nil, err
+ }
+
+ res, err := self.coder.ReadResponse()
+ if err != nil {
+ return nil, err
+ }
+
+ if sucRes, ok := res.(shared.SuccessResponse); ok {
+ data, _ := json.Marshal(sucRes.Result)
+ modules := make(map[string]string)
+ err = json.Unmarshal(data, &modules)
+ if err == nil {
+ return modules, nil
+ }
+ }
+
+ return nil, fmt.Errorf("Invalid response")
}
// Create a new IPC client, UNIX domain socket on posix, named pipe on Windows
@@ -31,7 +91,6 @@ func NewIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) {
}
// Start IPC server
-func StartIpc(cfg IpcConfig, codec codec.Codec, apis ...api.EthereumApi) error {
- offeredApi := api.Merge(apis...)
+func StartIpc(cfg IpcConfig, codec codec.Codec, offeredApi shared.EthereumApi) error {
return startIpc(cfg, codec, offeredApi)
}
diff --git a/rpc/comms/ipc_unix.go b/rpc/comms/ipc_unix.go
index 5a94fd1e0..295eb916b 100644
--- a/rpc/comms/ipc_unix.go
+++ b/rpc/comms/ipc_unix.go
@@ -3,13 +3,11 @@
package comms
import (
- "io"
"net"
"os"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
- "github.com/ethereum/go-ethereum/rpc/api"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/shared"
)
@@ -20,10 +18,20 @@ func newIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) {
return nil, err
}
- return &ipcClient{codec.New(c)}, nil
+ return &ipcClient{cfg.Endpoint, codec, codec.New(c)}, nil
}
-func startIpc(cfg IpcConfig, codec codec.Codec, api api.EthereumApi) error {
+func (self *ipcClient) reconnect() error {
+ self.coder.Close()
+ c, err := net.DialUnix("unix", nil, &net.UnixAddr{self.endpoint, "unix"})
+ if err == nil {
+ self.coder = self.codec.New(c)
+ }
+
+ return err
+}
+
+func startIpc(cfg IpcConfig, codec codec.Codec, api shared.EthereumApi) error {
os.Remove(cfg.Endpoint) // in case it still exists from a previous run
l, err := net.ListenUnix("unix", &net.UnixAddr{Name: cfg.Endpoint, Net: "unix"})
@@ -40,32 +48,7 @@ func startIpc(cfg IpcConfig, codec codec.Codec, api api.EthereumApi) error {
continue
}
- go func(conn net.Conn) {
- codec := codec.New(conn)
-
- for {
- req, err := codec.ReadRequest()
- if err == io.EOF {
- codec.Close()
- return
- } else if err != nil {
- glog.V(logger.Error).Infof("IPC recv err - %v\n", err)
- codec.Close()
- return
- }
-
- var rpcResponse interface{}
- res, err := api.Execute(req)
-
- rpcResponse = shared.NewRpcResponse(req.Id, req.Jsonrpc, res, err)
- err = codec.WriteResponse(rpcResponse)
- if err != nil {
- glog.V(logger.Error).Infof("IPC send err - %v\n", err)
- codec.Close()
- return
- }
- }
- }(conn)
+ go handle(conn, api, codec)
}
os.Remove(cfg.Endpoint)
diff --git a/rpc/comms/ipc_windows.go b/rpc/comms/ipc_windows.go
index c48dfb7fb..44c82ef8a 100644
--- a/rpc/comms/ipc_windows.go
+++ b/rpc/comms/ipc_windows.go
@@ -14,7 +14,6 @@ import (
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
- "github.com/ethereum/go-ethereum/rpc/api"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/shared"
)
@@ -641,10 +640,18 @@ func newIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) {
return nil, err
}
- return &ipcClient{codec.New(c)}, nil
+ return &ipcClient{cfg.Endpoint, codec, codec.New(c)}, nil
}
-func startIpc(cfg IpcConfig, codec codec.Codec, api api.EthereumApi) error {
+func (self *ipcClient) reconnect() error {
+ c, err := Dial(self.endpoint)
+ if err == nil {
+ self.coder = self.codec.New(c)
+ }
+ return err
+}
+
+func startIpc(cfg IpcConfig, codec codec.Codec, api shared.EthereumApi) error {
os.Remove(cfg.Endpoint) // in case it still exists from a previous run
l, err := Listen(cfg.Endpoint)
diff --git a/rpc/http.go b/rpc/http.go
deleted file mode 100644
index f37e102f5..000000000
--- a/rpc/http.go
+++ /dev/null
@@ -1,163 +0,0 @@
-package rpc
-
-import (
- "encoding/json"
- "fmt"
- "io"
- "io/ioutil"
- "net/http"
- "strings"
-
- "github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/logger/glog"
- "github.com/ethereum/go-ethereum/xeth"
- "github.com/rs/cors"
-)
-
-var rpclistener *stoppableTCPListener
-
-const (
- jsonrpcver = "2.0"
- maxSizeReqLength = 1024 * 1024 // 1MB
-)
-
-func Start(pipe *xeth.XEth, config RpcConfig) error {
- if rpclistener != nil {
- if fmt.Sprintf("%s:%d", config.ListenAddress, config.ListenPort) != rpclistener.Addr().String() {
- return fmt.Errorf("RPC service already running on %s ", rpclistener.Addr().String())
- }
- return nil // RPC service already running on given host/port
- }
-
- l, err := newStoppableTCPListener(fmt.Sprintf("%s:%d", config.ListenAddress, config.ListenPort))
- if err != nil {
- glog.V(logger.Error).Infof("Can't listen on %s:%d: %v", config.ListenAddress, config.ListenPort, err)
- return err
- }
- rpclistener = l
-
- var handler http.Handler
- if len(config.CorsDomain) > 0 {
- var opts cors.Options
- opts.AllowedMethods = []string{"POST"}
- opts.AllowedOrigins = strings.Split(config.CorsDomain, " ")
-
- c := cors.New(opts)
- handler = newStoppableHandler(c.Handler(JSONRPC(pipe)), l.stop)
- } else {
- handler = newStoppableHandler(JSONRPC(pipe), l.stop)
- }
-
- go http.Serve(l, handler)
-
- return nil
-}
-
-func Stop() error {
- if rpclistener != nil {
- rpclistener.Stop()
- rpclistener = nil
- }
-
- return nil
-}
-
-// JSONRPC returns a handler that implements the Ethereum JSON-RPC API.
-func JSONRPC(pipe *xeth.XEth) http.Handler {
- api := NewEthereumApi(pipe)
-
- return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- w.Header().Set("Content-Type", "application/json")
-
- // Limit request size to resist DoS
- if req.ContentLength > maxSizeReqLength {
- jsonerr := &RpcErrorObject{-32700, "Request too large"}
- send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr})
- return
- }
-
- // Read request body
- defer req.Body.Close()
- body, err := ioutil.ReadAll(req.Body)
- if err != nil {
- jsonerr := &RpcErrorObject{-32700, "Could not read request body"}
- send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr})
- }
-
- // Try to parse the request as a single
- var reqSingle RpcRequest
- if err := json.Unmarshal(body, &reqSingle); err == nil {
- response := RpcResponse(api, &reqSingle)
- if reqSingle.Id != nil {
- send(w, &response)
- }
- return
- }
-
- // Try to parse the request to batch
- var reqBatch []RpcRequest
- if err := json.Unmarshal(body, &reqBatch); err == nil {
- // Build response batch
- resBatch := make([]*interface{}, len(reqBatch))
- resCount := 0
-
- for i, request := range reqBatch {
- response := RpcResponse(api, &request)
- // this leaves nil entries in the response batch for later removal
- if request.Id != nil {
- resBatch[i] = response
- resCount = resCount + 1
- }
- }
-
- // make response omitting nil entries
- respBatchComp := make([]*interface{}, resCount)
- for _, v := range resBatch {
- if v != nil {
- respBatchComp[len(respBatchComp)-resCount] = v
- resCount = resCount - 1
- }
- }
-
- send(w, respBatchComp)
- return
- }
-
- // Not a batch or single request, error
- jsonerr := &RpcErrorObject{-32600, "Could not decode request"}
- send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr})
- })
-}
-
-func RpcResponse(api *EthereumApi, request *RpcRequest) *interface{} {
- var reply, response interface{}
- reserr := api.GetRequestReply(request, &reply)
- switch reserr.(type) {
- case nil:
- response = &RpcSuccessResponse{Jsonrpc: jsonrpcver, Id: request.Id, Result: reply}
- case *NotImplementedError, *NotAvailableError:
- jsonerr := &RpcErrorObject{-32601, reserr.Error()}
- response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr}
- case *DecodeParamError, *InsufficientParamsError, *ValidationError, *InvalidTypeError:
- jsonerr := &RpcErrorObject{-32602, reserr.Error()}
- response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr}
- default:
- jsonerr := &RpcErrorObject{-32603, reserr.Error()}
- response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr}
- }
-
- glog.V(logger.Detail).Infof("Generated response: %T %s", response, response)
- return &response
-}
-
-func send(writer io.Writer, v interface{}) (n int, err error) {
- var payload []byte
- payload, err = json.MarshalIndent(v, "", "\t")
- if err != nil {
- glog.V(logger.Error).Infoln("Error marshalling JSON", err)
- return 0, err
- }
- glog.V(logger.Detail).Infof("Sending payload: %s", payload)
-
- return writer.Write(payload)
-}
diff --git a/rpc/jeth.go b/rpc/jeth.go
index e578775bb..33fcd6efd 100644
--- a/rpc/jeth.go
+++ b/rpc/jeth.go
@@ -2,30 +2,26 @@ package rpc
import (
"encoding/json"
- "fmt"
-
- "reflect"
"github.com/ethereum/go-ethereum/jsre"
- "github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/comms"
"github.com/ethereum/go-ethereum/rpc/shared"
"github.com/robertkrimen/otto"
)
type Jeth struct {
- ethApi *EthereumApi
- re *jsre.JSRE
- ipcpath string
+ ethApi shared.EthereumApi
+ re *jsre.JSRE
+ client comms.EthereumClient
}
-func NewJeth(ethApi *EthereumApi, re *jsre.JSRE, ipcpath string) *Jeth {
- return &Jeth{ethApi, re, ipcpath}
+func NewJeth(ethApi shared.EthereumApi, re *jsre.JSRE, client comms.EthereumClient) *Jeth {
+ return &Jeth{ethApi, re, client}
}
func (self *Jeth) err(call otto.FunctionCall, code int, msg string, id interface{}) (response otto.Value) {
- rpcerr := &RpcErrorObject{code, msg}
- call.Otto.Set("ret_jsonrpc", jsonrpcver)
+ rpcerr := &shared.ErrorObject{code, msg}
+ call.Otto.Set("ret_jsonrpc", shared.JsonRpcVersion)
call.Otto.Set("ret_id", id)
call.Otto.Set("ret_error", rpcerr)
response, _ = call.Otto.Run(`
@@ -41,11 +37,11 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) {
}
jsonreq, err := json.Marshal(reqif)
- var reqs []RpcRequest
+ var reqs []shared.Request
batch := true
err = json.Unmarshal(jsonreq, &reqs)
if err != nil {
- reqs = make([]RpcRequest, 1)
+ reqs = make([]shared.Request, 1)
err = json.Unmarshal(jsonreq, &reqs[0])
batch = false
}
@@ -55,12 +51,16 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) {
for i, req := range reqs {
var respif interface{}
- err = self.ethApi.GetRequestReply(&req, &respif)
+ err := self.client.Send(&req)
+ if err != nil {
+ return self.err(call, -32603, err.Error(), req.Id)
+ }
+ respif, err = self.client.Recv()
if err != nil {
- fmt.Println("Error response:", err)
return self.err(call, -32603, err.Error(), req.Id)
}
- call.Otto.Set("ret_jsonrpc", jsonrpcver)
+
+ call.Otto.Set("ret_jsonrpc", shared.JsonRpcVersion)
call.Otto.Set("ret_id", req.Id)
res, _ := json.Marshal(respif)
@@ -87,85 +87,3 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) {
return
}
-
-func (self *Jeth) SendIpc(call otto.FunctionCall) (response otto.Value) {
- reqif, err := call.Argument(0).Export()
- if err != nil {
- return self.err(call, -32700, err.Error(), nil)
- }
-
- client, err := comms.NewIpcClient(comms.IpcConfig{self.ipcpath}, codec.JSON)
- if err != nil {
- fmt.Println("Unable to connect to geth.")
- return self.err(call, -32603, err.Error(), -1)
- }
- defer client.Close()
-
- jsonreq, err := json.Marshal(reqif)
- var reqs []RpcRequest
- batch := true
- err = json.Unmarshal(jsonreq, &reqs)
- if err != nil {
- reqs = make([]RpcRequest, 1)
- err = json.Unmarshal(jsonreq, &reqs[0])
- batch = false
- }
-
- call.Otto.Set("response_len", len(reqs))
- call.Otto.Run("var ret_response = new Array(response_len);")
-
- for i, req := range reqs {
- err := client.Send(&req)
- if err != nil {
- fmt.Println("Error send request:", err)
- return self.err(call, -32603, err.Error(), req.Id)
- }
-
- respif, err := client.Recv()
- if err != nil {
- fmt.Println("Error recv response:", err)
- return self.err(call, -32603, err.Error(), req.Id)
- }
-
- if res, ok := respif.(shared.SuccessResponse); ok {
- call.Otto.Set("ret_id", res.Id)
- call.Otto.Set("ret_jsonrpc", res.Jsonrpc)
- resObj, _ := json.Marshal(res.Result)
- call.Otto.Set("ret_result", string(resObj))
- call.Otto.Set("response_idx", i)
-
- response, err = call.Otto.Run(`
- ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, result: JSON.parse(ret_result) };
- `)
- } else if res, ok := respif.(shared.ErrorResponse); ok {
- fmt.Printf("Error: %s (%d)\n", res.Error.Message, res.Error.Code)
-
- call.Otto.Set("ret_id", res.Id)
- call.Otto.Set("ret_jsonrpc", res.Jsonrpc)
- call.Otto.Set("ret_error", res.Error)
- call.Otto.Set("response_idx", i)
-
- response, _ = call.Otto.Run(`
- ret_response = { jsonrpc: ret_jsonrpc, id: ret_id, error: ret_error };
- `)
- return
- } else {
- fmt.Printf("unexpected response\n", reflect.TypeOf(respif))
- }
- }
-
- if !batch {
- call.Otto.Run("ret_response = ret_response[0];")
- }
-
- if call.Argument(1).IsObject() {
- call.Otto.Set("callback", call.Argument(1))
- call.Otto.Run(`
- if (Object.prototype.toString.call(callback) == '[object Function]') {
- callback(null, ret_response);
- }
- `)
- }
-
- return
-}
diff --git a/rpc/responses.go b/rpc/responses.go
deleted file mode 100644
index 9fdf60c02..000000000
--- a/rpc/responses.go
+++ /dev/null
@@ -1,316 +0,0 @@
-package rpc
-
-import (
- "encoding/json"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/types"
-)
-
-type BlockRes struct {
- fullTx bool
-
- BlockNumber *hexnum `json:"number"`
- BlockHash *hexdata `json:"hash"`
- ParentHash *hexdata `json:"parentHash"`
- Nonce *hexdata `json:"nonce"`
- Sha3Uncles *hexdata `json:"sha3Uncles"`
- LogsBloom *hexdata `json:"logsBloom"`
- TransactionRoot *hexdata `json:"transactionsRoot"`
- StateRoot *hexdata `json:"stateRoot"`
- Miner *hexdata `json:"miner"`
- Difficulty *hexnum `json:"difficulty"`
- TotalDifficulty *hexnum `json:"totalDifficulty"`
- Size *hexnum `json:"size"`
- ExtraData *hexdata `json:"extraData"`
- GasLimit *hexnum `json:"gasLimit"`
- GasUsed *hexnum `json:"gasUsed"`
- UnixTimestamp *hexnum `json:"timestamp"`
- Transactions []*TransactionRes `json:"transactions"`
- Uncles []*UncleRes `json:"uncles"`
-}
-
-func (b *BlockRes) MarshalJSON() ([]byte, error) {
- if b.fullTx {
- var ext struct {
- BlockNumber *hexnum `json:"number"`
- BlockHash *hexdata `json:"hash"`
- ParentHash *hexdata `json:"parentHash"`
- Nonce *hexdata `json:"nonce"`
- Sha3Uncles *hexdata `json:"sha3Uncles"`
- LogsBloom *hexdata `json:"logsBloom"`
- TransactionRoot *hexdata `json:"transactionsRoot"`
- StateRoot *hexdata `json:"stateRoot"`
- Miner *hexdata `json:"miner"`
- Difficulty *hexnum `json:"difficulty"`
- TotalDifficulty *hexnum `json:"totalDifficulty"`
- Size *hexnum `json:"size"`
- ExtraData *hexdata `json:"extraData"`
- GasLimit *hexnum `json:"gasLimit"`
- GasUsed *hexnum `json:"gasUsed"`
- UnixTimestamp *hexnum `json:"timestamp"`
- Transactions []*TransactionRes `json:"transactions"`
- Uncles []*hexdata `json:"uncles"`
- }
-
- ext.BlockNumber = b.BlockNumber
- ext.BlockHash = b.BlockHash
- ext.ParentHash = b.ParentHash
- ext.Nonce = b.Nonce
- ext.Sha3Uncles = b.Sha3Uncles
- ext.LogsBloom = b.LogsBloom
- ext.TransactionRoot = b.TransactionRoot
- ext.StateRoot = b.StateRoot
- ext.Miner = b.Miner
- ext.Difficulty = b.Difficulty
- ext.TotalDifficulty = b.TotalDifficulty
- ext.Size = b.Size
- ext.ExtraData = b.ExtraData
- ext.GasLimit = b.GasLimit
- ext.GasUsed = b.GasUsed
- ext.UnixTimestamp = b.UnixTimestamp
- ext.Transactions = b.Transactions
- ext.Uncles = make([]*hexdata, len(b.Uncles))
- for i, u := range b.Uncles {
- ext.Uncles[i] = u.BlockHash
- }
- return json.Marshal(ext)
- } else {
- var ext struct {
- BlockNumber *hexnum `json:"number"`
- BlockHash *hexdata `json:"hash"`
- ParentHash *hexdata `json:"parentHash"`
- Nonce *hexdata `json:"nonce"`
- Sha3Uncles *hexdata `json:"sha3Uncles"`
- LogsBloom *hexdata `json:"logsBloom"`
- TransactionRoot *hexdata `json:"transactionsRoot"`
- StateRoot *hexdata `json:"stateRoot"`
- Miner *hexdata `json:"miner"`
- Difficulty *hexnum `json:"difficulty"`
- TotalDifficulty *hexnum `json:"totalDifficulty"`
- Size *hexnum `json:"size"`
- ExtraData *hexdata `json:"extraData"`
- GasLimit *hexnum `json:"gasLimit"`
- GasUsed *hexnum `json:"gasUsed"`
- UnixTimestamp *hexnum `json:"timestamp"`
- Transactions []*hexdata `json:"transactions"`
- Uncles []*hexdata `json:"uncles"`
- }
-
- ext.BlockNumber = b.BlockNumber
- ext.BlockHash = b.BlockHash
- ext.ParentHash = b.ParentHash
- ext.Nonce = b.Nonce
- ext.Sha3Uncles = b.Sha3Uncles
- ext.LogsBloom = b.LogsBloom
- ext.TransactionRoot = b.TransactionRoot
- ext.StateRoot = b.StateRoot
- ext.Miner = b.Miner
- ext.Difficulty = b.Difficulty
- ext.TotalDifficulty = b.TotalDifficulty
- ext.Size = b.Size
- ext.ExtraData = b.ExtraData
- ext.GasLimit = b.GasLimit
- ext.GasUsed = b.GasUsed
- ext.UnixTimestamp = b.UnixTimestamp
- ext.Transactions = make([]*hexdata, len(b.Transactions))
- for i, tx := range b.Transactions {
- ext.Transactions[i] = tx.Hash
- }
- ext.Uncles = make([]*hexdata, len(b.Uncles))
- for i, u := range b.Uncles {
- ext.Uncles[i] = u.BlockHash
- }
- return json.Marshal(ext)
- }
-}
-
-func NewBlockRes(block *types.Block, fullTx bool) *BlockRes {
- if block == nil {
- return nil
- }
-
- res := new(BlockRes)
- res.fullTx = fullTx
- res.BlockNumber = newHexNum(block.Number())
- res.BlockHash = newHexData(block.Hash())
- res.ParentHash = newHexData(block.ParentHash())
- res.Nonce = newHexData(block.Nonce())
- res.Sha3Uncles = newHexData(block.Header().UncleHash)
- res.LogsBloom = newHexData(block.Bloom())
- res.TransactionRoot = newHexData(block.Header().TxHash)
- res.StateRoot = newHexData(block.Root())
- res.Miner = newHexData(block.Header().Coinbase)
- res.Difficulty = newHexNum(block.Difficulty())
- res.TotalDifficulty = newHexNum(block.Td)
- res.Size = newHexNum(block.Size().Int64())
- res.ExtraData = newHexData(block.Header().Extra)
- res.GasLimit = newHexNum(block.GasLimit())
- res.GasUsed = newHexNum(block.GasUsed())
- res.UnixTimestamp = newHexNum(block.Time())
-
- res.Transactions = make([]*TransactionRes, len(block.Transactions()))
- for i, tx := range block.Transactions() {
- res.Transactions[i] = NewTransactionRes(tx)
- res.Transactions[i].BlockHash = res.BlockHash
- res.Transactions[i].BlockNumber = res.BlockNumber
- res.Transactions[i].TxIndex = newHexNum(i)
- }
-
- res.Uncles = make([]*UncleRes, len(block.Uncles()))
- for i, uncle := range block.Uncles() {
- res.Uncles[i] = NewUncleRes(uncle)
- }
-
- return res
-}
-
-type TransactionRes struct {
- Hash *hexdata `json:"hash"`
- Nonce *hexnum `json:"nonce"`
- BlockHash *hexdata `json:"blockHash"`
- BlockNumber *hexnum `json:"blockNumber"`
- TxIndex *hexnum `json:"transactionIndex"`
- From *hexdata `json:"from"`
- To *hexdata `json:"to"`
- Value *hexnum `json:"value"`
- Gas *hexnum `json:"gas"`
- GasPrice *hexnum `json:"gasPrice"`
- Input *hexdata `json:"input"`
-}
-
-func NewTransactionRes(tx *types.Transaction) *TransactionRes {
- if tx == nil {
- return nil
- }
-
- var v = new(TransactionRes)
- v.Hash = newHexData(tx.Hash())
- v.Nonce = newHexNum(tx.Nonce())
- // v.BlockHash =
- // v.BlockNumber =
- // v.TxIndex =
- from, _ := tx.From()
- v.From = newHexData(from)
- v.To = newHexData(tx.To())
- v.Value = newHexNum(tx.Value())
- v.Gas = newHexNum(tx.Gas())
- v.GasPrice = newHexNum(tx.GasPrice())
- v.Input = newHexData(tx.Data())
- return v
-}
-
-type UncleRes struct {
- BlockNumber *hexnum `json:"number"`
- BlockHash *hexdata `json:"hash"`
- ParentHash *hexdata `json:"parentHash"`
- Nonce *hexdata `json:"nonce"`
- Sha3Uncles *hexdata `json:"sha3Uncles"`
- ReceiptHash *hexdata `json:"receiptHash"`
- LogsBloom *hexdata `json:"logsBloom"`
- TransactionRoot *hexdata `json:"transactionsRoot"`
- StateRoot *hexdata `json:"stateRoot"`
- Miner *hexdata `json:"miner"`
- Difficulty *hexnum `json:"difficulty"`
- ExtraData *hexdata `json:"extraData"`
- GasLimit *hexnum `json:"gasLimit"`
- GasUsed *hexnum `json:"gasUsed"`
- UnixTimestamp *hexnum `json:"timestamp"`
-}
-
-func NewUncleRes(h *types.Header) *UncleRes {
- if h == nil {
- return nil
- }
-
- var v = new(UncleRes)
- v.BlockNumber = newHexNum(h.Number)
- v.BlockHash = newHexData(h.Hash())
- v.ParentHash = newHexData(h.ParentHash)
- v.Sha3Uncles = newHexData(h.UncleHash)
- v.Nonce = newHexData(h.Nonce[:])
- v.LogsBloom = newHexData(h.Bloom)
- v.TransactionRoot = newHexData(h.TxHash)
- v.StateRoot = newHexData(h.Root)
- v.Miner = newHexData(h.Coinbase)
- v.Difficulty = newHexNum(h.Difficulty)
- v.ExtraData = newHexData(h.Extra)
- v.GasLimit = newHexNum(h.GasLimit)
- v.GasUsed = newHexNum(h.GasUsed)
- v.UnixTimestamp = newHexNum(h.Time)
- v.ReceiptHash = newHexData(h.ReceiptHash)
-
- return v
-}
-
-// type FilterLogRes struct {
-// Hash string `json:"hash"`
-// Address string `json:"address"`
-// Data string `json:"data"`
-// BlockNumber string `json:"blockNumber"`
-// TransactionHash string `json:"transactionHash"`
-// BlockHash string `json:"blockHash"`
-// TransactionIndex string `json:"transactionIndex"`
-// LogIndex string `json:"logIndex"`
-// }
-
-// type FilterWhisperRes struct {
-// Hash string `json:"hash"`
-// From string `json:"from"`
-// To string `json:"to"`
-// Expiry string `json:"expiry"`
-// Sent string `json:"sent"`
-// Ttl string `json:"ttl"`
-// Topics string `json:"topics"`
-// Payload string `json:"payload"`
-// WorkProved string `json:"workProved"`
-// }
-
-type LogRes struct {
- Address *hexdata `json:"address"`
- Topics []*hexdata `json:"topics"`
- Data *hexdata `json:"data"`
- BlockNumber *hexnum `json:"blockNumber"`
- LogIndex *hexnum `json:"logIndex"`
- BlockHash *hexdata `json:"blockHash"`
- TransactionHash *hexdata `json:"transactionHash"`
- TransactionIndex *hexnum `json:"transactionIndex"`
-}
-
-func NewLogRes(log *state.Log) LogRes {
- var l LogRes
- l.Topics = make([]*hexdata, len(log.Topics))
- for j, topic := range log.Topics {
- l.Topics[j] = newHexData(topic)
- }
- l.Address = newHexData(log.Address)
- l.Data = newHexData(log.Data)
- l.BlockNumber = newHexNum(log.Number)
- l.LogIndex = newHexNum(log.Index)
- l.TransactionHash = newHexData(log.TxHash)
- l.TransactionIndex = newHexNum(log.TxIndex)
- l.BlockHash = newHexData(log.BlockHash)
-
- return l
-}
-
-func NewLogsRes(logs state.Logs) (ls []LogRes) {
- ls = make([]LogRes, len(logs))
-
- for i, log := range logs {
- ls[i] = NewLogRes(log)
- }
-
- return
-}
-
-func NewHashesRes(hs []common.Hash) []string {
- hashes := make([]string, len(hs))
-
- for i, hash := range hs {
- hashes[i] = hash.Hex()
- }
-
- return hashes
-}
diff --git a/rpc/responses_test.go b/rpc/responses_test.go
deleted file mode 100644
index 66323e5f5..000000000
--- a/rpc/responses_test.go
+++ /dev/null
@@ -1,295 +0,0 @@
-package rpc
-
-import (
- "encoding/json"
- "fmt"
- "math/big"
- "regexp"
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/types"
-)
-
-const (
- reHash = `"0x[0-9a-f]{64}"` // 32 bytes
- reHashOpt = `"(0x[0-9a-f]{64})"|null` // 32 bytes or null
- reAddress = `"0x[0-9a-f]{40}"` // 20 bytes
- reAddressOpt = `"0x[0-9a-f]{40}"|null` // 20 bytes or null
- reNum = `"0x([1-9a-f][0-9a-f]{0,15})|0"` // must not have left-padded zeros
- reNumNonZero = `"0x([1-9a-f][0-9a-f]{0,15})"` // non-zero required must not have left-padded zeros
- reNumOpt = `"0x([1-9a-f][0-9a-f]{0,15})|0"|null` // must not have left-padded zeros or null
- reData = `"0x[0-9a-f]*"` // can be "empty"
- // reListHash = `[("\w":"0x[0-9a-f]{64}",?)*]`
- // reListObj = `[("\w":(".+"|null),?)*]`
-)
-
-func TestNewBlockRes(t *testing.T) {
- tests := map[string]string{
- "number": reNum,
- "hash": reHash,
- "parentHash": reHash,
- "nonce": reData,
- "sha3Uncles": reHash,
- "logsBloom": reData,
- "transactionsRoot": reHash,
- "stateRoot": reHash,
- "miner": reAddress,
- "difficulty": `"0x1"`,
- "totalDifficulty": reNum,
- "size": reNumNonZero,
- "extraData": reData,
- "gasLimit": reNum,
- // "minGasPrice": "0x",
- "gasUsed": reNum,
- "timestamp": reNum,
- // "transactions": reListHash,
- // "uncles": reListHash,
- }
-
- block := makeBlock()
- v := NewBlockRes(block, false)
- j, _ := json.Marshal(v)
-
- for k, re := range tests {
- match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j))
- if !match {
- t.Error(fmt.Sprintf("%s output json does not match format %s. Got %s", k, re, j))
- }
- }
-}
-
-func TestNewBlockResTxFull(t *testing.T) {
- tests := map[string]string{
- "number": reNum,
- "hash": reHash,
- "parentHash": reHash,
- "nonce": reData,
- "sha3Uncles": reHash,
- "logsBloom": reData,
- "transactionsRoot": reHash,
- "stateRoot": reHash,
- "miner": reAddress,
- "difficulty": `"0x1"`,
- "totalDifficulty": reNum,
- "size": reNumNonZero,
- "extraData": reData,
- "gasLimit": reNum,
- // "minGasPrice": "0x",
- "gasUsed": reNum,
- "timestamp": reNum,
- // "transactions": reListHash,
- // "uncles": reListHash,
- }
-
- block := makeBlock()
- v := NewBlockRes(block, true)
- j, _ := json.Marshal(v)
-
- for k, re := range tests {
- match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j))
- if !match {
- t.Error(fmt.Sprintf("%s output json does not match format %s. Got %s", k, re, j))
- }
- }
-}
-
-func TestBlockNil(t *testing.T) {
- var block *types.Block
- block = nil
- u := NewBlockRes(block, false)
- j, _ := json.Marshal(u)
- if string(j) != "null" {
- t.Errorf("Expected null but got %v", string(j))
- }
-}
-
-func TestNewTransactionRes(t *testing.T) {
- to := common.HexToAddress("0x02")
- amount := big.NewInt(1)
- gasAmount := big.NewInt(1)
- gasPrice := big.NewInt(1)
- data := []byte{1, 2, 3}
- tx := types.NewTransactionMessage(to, amount, gasAmount, gasPrice, data)
-
- tests := map[string]string{
- "hash": reHash,
- "nonce": reNum,
- "blockHash": reHashOpt,
- "blockNum": reNumOpt,
- "transactionIndex": reNumOpt,
- "from": reAddress,
- "to": reAddressOpt,
- "value": reNum,
- "gas": reNum,
- "gasPrice": reNum,
- "input": reData,
- }
-
- v := NewTransactionRes(tx)
- v.BlockHash = newHexData(common.HexToHash("0x030201"))
- v.BlockNumber = newHexNum(5)
- v.TxIndex = newHexNum(0)
- j, _ := json.Marshal(v)
- for k, re := range tests {
- match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j))
- if !match {
- t.Error(fmt.Sprintf("`%s` output json does not match format %s. Source %s", k, re, j))
- }
- }
-
-}
-
-func TestTransactionNil(t *testing.T) {
- var tx *types.Transaction
- tx = nil
- u := NewTransactionRes(tx)
- j, _ := json.Marshal(u)
- if string(j) != "null" {
- t.Errorf("Expected null but got %v", string(j))
- }
-}
-
-func TestNewUncleRes(t *testing.T) {
- header := makeHeader()
- u := NewUncleRes(header)
- tests := map[string]string{
- "number": reNum,
- "hash": reHash,
- "parentHash": reHash,
- "nonce": reData,
- "sha3Uncles": reHash,
- "receiptHash": reHash,
- "transactionsRoot": reHash,
- "stateRoot": reHash,
- "miner": reAddress,
- "difficulty": reNum,
- "extraData": reData,
- "gasLimit": reNum,
- "gasUsed": reNum,
- "timestamp": reNum,
- }
-
- j, _ := json.Marshal(u)
- for k, re := range tests {
- match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j))
- if !match {
- t.Error(fmt.Sprintf("`%s` output json does not match format %s. Source %s", k, re, j))
- }
- }
-}
-
-func TestUncleNil(t *testing.T) {
- var header *types.Header
- header = nil
- u := NewUncleRes(header)
- j, _ := json.Marshal(u)
- if string(j) != "null" {
- t.Errorf("Expected null but got %v", string(j))
- }
-}
-
-func TestNewLogRes(t *testing.T) {
- log := makeStateLog(0)
- tests := map[string]string{
- "address": reAddress,
- // "topics": "[.*]"
- "data": reData,
- "blockNumber": reNum,
- // "hash": reHash,
- // "logIndex": reNum,
- // "blockHash": reHash,
- // "transactionHash": reHash,
- "transactionIndex": reNum,
- }
-
- v := NewLogRes(log)
- j, _ := json.Marshal(v)
-
- for k, re := range tests {
- match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j))
- if !match {
- t.Error(fmt.Sprintf("`%s` output json does not match format %s. Got %s", k, re, j))
- }
- }
-
-}
-
-func TestNewLogsRes(t *testing.T) {
- logs := make([]*state.Log, 3)
- logs[0] = makeStateLog(1)
- logs[1] = makeStateLog(2)
- logs[2] = makeStateLog(3)
- tests := map[string]string{}
-
- v := NewLogsRes(logs)
- j, _ := json.Marshal(v)
-
- for k, re := range tests {
- match, _ := regexp.MatchString(fmt.Sprintf(`[{.*"%s":%s.*}]`, k, re), string(j))
- if !match {
- t.Error(fmt.Sprintf("%s output json does not match format %s. Got %s", k, re, j))
- }
- }
-
-}
-
-func makeStateLog(num int) *state.Log {
- address := common.HexToAddress("0x0")
- data := []byte{1, 2, 3}
- number := uint64(num)
- topics := make([]common.Hash, 3)
- topics = append(topics, common.HexToHash("0x00"))
- topics = append(topics, common.HexToHash("0x10"))
- topics = append(topics, common.HexToHash("0x20"))
- log := state.NewLog(address, topics, data, number)
- return log
-}
-
-func makeHeader() *types.Header {
- header := &types.Header{
- ParentHash: common.StringToHash("0x00"),
- UncleHash: common.StringToHash("0x00"),
- Coinbase: common.StringToAddress("0x00"),
- Root: common.StringToHash("0x00"),
- TxHash: common.StringToHash("0x00"),
- ReceiptHash: common.StringToHash("0x00"),
- // Bloom:
- Difficulty: big.NewInt(88888888),
- Number: big.NewInt(16),
- GasLimit: big.NewInt(70000),
- GasUsed: big.NewInt(25000),
- Time: 124356789,
- Extra: nil,
- MixDigest: common.StringToHash("0x00"),
- Nonce: [8]byte{0, 1, 2, 3, 4, 5, 6, 7},
- }
- return header
-}
-
-func makeBlock() *types.Block {
- parentHash := common.HexToHash("0x01")
- coinbase := common.HexToAddress("0x01")
- root := common.HexToHash("0x01")
- difficulty := common.Big1
- nonce := uint64(1)
- block := types.NewBlock(parentHash, coinbase, root, difficulty, nonce, nil)
-
- txto := common.HexToAddress("0x02")
- txamount := big.NewInt(1)
- txgasAmount := big.NewInt(1)
- txgasPrice := big.NewInt(1)
- txdata := []byte{1, 2, 3}
-
- tx := types.NewTransactionMessage(txto, txamount, txgasAmount, txgasPrice, txdata)
- txs := make([]*types.Transaction, 1)
- txs[0] = tx
- block.SetTransactions(txs)
-
- uncles := make([]*types.Header, 1)
- uncles[0] = makeHeader()
- block.SetUncles(uncles)
-
- return block
-}
diff --git a/rpc/shared/types.go b/rpc/shared/types.go
index 6a29fa88e..7c4b04e83 100644
--- a/rpc/shared/types.go
+++ b/rpc/shared/types.go
@@ -7,6 +7,21 @@ import (
"github.com/ethereum/go-ethereum/logger/glog"
)
+// Ethereum RPC API interface
+type EthereumApi interface {
+ // API identifier
+ Name() string
+
+ // API version
+ ApiVersion() string
+
+ // Execute the given request and returns the response or an error
+ Execute(*Request) (interface{}, error)
+
+ // List of supported RCP methods this API provides
+ Methods() []string
+}
+
// RPC request
type Request struct {
Id interface{} `json:"id"`
@@ -42,6 +57,18 @@ type ErrorObject struct {
// Data interface{} `json:"data"`
}
+// Create RPC error response, this allows for custom error codes
+func NewRpcErrorResponse(id interface{}, jsonrpcver string, errCode int, err error) *interface{} {
+ var response interface{}
+
+ jsonerr := &ErrorObject{errCode, err.Error()}
+ response = ErrorResponse{Jsonrpc: jsonrpcver, Id: id, Error: jsonerr}
+
+ glog.V(logger.Detail).Infof("Generated error response: %s", response)
+ return &response
+}
+
+// Create RPC response
func NewRpcResponse(id interface{}, jsonrpcver string, reply interface{}, err error) *interface{} {
var response interface{}
diff --git a/rpc/shared/utils.go b/rpc/shared/utils.go
new file mode 100644
index 000000000..e5d6ad417
--- /dev/null
+++ b/rpc/shared/utils.go
@@ -0,0 +1,27 @@
+package shared
+
+import "strings"
+
+const (
+ AdminApiName = "admin"
+ EthApiName = "eth"
+ DbApiName = "db"
+ DebugApiName = "debug"
+ MergedApiName = "merged"
+ MinerApiName = "miner"
+ NetApiName = "net"
+ ShhApiName = "shh"
+ TxPoolApiName = "txpool"
+ PersonalApiName = "personal"
+ Web3ApiName = "web3"
+
+ JsonRpcVersion = "2.0"
+)
+
+var (
+ // All API's
+ AllApis = strings.Join([]string{
+ AdminApiName, DbApiName, EthApiName, DebugApiName, MinerApiName, NetApiName,
+ ShhApiName, TxPoolApiName, PersonalApiName, Web3ApiName,
+ }, ",")
+)
diff --git a/rpc/types.go b/rpc/types.go
deleted file mode 100644
index 1f49a3dea..000000000
--- a/rpc/types.go
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- 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 rpc
-
-import (
- "encoding/binary"
- "encoding/hex"
- "encoding/json"
- "fmt"
- "math/big"
- "strings"
-
- "errors"
- "net"
- "net/http"
- "time"
-
- "io"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/types"
-)
-
-type hexdata struct {
- data []byte
- isNil bool
-}
-
-func (d *hexdata) String() string {
- return "0x" + common.Bytes2Hex(d.data)
-}
-
-func (d *hexdata) MarshalJSON() ([]byte, error) {
- if d.isNil {
- return json.Marshal(nil)
- }
- return json.Marshal(d.String())
-}
-
-func newHexData(input interface{}) *hexdata {
- d := new(hexdata)
-
- if input == nil {
- d.isNil = true
- return d
- }
- switch input := input.(type) {
- case []byte:
- d.data = input
- case common.Hash:
- d.data = input.Bytes()
- case *common.Hash:
- if input == nil {
- d.isNil = true
- } else {
- d.data = input.Bytes()
- }
- case common.Address:
- d.data = input.Bytes()
- case *common.Address:
- if input == nil {
- d.isNil = true
- } else {
- d.data = input.Bytes()
- }
- case types.Bloom:
- d.data = input.Bytes()
- case *types.Bloom:
- if input == nil {
- d.isNil = true
- } else {
- d.data = input.Bytes()
- }
- case *big.Int:
- if input == nil {
- d.isNil = true
- } else {
- d.data = input.Bytes()
- }
- case int64:
- d.data = big.NewInt(input).Bytes()
- case uint64:
- buff := make([]byte, 8)
- binary.BigEndian.PutUint64(buff, input)
- d.data = buff
- case int:
- d.data = big.NewInt(int64(input)).Bytes()
- case uint:
- d.data = big.NewInt(int64(input)).Bytes()
- case int8:
- d.data = big.NewInt(int64(input)).Bytes()
- case uint8:
- d.data = big.NewInt(int64(input)).Bytes()
- case int16:
- d.data = big.NewInt(int64(input)).Bytes()
- case uint16:
- buff := make([]byte, 2)
- binary.BigEndian.PutUint16(buff, input)
- d.data = buff
- case int32:
- d.data = big.NewInt(int64(input)).Bytes()
- case uint32:
- buff := make([]byte, 4)
- binary.BigEndian.PutUint32(buff, input)
- d.data = buff
- case string: // hexstring
- // aaargh ffs TODO: avoid back-and-forth hex encodings where unneeded
- bytes, err := hex.DecodeString(strings.TrimPrefix(input, "0x"))
- if err != nil {
- d.isNil = true
- } else {
- d.data = bytes
- }
- default:
- d.isNil = true
- }
-
- return d
-}
-
-type hexnum struct {
- data []byte
- isNil bool
-}
-
-func (d *hexnum) String() string {
- // Get hex string from bytes
- out := common.Bytes2Hex(d.data)
- // Trim leading 0s
- out = strings.TrimLeft(out, "0")
- // Output "0x0" when value is 0
- if len(out) == 0 {
- out = "0"
- }
- return "0x" + out
-}
-
-func (d *hexnum) MarshalJSON() ([]byte, error) {
- if d.isNil {
- return json.Marshal(nil)
- }
- return json.Marshal(d.String())
-}
-
-func newHexNum(input interface{}) *hexnum {
- d := new(hexnum)
-
- d.data = newHexData(input).data
-
- return d
-}
-
-type RpcConfig struct {
- ListenAddress string
- ListenPort uint
- CorsDomain string
-}
-
-type InvalidTypeError struct {
- method string
- msg string
-}
-
-func (e *InvalidTypeError) Error() string {
- return fmt.Sprintf("invalid type on field %s: %s", e.method, e.msg)
-}
-
-func NewInvalidTypeError(method, msg string) *InvalidTypeError {
- return &InvalidTypeError{
- method: method,
- msg: msg,
- }
-}
-
-type InsufficientParamsError struct {
- have int
- want int
-}
-
-func (e *InsufficientParamsError) Error() string {
- return fmt.Sprintf("insufficient params, want %d have %d", e.want, e.have)
-}
-
-func NewInsufficientParamsError(have int, want int) *InsufficientParamsError {
- return &InsufficientParamsError{
- have: have,
- want: want,
- }
-}
-
-type NotImplementedError struct {
- Method string
-}
-
-func (e *NotImplementedError) Error() string {
- return fmt.Sprintf("%s method not implemented", e.Method)
-}
-
-func NewNotImplementedError(method string) *NotImplementedError {
- return &NotImplementedError{
- Method: method,
- }
-}
-
-type NotAvailableError struct {
- Method string
- Reason string
-}
-
-func (e *NotAvailableError) Error() string {
- return fmt.Sprintf("%s method not available: %s", e.Method, e.Reason)
-}
-
-func NewNotAvailableError(method string, reason string) *NotAvailableError {
- return &NotAvailableError{
- Method: method,
- Reason: reason,
- }
-}
-
-type DecodeParamError struct {
- err string
-}
-
-func (e *DecodeParamError) Error() string {
- return fmt.Sprintf("could not decode, %s", e.err)
-
-}
-
-func NewDecodeParamError(errstr string) error {
- return &DecodeParamError{
- err: errstr,
- }
-}
-
-type ValidationError struct {
- ParamName string
- msg string
-}
-
-func (e *ValidationError) Error() string {
- return fmt.Sprintf("%s not valid, %s", e.ParamName, e.msg)
-}
-
-func NewValidationError(param string, msg string) error {
- return &ValidationError{
- ParamName: param,
- msg: msg,
- }
-}
-
-type RpcRequest struct {
- Id interface{} `json:"id"`
- Jsonrpc string `json:"jsonrpc"`
- Method string `json:"method"`
- Params json.RawMessage `json:"params"`
-}
-
-type RpcSuccessResponse struct {
- Id interface{} `json:"id"`
- Jsonrpc string `json:"jsonrpc"`
- Result interface{} `json:"result"`
-}
-
-type RpcErrorResponse struct {
- Id interface{} `json:"id"`
- Jsonrpc string `json:"jsonrpc"`
- Error *RpcErrorObject `json:"error"`
-}
-
-type RpcErrorObject struct {
- Code int `json:"code"`
- Message string `json:"message"`
- // Data interface{} `json:"data"`
-}
-
-type listenerHasStoppedError struct {
- msg string
-}
-
-func (self listenerHasStoppedError) Error() string {
- return self.msg
-}
-
-var listenerStoppedError = listenerHasStoppedError{"Listener stopped"}
-
-// When https://github.com/golang/go/issues/4674 is fixed this could be replaced
-type stoppableTCPListener struct {
- *net.TCPListener
- stop chan struct{} // closed when the listener must stop
-}
-
-// Wraps the default handler and checks if the RPC service was stopped. In that case it returns an
-// error indicating that the service was stopped. This will only happen for connections which are
-// kept open (HTTP keep-alive) when the RPC service was shutdown.
-func newStoppableHandler(h http.Handler, stop chan struct{}) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- select {
- case <-stop:
- w.Header().Set("Content-Type", "application/json")
- jsonerr := &RpcErrorObject{-32603, "RPC service stopped"}
- send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr})
- default:
- h.ServeHTTP(w, r)
- }
- })
-}
-
-// Stop the listener and all accepted and still active connections.
-func (self *stoppableTCPListener) Stop() {
- close(self.stop)
-}
-
-func newStoppableTCPListener(addr string) (*stoppableTCPListener, error) {
- wl, err := net.Listen("tcp", addr)
- if err != nil {
- return nil, err
- }
-
- if tcpl, ok := wl.(*net.TCPListener); ok {
- stop := make(chan struct{})
- l := &stoppableTCPListener{tcpl, stop}
- return l, nil
- }
-
- return nil, errors.New("Unable to create TCP listener for RPC service")
-}
-
-func (self *stoppableTCPListener) Accept() (net.Conn, error) {
- for {
- self.SetDeadline(time.Now().Add(time.Duration(1 * time.Second)))
- c, err := self.TCPListener.AcceptTCP()
-
- select {
- case <-self.stop:
- if c != nil { // accept timeout
- c.Close()
- }
- self.TCPListener.Close()
- return nil, listenerStoppedError
- default:
- }
-
- if err != nil {
- if netErr, ok := err.(net.Error); ok && netErr.Timeout() && netErr.Temporary() {
- continue // regular timeout
- }
- }
-
- return &closableConnection{c, self.stop}, err
- }
-}
-
-type closableConnection struct {
- *net.TCPConn
- closed chan struct{}
-}
-
-func (self *closableConnection) Read(b []byte) (n int, err error) {
- select {
- case <-self.closed:
- self.TCPConn.Close()
- return 0, io.EOF
- default:
- return self.TCPConn.Read(b)
- }
-}
diff --git a/rpc/types_test.go b/rpc/types_test.go
deleted file mode 100644
index 9ef7b8d38..000000000
--- a/rpc/types_test.go
+++ /dev/null
@@ -1,204 +0,0 @@
-package rpc
-
-import (
- "bytes"
- "encoding/json"
- "math/big"
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/types"
-)
-
-func TestInvalidTypeError(t *testing.T) {
- err := NewInvalidTypeError("testField", "not string")
- expected := "invalid type on field testField: not string"
-
- if err.Error() != expected {
- t.Error(err.Error())
- }
-}
-
-func TestInsufficientParamsError(t *testing.T) {
- err := NewInsufficientParamsError(0, 1)
- expected := "insufficient params, want 1 have 0"
-
- if err.Error() != expected {
- t.Error(err.Error())
- }
-}
-
-func TestNotImplementedError(t *testing.T) {
- err := NewNotImplementedError("foo")
- expected := "foo method not implemented"
-
- if err.Error() != expected {
- t.Error(err.Error())
- }
-}
-
-func TestDecodeParamError(t *testing.T) {
- err := NewDecodeParamError("foo")
- expected := "could not decode, foo"
-
- if err.Error() != expected {
- t.Error(err.Error())
- }
-}
-
-func TestValidationError(t *testing.T) {
- err := NewValidationError("foo", "should be `bar`")
- expected := "foo not valid, should be `bar`"
-
- if err.Error() != expected {
- t.Error(err.Error())
- }
-}
-
-func TestHexdataMarshalNil(t *testing.T) {
- hd := newHexData([]byte{})
- hd.isNil = true
- v, _ := json.Marshal(hd)
- if string(v) != "null" {
- t.Errorf("Expected null, got %s", v)
- }
-}
-
-func TestHexnumMarshalNil(t *testing.T) {
- hn := newHexNum([]byte{})
- hn.isNil = true
- v, _ := json.Marshal(hn)
- if string(v) != "null" {
- t.Errorf("Expected null, got %s", v)
- }
-}
-
-func TestHexdataNil(t *testing.T) {
- v := newHexData(nil)
- if v.isNil != true {
- t.Errorf("Expected isNil to be true, but is %v", v.isNil)
- }
-}
-
-func TestHexdataPtrHash(t *testing.T) {
- in := common.Hash{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}
- v := newHexData(&in)
- if bytes.Compare(in.Bytes(), v.data) != 0 {
- t.Errorf("Got % x expected % x", in, v.data)
- }
-}
-
-func TestHexdataPtrHashNil(t *testing.T) {
- var in *common.Hash
- in = nil
- v := newHexData(in)
- if !v.isNil {
- t.Errorf("Expect isNil to be true, but is %v", v.isNil)
- }
-}
-
-func TestHexdataPtrAddress(t *testing.T) {
- in := common.Address{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
- v := newHexData(&in)
- if bytes.Compare(in.Bytes(), v.data) != 0 {
- t.Errorf("Got % x expected % x", in, v.data)
- }
-}
-
-func TestHexdataPtrAddressNil(t *testing.T) {
- var in *common.Address
- in = nil
- v := newHexData(in)
- if !v.isNil {
- t.Errorf("Expect isNil to be true, but is %v", v.isNil)
- }
-}
-
-func TestHexdataPtrBloom(t *testing.T) {
- in := types.Bloom{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
- v := newHexData(&in)
- if bytes.Compare(in.Bytes(), v.data) != 0 {
- t.Errorf("Got % x expected % x", in, v.data)
- }
-}
-
-func TestHexdataPtrBloomNil(t *testing.T) {
- var in *types.Bloom
- in = nil
- v := newHexData(in)
- if !v.isNil {
- t.Errorf("Expect isNil to be true, but is %v", v.isNil)
- }
-}
-
-func TestHexdataBigintNil(t *testing.T) {
- var in *big.Int
- in = nil
- v := newHexData(in)
- if !v.isNil {
- t.Errorf("Expect isNil to be true, but is %v", v.isNil)
- }
-}
-
-func TestHexdataUint(t *testing.T) {
- var in = uint(16)
- var expected = []byte{0x10}
- v := newHexData(in)
- if bytes.Compare(expected, v.data) != 0 {
- t.Errorf("Expected % x got % x", expected, v.data)
- }
-}
-
-func TestHexdataInt8(t *testing.T) {
- var in = int8(16)
- var expected = []byte{0x10}
- v := newHexData(in)
- if bytes.Compare(expected, v.data) != 0 {
- t.Errorf("Expected % x got % x", expected, v.data)
- }
-}
-
-func TestHexdataUint8(t *testing.T) {
- var in = uint8(16)
- var expected = []byte{0x10}
- v := newHexData(in)
- if bytes.Compare(expected, v.data) != 0 {
- t.Errorf("Expected % x got % x", expected, v.data)
- }
-}
-
-func TestHexdataInt16(t *testing.T) {
- var in = int16(16)
- var expected = []byte{0x10}
- v := newHexData(in)
- if bytes.Compare(expected, v.data) != 0 {
- t.Errorf("Expected % x got % x", expected, v.data)
- }
-}
-
-func TestHexdataUint16(t *testing.T) {
- var in = uint16(16)
- var expected = []byte{0x0, 0x10}
- v := newHexData(in)
- if bytes.Compare(expected, v.data) != 0 {
- t.Errorf("Expected % x got % x", expected, v.data)
- }
-}
-
-func TestHexdataInt32(t *testing.T) {
- var in = int32(16)
- var expected = []byte{0x10}
- v := newHexData(in)
- if bytes.Compare(expected, v.data) != 0 {
- t.Errorf("Expected % x got % x", expected, v.data)
- }
-}
-
-func TestHexdataUint32(t *testing.T) {
- var in = uint32(16)
- var expected = []byte{0x0, 0x0, 0x0, 0x10}
- v := newHexData(in)
- if bytes.Compare(expected, v.data) != 0 {
- t.Errorf("Expected % x got % x", expected, v.data)
- }
-}