mirror of https://github.com/ethereum/go-ethereum
Merge pull request #1279 from bas-vk/rpc-http
Integrate console and remove old rpc package structurepull/1323/head
commit
6b5532ab0d
@ -1,9 +0,0 @@ |
|||||||
package main |
|
||||||
|
|
||||||
/* |
|
||||||
node admin bindings |
|
||||||
*/ |
|
||||||
|
|
||||||
func (js *jsre) adminBindings() { |
|
||||||
|
|
||||||
} |
|
@ -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" |
|
||||||
) |
|
@ -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) |
|
||||||
} |
|
||||||
} |
|
@ -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) |
|
||||||
} |
|
||||||
} |
|
@ -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(), |
|
||||||
} |
|
||||||
} |
|
@ -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) |
|
||||||
} |
|
@ -1,48 +1,10 @@ |
|||||||
package api |
package api |
||||||
|
|
||||||
import ( |
import ( |
||||||
"strings" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
"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
|
// Merge multiple API's to a single API instance
|
||||||
func Merge(apis ...EthereumApi) EthereumApi { |
func Merge(apis ...shared.EthereumApi) shared.EthereumApi { |
||||||
return newMergedApi(apis...) |
return newMergedApi(apis...) |
||||||
} |
} |
||||||
|
@ -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 |
||||||
|
} |
@ -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 |
||||||
|
} |
@ -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 |
||||||
|
} |
@ -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: |
||||||
|
[ |
||||||
|
] |
||||||
|
}); |
||||||
|
` |
@ -1,3 +1,20 @@ |
|||||||
package api |
package api |
||||||
|
|
||||||
// JS api provided by web3.js
|
// 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 |
||||||
|
}) |
||||||
|
] |
||||||
|
}); |
||||||
|
` |
||||||
|
@ -1 +0,0 @@ |
|||||||
package api |
|
@ -1,5 +1,29 @@ |
|||||||
package api |
package api |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/rpc/shared" |
||||||
|
) |
||||||
|
|
||||||
type Sha3Args struct { |
type Sha3Args struct { |
||||||
Data string |
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 |
||||||
|
} |
||||||
|
@ -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(ð.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")
|
|
||||||
// }
|
|
||||||
// }
|
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,111 @@ |
|||||||
package comms |
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 { |
type EthereumClient interface { |
||||||
|
// Close underlaying connection
|
||||||
Close() |
Close() |
||||||
|
// Send request
|
||||||
Send(interface{}) error |
Send(interface{}) error |
||||||
|
// Receive response
|
||||||
Recv() (interface{}, error) |
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") |
||||||
} |
} |
||||||
|
@ -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 |
||||||
|
} |
@ -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) |
||||||
|
}) |
||||||
|
} |
@ -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") |
||||||
|
} |
@ -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) |
|
||||||
} |
|
@ -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 |
|
||||||
} |
|
@ -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 |
|
||||||
} |
|
@ -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, |
||||||
|
}, ",") |
||||||
|
) |
@ -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) |
|
||||||
} |
|
||||||
} |
|
@ -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) |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue