Commit e2c2d8e1 authored by Jeffrey Wilcke's avatar Jeffrey Wilcke

Merge pull request #1239 from bas-vk/rpc-apis

RPC refactoring
parents 6609d45e b3c07f16
......@@ -10,6 +10,11 @@ geth:
@echo "Done building."
@echo "Run \"$(GOBIN)/geth\" to launch geth."
console:
build/env.sh go install -v $(shell build/ldflags.sh) ./cmd/console
@echo "Done building."
@echo "Run \"$(GOBIN)/console\" to launch the console."
mist:
build/env.sh go install -v $(shell build/ldflags.sh) ./cmd/mist
@echo "Done building."
......
package main
/*
node admin bindings
*/
func (js *jsre) adminBindings() {
}
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"
)
This diff is collapsed.
/*
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 ether console")
)
func init() {
if gitCommit == "" {
nodeNameVersion = Version
} else {
nodeNameVersion = Version + "-" + gitCommit[:8]
}
app.Action = run
app.Flags = []cli.Flag{
utils.IPCDisabledFlag,
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 := ctx.GlobalString(utils.IPCPathFlag.Name)
repl := newJSRE(jspath, ipcpath)
repl.welcome(ipcpath)
repl.interactive()
}
......@@ -73,7 +73,7 @@ type jsre struct {
prompter
}
func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain string, interactive bool, f xeth.Frontend) *jsre {
func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain, ipcpath string, interactive bool, f xeth.Frontend) *jsre {
js := &jsre{ethereum: ethereum, ps1: "> "}
// set default cors domain used by startRpc from CLI flag
js.corsDomain = corsDomain
......@@ -84,7 +84,7 @@ func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain string, interactive boo
js.wait = js.xeth.UpdateState()
// update state in separare forever blocks
js.re = re.New(libPath)
js.apiBindings(f)
js.apiBindings(ipcpath, f)
js.adminBindings()
if !liner.TerminalSupported() || !interactive {
......@@ -103,14 +103,15 @@ func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain string, interactive boo
return js
}
func (js *jsre) apiBindings(f xeth.Frontend) {
func (js *jsre) apiBindings(ipcpath string, f xeth.Frontend) {
xe := xeth.New(js.ethereum, f)
ethApi := rpc.NewEthereumApi(xe)
jeth := rpc.NewJeth(ethApi, js.re)
jeth := rpc.NewJeth(ethApi, js.re, ipcpath)
js.re.Set("jeth", struct{}{})
t, _ := js.re.Get("jeth")
jethObj := t.Object()
jethObj.Set("send", jeth.Send)
jethObj.Set("sendAsync", jeth.Send)
......@@ -119,7 +120,7 @@ func (js *jsre) apiBindings(f xeth.Frontend) {
utils.Fatalf("Error loading bignumber.js: %v", err)
}
err = js.re.Compile("ethereum.js", re.Ethereum_JS)
err = js.re.Compile("ethereum.js", re.Web3_JS)
if err != nil {
utils.Fatalf("Error loading ethereum.js: %v", err)
}
......
......@@ -105,7 +105,7 @@ func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) {
t.Errorf("Error creating DocServer: %v", err)
}
tf := &testjethre{ds: ds, stateDb: ethereum.ChainManager().State().Copy()}
repl := newJSRE(ethereum, assetPath, "", false, tf)
repl := newJSRE(ethereum, assetPath, "", "", false, tf)
tf.jsre = repl
return tmp, tf, ethereum
}
......
......@@ -239,6 +239,9 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
utils.RPCEnabledFlag,
utils.RPCListenAddrFlag,
utils.RPCPortFlag,
utils.IPCDisabledFlag,
utils.IPCApiFlag,
utils.IPCPathFlag,
utils.WhisperEnabledFlag,
utils.VMDebugFlag,
utils.ProtocolVersionFlag,
......@@ -305,6 +308,7 @@ func console(ctx *cli.Context) {
ethereum,
ctx.String(utils.JSpathFlag.Name),
ctx.GlobalString(utils.RPCCORSDomainFlag.Name),
ctx.GlobalString(utils.IPCPathFlag.Name),
true,
nil,
)
......@@ -326,6 +330,7 @@ func execJSFiles(ctx *cli.Context) {
ethereum,
ctx.String(utils.JSpathFlag.Name),
ctx.GlobalString(utils.RPCCORSDomainFlag.Name),
ctx.GlobalString(utils.IPCPathFlag.Name),
false,
nil,
)
......@@ -382,6 +387,11 @@ func startEth(ctx *cli.Context, eth *eth.Ethereum) {
}
}
// Start auxiliary services if enabled.
if !ctx.GlobalBool(utils.IPCDisabledFlag.Name) {
if err := utils.StartIPC(eth, ctx); err != nil {
utils.Fatalf("Error string IPC: %v", err)
}
}
if ctx.GlobalBool(utils.RPCEnabledFlag.Name) {
if err := utils.StartRPC(eth, ctx); err != nil {
utils.Fatalf("Error starting RPC: %v", err)
......
......@@ -24,6 +24,9 @@ import (
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/xeth"
"github.com/ethereum/go-ethereum/rpc/api"
"github.com/ethereum/go-ethereum/rpc/comms"
"github.com/ethereum/go-ethereum/rpc/codec"
)
func init() {
......@@ -206,6 +209,20 @@ var (
Usage: "Domain on which to send Access-Control-Allow-Origin header",
Value: "",
}
IPCDisabledFlag = cli.BoolFlag{
Name: "ipcdisable",
Usage: "Disable the IPC-RPC server",
}
IPCApiFlag = cli.StringFlag{
Name: "ipcapi",
Usage: "Specify the API's which are offered over this interface",
Value: api.DefaultIpcApis,
}
IPCPathFlag = DirectoryFlag{
Name: "ipcpath",
Usage: "Filename for IPC socket/pipe",
Value: DirectoryString{common.DefaultIpcPath()},
}
// Network Settings
MaxPeersFlag = cli.IntFlag{
Name: "maxpeers",
......@@ -368,6 +385,22 @@ func MakeAccountManager(ctx *cli.Context) *accounts.Manager {
return accounts.NewManager(ks)
}
func StartIPC(eth *eth.Ethereum, ctx *cli.Context) error {
config := comms.IpcConfig{
Endpoint: ctx.GlobalString(IPCPathFlag.Name),
}
xeth := xeth.New(eth, nil)
codec := codec.JSON
apis, err := api.ParseApiString(ctx.GlobalString(IPCApiFlag.Name), codec, xeth, eth)
if err != nil {
return err
}
return comms.StartIpc(config, codec, apis...)
}
func StartRPC(eth *eth.Ethereum, ctx *cli.Context) error {
config := rpc.RpcConfig{
ListenAddress: ctx.GlobalString(RPCListenAddrFlag.Name),
......
......@@ -94,6 +94,10 @@ func DefaultDataDir() string {
}
}
func DefaultIpcPath() string {
return filepath.Join(DefaultDataDir(), "geth.ipc")
}
func IsWindows() bool {
return runtime.GOOS == "windows"
}
......
This source diff could not be displayed because it is too large. You can view the blob instead.
package api
import (
"fmt"
"io"
"os"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/shared"
"github.com/ethereum/go-ethereum/xeth"
)
const (
AdminApiversion = "1.0"
importBatchSize = 2500
)
var (
// mapping between methods and handlers
AdminMapping = map[string]adminhandler{
// "admin_startRPC": (*adminApi).StartRPC,
// "admin_stopRPC": (*adminApi).StopRPC,
"admin_addPeer": (*adminApi).AddPeer,
"admin_peers": (*adminApi).Peers,
"admin_nodeInfo": (*adminApi).NodeInfo,
"admin_exportChain": (*adminApi).ExportChain,
"admin_importChain": (*adminApi).ImportChain,
"admin_verbosity": (*adminApi).Verbosity,
"admin_chainSyncStatus": (*adminApi).ChainSyncStatus,
"admin_setSolc": (*adminApi).SetSolc,
"admin_datadir": (*adminApi).DataDir,
}
)
// admin callback handler
type adminhandler func(*adminApi, *shared.Request) (interface{}, error)
// admin api provider
type adminApi struct {
xeth *xeth.XEth
ethereum *eth.Ethereum
methods map[string]adminhandler
codec codec.ApiCoder
}
// create a new admin api instance
func NewAdminApi(xeth *xeth.XEth, ethereum *eth.Ethereum, coder codec.Codec) *adminApi {
return &adminApi{
xeth: xeth,
ethereum: ethereum,
methods: AdminMapping,
codec: coder.New(nil),
}
}
// collection with supported methods
func (self *adminApi) 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 *adminApi) 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 *adminApi) Name() string {
return AdminApiName
}
func (self *adminApi) ApiVersion() string {
return AdminApiversion
}
func (self *adminApi) AddPeer(req *shared.Request) (interface{}, error) {
args := new(AddPeerArgs)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
err := self.ethereum.AddPeer(args.Url)
if err == nil {
return true, nil
}
return false, err
}
func (self *adminApi) Peers(req *shared.Request) (interface{}, error) {
return self.ethereum.PeersInfo(), nil
}
func (self *adminApi) StartRPC(req *shared.Request) (interface{}, error) {
return false, nil
// Enable when http rpc interface is refactored to prevent import cycles
// args := new(StartRpcArgs)
// if err := self.codec.Decode(req.Params, &args); err != nil {
// return nil, shared.NewDecodeParamError(err.Error())
// }
//
// cfg := rpc.RpcConfig{
// ListenAddress: args.Address,
// ListenPort: args.Port,
// }
//
// err := rpc.Start(self.xeth, cfg)
// if err == nil {
// return true, nil
// }
// return false, err
}
func (self *adminApi) StopRPC(req *shared.Request) (interface{}, error) {
return false, nil
// Enable when http rpc interface is refactored to prevent import cycles
// rpc.Stop()
// return true, nil
}
func (self *adminApi) NodeInfo(req *shared.Request) (interface{}, error) {
return self.ethereum.NodeInfo(), nil
}
func (self *adminApi) DataDir(req *shared.Request) (interface{}, error) {
return self.ethereum.DataDir, nil
}
func hasAllBlocks(chain *core.ChainManager, bs []*types.Block) bool {
for _, b := range bs {
if !chain.HasBlock(b.Hash()) {
return false
}
}
return true
}
func (self *adminApi) ImportChain(req *shared.Request) (interface{}, error) {
args := new(ImportExportChainArgs)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
fh, err := os.Open(args.Filename)
if err != nil {
return false, err
}
defer fh.Close()
stream := rlp.NewStream(fh, 0)
// Run actual the import.
blocks := make(types.Blocks, importBatchSize)
n := 0
for batch := 0; ; batch++ {
i := 0
for ; i < importBatchSize; i++ {
var b types.Block
if err := stream.Decode(&b); err == io.EOF {
break
} else if err != nil {
return false, fmt.Errorf("at block %d: %v", n, err)
}
blocks[i] = &b
n++
}
if i == 0 {
break
}
// Import the batch.
if hasAllBlocks(self.ethereum.ChainManager(), blocks[:i]) {
continue
}
if _, err := self.ethereum.ChainManager().InsertChain(blocks[:i]); err != nil {
return false, fmt.Errorf("invalid block %d: %v", n, err)
}
}
return true, nil
}
func (self *adminApi) ExportChain(req *shared.Request) (interface{}, error) {
args := new(ImportExportChainArgs)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
fh, err := os.OpenFile(args.Filename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
if err != nil {
return false, err
}
defer fh.Close()
if err := self.ethereum.ChainManager().Export(fh); err != nil {
return false, err
}
return true, nil
}
func (self *adminApi) Verbosity(req *shared.Request) (interface{}, error) {
args := new(VerbosityArgs)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
glog.SetV(args.Level)
return true, nil
}
func (self *adminApi) ChainSyncStatus(req *shared.Request) (interface{}, error) {
pending, cached, importing, estimate := self.ethereum.Downloader().Stats()
return map[string]interface{}{
"blocksAvailable": pending,
"blocksWaitingForImport": cached,
"importing": importing,
"estimate": estimate.String(),
}, nil
}
func (self *adminApi) SetSolc(req *shared.Request) (interface{}, error) {
args := new(SetSolcArgs)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
solc, err := self.xeth.SetSolc(args.Path)
if err != nil {
return nil, err
}
return solc.Info(), nil
}
package api
import (
"encoding/json"
"github.com/ethereum/go-ethereum/rpc/shared"
)
type AddPeerArgs struct {
Url string
}
func (args *AddPeerArgs) 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.NewDecodeParamError("Expected enode as argument")
}
urlstr, ok := obj[0].(string)
if !ok {
return shared.NewInvalidTypeError("url", "not a string")
}
args.Url = urlstr
return nil
}
type ImportExportChainArgs struct {
Filename string
}
func (args *ImportExportChainArgs) 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.NewDecodeParamError("Expected filename as argument")
}
filename, ok := obj[0].(string)
if !ok {
return shared.NewInvalidTypeError("filename", "not a string")
}
args.Filename = filename
return nil
}
type VerbosityArgs struct {
Level int
}
func (args *VerbosityArgs) 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.NewDecodeParamError("Expected enode as argument")
}
level, err := numString(obj[0])
if err == nil {
args.Level = int(level.Int64())
}
return nil
}
type SetSolcArgs struct {
Path string
}
func (args *SetSolcArgs) 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.NewDecodeParamError("Expected path as argument")
}
if pathstr, ok := obj[0].(string); ok {
args.Path = pathstr
return nil
}
return shared.NewInvalidTypeError("path", "not a string")
}
package api
const Admin_JS = `
web3._extend({
property: 'admin',
methods:
[
new web3._extend.Method({
name: 'addPeer',
call: 'admin_addPeer',
params: 1,
inputFormatter: [web3._extend.utils.formatInputString],
outputFormatter: web3._extend.formatters.formatOutputBool
}),
new web3._extend.Method({
name: 'exportChain',
call: 'admin_exportChain',
params: 1,
inputFormatter: [web3._extend.utils.formatInputString],
outputFormatter: function(obj) { return obj; }
}),
new web3._extend.Method({
name: 'importChain',
call: 'admin_importChain',
params: 1,
inputFormatter: [web3._extend.utils.formatInputString],
outputFormatter: function(obj) { return obj; }
}),
new web3._extend.Method({
name: 'verbosity',
call: 'admin_verbosity',
params: 1,
inputFormatter: [web3._extend.utils.formatInputInt],
outputFormatter: web3._extend.formatters.formatOutputBool
}),
new web3._extend.Method({
name: 'setSolc',
call: 'admin_setSolc',
params: 1,
inputFormatter: [web3._extend.utils.formatInputString],
outputFormatter: web3._extend.formatters.formatOutputString
})
],
properties:
[
new web3._extend.Property({
name: 'nodeInfo',
getter: 'admin_nodeInfo',
outputFormatter: web3._extend.formatters.formatOutputString
}),
new web3._extend.Property({
name: 'peers',
getter: 'admin_peers',
outputFormatter: function(obj) { return obj; }
}),
new web3._extend.Property({
name: 'datadir',
getter: 'admin_datadir',
outputFormatter: web3._extend.formatters.formatOutputString
}),
new web3._extend.Property({
name: 'chainSyncStatus',
getter: 'admin_chainSyncStatus',
outputFormatter: function(obj) { return obj; }
})
]
});
`
package api
import (
"strings"
"github.com/ethereum/go-ethereum/rpc/shared"
)
const (
AdminApiName = "admin"
EthApiName = "eth"
DebugApiName = "debug"
MergedApiName = "merged"
MinerApiName = "miner"
NetApiName = "net"
ShhApiName = "shh"
TxPoolApiName = "txpool"
PersonalApiName = "personal"
Web3ApiName = "web3"
)
var (
// List with all API's which are offered over the IPC interface by default
DefaultIpcApis = strings.Join([]string{
AdminApiName, EthApiName, DebugApiName, MinerApiName, NetApiName,
ShhApiName, TxPoolApiName, PersonalApiName, Web3ApiName,
}, ",")
)
// Ethereum RPC API interface
type EthereumApi interface {
// API identifier
Name() string
// API version
ApiVersion() string
// Execute the given request and returns the response or an error
Execute(*shared.Request) (interface{}, error)
// List of supported RCP methods this API provides
Methods() []string
}
// Merge multiple API's to a single API instance
func Merge(apis ...EthereumApi) EthereumApi {
return newMergedApi(apis...)
}
package api
import (
"testing"
"github.com/ethereum/go-ethereum/rpc/codec"
)
func TestParseApiString(t *testing.T) {
apis, err := ParseApiString("", codec.JSON, nil, nil)
if err == nil {
t.Errorf("Expected an err from parsing empty API string but got nil")
}
if len(apis) != 0 {
t.Errorf("Expected 0 apis from empty API string")
}
apis, err = ParseApiString("eth", codec.JSON, nil, nil)
if err != nil {
t.Errorf("Expected nil err from parsing empty API string but got %v", err)
}
if len(apis) != 1 {
t.Errorf("Expected 1 apis but got %d - %v", apis, apis)
}
apis, err = ParseApiString("eth,eth", codec.JSON, nil, nil)
if err != nil {
t.Errorf("Expected nil err from parsing empty API string but got \"%v\"", err)
}
if len(apis) != 2 {
t.Errorf("Expected 2 apis but got %d - %v", apis, apis)
}
apis, err = ParseApiString("eth,invalid", codec.JSON, nil, nil)
if err == nil {
t.Errorf("Expected an err but got no err")
}
}
package api
import (
"fmt"
"github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/shared"
"github.com/ethereum/go-ethereum/xeth"
)
const (
DebugApiVersion = "1.0"
)
var (
// mapping between methods and handlers
DebugMapping = map[string]debughandler{
"debug_dumpBlock": (*debugApi).DumpBlock,
"debug_getBlockRlp": (*debugApi).GetBlockRlp,
"debug_printBlock": (*debugApi).PrintBlock,
"debug_processBlock": (*debugApi).ProcessBlock,
"debug_seedHash": (*debugApi).SeedHash,
"debug_setHead": (*debugApi).SetHead,
}
)
// debug callback handler
type debughandler func(*debugApi, *shared.Request) (interface{}, error)
// admin api provider
type debugApi struct {
xeth *xeth.XEth
ethereum *eth.Ethereum
methods map[string]debughandler
codec codec.ApiCoder
}
// create a new debug api instance
func NewDebugApi(xeth *xeth.XEth, ethereum *eth.Ethereum, coder codec.Codec) *debugApi {
return &debugApi{
xeth: xeth,
ethereum: ethereum,
methods: DebugMapping,
codec: coder.New(nil),
}
}
// collection with supported methods
func (self *debugApi) 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 *debugApi) 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 *debugApi) Name() string {
return DebugApiName
}
func (self *debugApi) ApiVersion() string {
return DebugApiVersion
}
func (self *debugApi) PrintBlock(req *shared.Request) (interface{}, error) {
args := new(BlockNumArg)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
block := self.xeth.EthBlockByNumber(args.BlockNumber)
return fmt.Sprintf("%s", block), nil
}
func (self *debugApi) DumpBlock(req *shared.Request) (interface{}, error) {
args := new(BlockNumArg)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
block := self.xeth.EthBlockByNumber(args.BlockNumber)
if block == nil {
return nil, fmt.Errorf("block #%d not found", args.BlockNumber)
}
stateDb := state.New(block.Root(), self.ethereum.StateDb())
if stateDb == nil {
return nil, nil
}
return stateDb.RawDump(), nil
}
func (self *debugApi) GetBlockRlp(req *shared.Request) (interface{}, error) {
args := new(BlockNumArg)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
block := self.xeth.EthBlockByNumber(args.BlockNumber)
if block == nil {
return nil, fmt.Errorf("block #%d not found", args.BlockNumber)
}
encoded, err := rlp.EncodeToBytes(block)
return fmt.Sprintf("%x", encoded), err
}
func (self *debugApi) SetHead(req *shared.Request) (interface{}, error) {
args := new(BlockNumArg)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
block := self.xeth.EthBlockByNumber(args.BlockNumber)
if block == nil {
return nil, fmt.Errorf("block #%d not found", args.BlockNumber)
}
self.ethereum.ChainManager().SetHead(block)
return nil, nil
}
func (self *debugApi) ProcessBlock(req *shared.Request) (interface{}, error) {
args := new(BlockNumArg)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
block := self.xeth.EthBlockByNumber(args.BlockNumber)
if block == nil {
return nil, fmt.Errorf("block #%d not found", args.BlockNumber)
}
old := vm.Debug
defer func() { vm.Debug = old }()
vm.Debug = true
_, err := self.ethereum.BlockProcessor().RetryProcess(block)
if err == nil {
return true, nil
}
return false, err
}
func (self *debugApi) SeedHash(req *shared.Request) (interface{}, error) {
args := new(BlockNumArg)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
if hash, err := ethash.GetSeedHash(uint64(args.BlockNumber)); err == nil {
return fmt.Sprintf("0x%x", hash), nil
} else {
return nil, err
}
}
package api
import (
"encoding/json"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/rpc/shared"
)
type WaitForBlockArgs struct {
MinHeight int
Timeout int // in seconds
}
func (args *WaitForBlockArgs) 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 fmt.Errorf("waitForArgs needs 0, 1, 2 arguments")
}
// default values when not provided
args.MinHeight = -1
args.Timeout = -1
if len(obj) >= 1 {
var minHeight *big.Int
if minHeight, err = numString(obj[0]); err != nil {
return err
}
args.MinHeight = int(minHeight.Int64())
}
if len(obj) >= 2 {
timeout, err := numString(obj[1])
if err != nil {
return err
}
args.Timeout = int(timeout.Int64())
}
return nil
}
package api
const Debug_JS = `
web3._extend({
property: 'debug',
methods:
[
new web3._extend.Method({
name: 'printBlock',
call: 'debug_printBlock',
params: 1,
inputFormatter: [web3._extend.formatters.formatInputInt],
outputFormatter: web3._extend.formatters.formatOutputString
}),
new web3._extend.Method({
name: 'getBlockRlp',
call: 'debug_getBlockRlp',
params: 1,
inputFormatter: [web3._extend.formatters.formatInputInt],
outputFormatter: web3._extend.formatters.formatOutputString
}),
new web3._extend.Method({
name: 'setHead',
call: 'debug_setHead',
params: 1,
inputFormatter: [web3._extend.formatters.formatInputInt],
outputFormatter: web3._extend.formatters.formatOutputBool
}),
new web3._extend.Method({
name: 'processBlock',
call: 'debug_processBlock',
params: 1,
inputFormatter: [web3._extend.formatters.formatInputInt],
outputFormatter: function(obj) { return obj; }
}),
new web3._extend.Method({
name: 'seedHash',
call: 'debug_seedHash',
params: 1,
inputFormatter: [web3._extend.formatters.formatInputInt],
outputFormatter: web3._extend.formatters.formatOutputString
}) ,
new web3._extend.Method({
name: 'dumpBlock',
call: 'debug_dumpBlock',
params: 1,
inputFormatter: [web3._extend.formatters.formatInputInt],
outputFormatter: function(obj) { return obj; }
})
],
properties:
[
]
});
`
This diff is collapsed.
This diff is collapsed.
package api
// JS api provided by web3.js
package api
import (
"github.com/ethereum/go-ethereum/rpc/shared"
)
const (
MergedApiVersion = "1.0"
)
// combines multiple API's
type MergedApi struct {
apis map[string]string
methods map[string]EthereumApi
}
// create new merged api instance
func newMergedApi(apis ...EthereumApi) *MergedApi {
mergedApi := new(MergedApi)
mergedApi.apis = make(map[string]string, len(apis))
mergedApi.methods = make(map[string]EthereumApi)
for _, api := range apis {
mergedApi.apis[api.Name()] = api.ApiVersion()
for _, method := range api.Methods() {
mergedApi.methods[method] = api
}
}
return mergedApi
}
// Supported RPC methods
func (self *MergedApi) Methods() []string {
all := make([]string, len(self.methods))
for method, _ := range self.methods {
all = append(all, method)
}
return all
}
// Call the correct API's Execute method for the given request
func (self *MergedApi) Execute(req *shared.Request) (interface{}, error) {
if res, _ := self.handle(req); res != nil {
return res, nil
}
if api, found := self.methods[req.Method]; found {
return api.Execute(req)
}
return nil, shared.NewNotImplementedError(req.Method)
}
func (self *MergedApi) Name() string {
return MergedApiName
}
func (self *MergedApi) ApiVersion() string {
return MergedApiVersion
}
func (self *MergedApi) handle(req *shared.Request) (interface{}, error) {
if req.Method == "modules" { // provided API's
return self.apis, nil
}
return nil, nil
}
package api
import (
"github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/shared"
)
const (
MinerApiVersion = "1.0"
)
var (
// mapping between methods and handlers
MinerMapping = map[string]minerhandler{
"miner_hashrate": (*minerApi).Hashrate,
"miner_makeDAG": (*minerApi).MakeDAG,
"miner_setExtra": (*minerApi).SetExtra,
"miner_setGasPrice": (*minerApi).SetGasPrice,
"miner_startAutoDAG": (*minerApi).StartAutoDAG,
"miner_start": (*minerApi).StartMiner,
"miner_stopAutoDAG": (*minerApi).StopAutoDAG,
"miner_stop": (*minerApi).StopMiner,
}
)
// miner callback handler
type minerhandler func(*minerApi, *shared.Request) (interface{}, error)
// miner api provider
type minerApi struct {
ethereum *eth.Ethereum
methods map[string]minerhandler
codec codec.ApiCoder
}
// create a new miner api instance
func NewMinerApi(ethereum *eth.Ethereum, coder codec.Codec) *minerApi {
return &minerApi{
ethereum: ethereum,
methods: MinerMapping,
codec: coder.New(nil),
}
}
// Execute given request
func (self *minerApi) Execute(req *shared.Request) (interface{}, error) {
if callback, ok := self.methods[req.Method]; ok {
return callback(self, req)
}
return nil, &shared.NotImplementedError{req.Method}
}
// collection with supported methods
func (self *minerApi) Methods() []string {
methods := make([]string, len(self.methods))
i := 0
for k := range self.methods {
methods[i] = k
i++
}
return methods
}
func (self *minerApi) Name() string {
return MinerApiName
}
func (self *minerApi) ApiVersion() string {
return MinerApiVersion
}
func (self *minerApi) StartMiner(req *shared.Request) (interface{}, error) {
args := new(StartMinerArgs)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, err
}
if args.Threads == -1 { // (not specified by user, use default)
args.Threads = self.ethereum.MinerThreads
}
self.ethereum.StartAutoDAG()
err := self.ethereum.StartMining(args.Threads)
if err == nil {
return true, nil
}
return false, err
}
func (self *minerApi) StopMiner(req *shared.Request) (interface{}, error) {
self.ethereum.StopMining()
return true, nil
}
func (self *minerApi) Hashrate(req *shared.Request) (interface{}, error) {
return self.ethereum.Miner().HashRate(), nil
}
func (self *minerApi) SetExtra(req *shared.Request) (interface{}, error) {
args := new(SetExtraArgs)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, err
}
self.ethereum.Miner().SetExtra([]byte(args.Data))
return true, nil
}
func (self *minerApi) SetGasPrice(req *shared.Request) (interface{}, error) {
args := new(GasPriceArgs)
if err := self.codec.Decode(req.Params, &args); err != nil {
return false, err
}
self.ethereum.Miner().SetGasPrice(common.String2Big(args.Price))
return true, nil
}
func (self *minerApi) StartAutoDAG(req *shared.Request) (interface{}, error) {
self.ethereum.StartAutoDAG()
return true, nil
}
func (self *minerApi) StopAutoDAG(req *shared.Request) (interface{}, error) {
self.ethereum.StopAutoDAG()
return true, nil
}
func (self *minerApi) MakeDAG(req *shared.Request) (interface{}, error) {
args := new(MakeDAGArgs)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, err
}
if args.BlockNumber < 0 {
return false, shared.NewValidationError("BlockNumber", "BlockNumber must be positive")
}
err := ethash.MakeDAG(uint64(args.BlockNumber), "")
if err == nil {
return true, nil
}
return false, err
}
package api
import (
"encoding/json"
"math/big"
"github.com/ethereum/go-ethereum/rpc/shared"
)
type StartMinerArgs struct {
Threads int
}
func (args *StartMinerArgs) UnmarshalJSON(b []byte) (err error) {
var obj []interface{}
if err := json.Unmarshal(b, &obj); err != nil {
return shared.NewDecodeParamError(err.Error())
}
if len(obj) == 0 || obj[0] == nil {
args.Threads = -1
return nil
}
var num *big.Int
if num, err = numString(obj[0]); err != nil {
return err
}
args.Threads = int(num.Int64())
return nil
}
type SetExtraArgs struct {
Data string
}
func (args *SetExtraArgs) 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)
}
extrastr, ok := obj[0].(string)
if !ok {
return shared.NewInvalidTypeError("Price", "not a string")
}
args.Data = extrastr
return nil
}
type GasPriceArgs struct {
Price string
}
func (args *GasPriceArgs) 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)
}
if pricestr, ok := obj[0].(string); ok {
args.Price = pricestr
return nil
}
return shared.NewInvalidTypeError("Price", "not a string")
}
type MakeDAGArgs struct {
BlockNumber int64
}
func (args *MakeDAGArgs) UnmarshalJSON(b []byte) (err error) {
args.BlockNumber = -1
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)
}
if err := blockHeight(obj[0], &args.BlockNumber); err != nil {
return err
}
return nil
}
package api
const Miner_JS = `
web3._extend({
property: 'miner',
methods:
[
new web3._extend.Method({
name: 'start',
call: 'miner_start',
params: 1,
inputFormatter: [web3._extend.formatters.formatInputInt],
outputFormatter: web3._extend.formatters.formatOutputBool
}),
new web3._extend.Method({
name: 'stop',
call: 'miner_stop',
params: 1,
inputFormatter: [web3._extend.formatters.formatInputInt],
outputFormatter: web3._extend.formatters.formatOutputBool
}),
new web3._extend.Method({
name: 'setExtra',
call: 'miner_setExtra',
params: 1,
inputFormatter: [web3._extend.utils.formatInputString],
outputFormatter: web3._extend.formatters.formatOutputBool
}),
new web3._extend.Method({
name: 'setGasPrice',
call: 'miner_setGasPrice',
params: 1,
inputFormatter: [web3._extend.utils.formatInputString],
outputFormatter: web3._extend.formatters.formatOutputBool
}),
new web3._extend.Method({
name: 'startAutoDAG',
call: 'miner_startAutoDAG',
params: 0,
inputFormatter: [],
outputFormatter: web3._extend.formatters.formatOutputBool
}),
new web3._extend.Method({
name: 'stopAutoDAG',
call: 'miner_stopAutoDAG',
params: 0,
inputFormatter: [],
outputFormatter: web3._extend.formatters.formatOutputBool
}),
new web3._extend.Method({
name: 'makeDAG',
call: 'miner_makeDAG',
params: 1,
inputFormatter: [web3._extend.formatters.inputDefaultBlockNumberFormatter],
outputFormatter: web3._extend.formatters.formatOutputBool
})
],
properties:
[
new web3._extend.Property({
name: 'hashrate',
getter: 'miner_hashrate',
outputFormatter: web3._extend.utils.toDecimal
})
]
});
`
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 (
NetApiVersion = "1.0"
)
var (
// mapping between methods and handlers
netMapping = map[string]nethandler{
"net_version": (*netApi).Version,
"net_peerCount": (*netApi).PeerCount,
"net_listening": (*netApi).IsListening,
"net_peers": (*netApi).Peers,
}
)
// net callback handler
type nethandler func(*netApi, *shared.Request) (interface{}, error)
// net api provider
type netApi struct {
xeth *xeth.XEth
ethereum *eth.Ethereum
methods map[string]nethandler
codec codec.ApiCoder
}
// create a new net api instance
func NewNetApi(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *netApi {
return &netApi{
xeth: xeth,
ethereum: eth,
methods: netMapping,
codec: coder.New(nil),
}
}
// collection with supported methods
func (self *netApi) 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 *netApi) Execute(req *shared.Request) (interface{}, error) {
if callback, ok := self.methods[req.Method]; ok {
return callback(self, req)
}
return nil, shared.NewNotImplementedError(req.Method)
}
func (self *netApi) Name() string {
return NetApiName
}
func (self *netApi) ApiVersion() string {
return NetApiVersion
}
// Network version
func (self *netApi) Version(req *shared.Request) (interface{}, error) {
return self.xeth.NetworkVersion(), nil
}
// Number of connected peers
func (self *netApi) PeerCount(req *shared.Request) (interface{}, error) {
return self.xeth.PeerCount(), nil
}
func (self *netApi) IsListening(req *shared.Request) (interface{}, error) {
return self.xeth.IsListening(), nil
}
func (self *netApi) Peers(req *shared.Request) (interface{}, error) {
return self.ethereum.PeersInfo(), nil
}
package api
const Net_JS = `
web3._extend({
property: 'network',
methods:
[
new web3._extend.Method({
name: 'addPeer',
call: 'net_addPeer',
params: 1,
inputFormatter: [web3._extend.utils.formatInputString],
outputFormatter: web3._extend.formatters.formatOutputBool
}),
new web3._extend.Method({
name: 'getPeerCount',
call: 'net_peerCount',
params: 0,
inputFormatter: [],
outputFormatter: web3._extend.formatters.formatOutputString
})
],
properties:
[
new web3._extend.Property({
name: 'listening',
getter: 'net_listening',
outputFormatter: web3._extend.formatters.formatOutputBool
}),
new web3._extend.Property({
name: 'peerCount',
getter: 'net_peerCount',
outputFormatter: web3._extend.utils.toDecimal
}),
new web3._extend.Property({
name: 'peers',
getter: 'net_peers',
outputFormatter: function(obj) { return obj; }
}),
new web3._extend.Property({
name: 'version',
getter: 'net_version',
outputFormatter: web3._extend.formatters.formatOutputString
})
]
});
`
This diff is collapsed.
package api
import (
"time"
"github.com/ethereum/go-ethereum/common"
"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 (
PersonalApiVersion = "1.0"
)
var (
// mapping between methods and handlers
personalMapping = map[string]personalhandler{
"personal_listAccounts": (*personalApi).ListAccounts,
"personal_newAccount": (*personalApi).NewAccount,
"personal_deleteAccount": (*personalApi).DeleteAccount,
"personal_unlockAccount": (*personalApi).UnlockAccount,
}
)
// net callback handler
type personalhandler func(*personalApi, *shared.Request) (interface{}, error)
// net api provider
type personalApi struct {
xeth *xeth.XEth
ethereum *eth.Ethereum
methods map[string]personalhandler
codec codec.ApiCoder
}
// create a new net api instance
func NewPersonalApi(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *personalApi {
return &personalApi{
xeth: xeth,
ethereum: eth,
methods: personalMapping,
codec: coder.New(nil),
}
}
// collection with supported methods
func (self *personalApi) 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 *personalApi) Execute(req *shared.Request) (interface{}, error) {
if callback, ok := self.methods[req.Method]; ok {
return callback(self, req)
}
return nil, shared.NewNotImplementedError(req.Method)
}
func (self *personalApi) Name() string {
return PersonalApiName
}
func (self *personalApi) ApiVersion() string {
return PersonalApiVersion
}
func (self *personalApi) ListAccounts(req *shared.Request) (interface{}, error) {
return self.xeth.Accounts(), nil
}
func (self *personalApi) NewAccount(req *shared.Request) (interface{}, error) {
args := new(NewAccountArgs)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
am := self.ethereum.AccountManager()
acc, err := am.NewAccount(args.Passphrase)
return acc.Address.Hex(), err
}
func (self *personalApi) DeleteAccount(req *shared.Request) (interface{}, error) {
args := new(DeleteAccountArgs)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
addr := common.HexToAddress(args.Address)
am := self.ethereum.AccountManager()
if err := am.DeleteAccount(addr, args.Passphrase); err == nil {
return true, nil
} else {
return false, err
}
}
func (self *personalApi) UnlockAccount(req *shared.Request) (interface{}, error) {
args := new(UnlockAccountArgs)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
var err error
am := self.ethereum.AccountManager()
addr := common.HexToAddress(args.Address)
if args.Duration == -1 {
err = am.Unlock(addr, args.Passphrase)
} else {
err = am.TimedUnlock(addr, args.Passphrase, time.Duration(args.Duration)*time.Second)
}
if err == nil {
return true, nil
}
return false, err
}
package api
import (
"encoding/json"
"github.com/ethereum/go-ethereum/rpc/shared"
)
type NewAccountArgs struct {
Passphrase string
}
func (args *NewAccountArgs) 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)
}
if passhrase, ok := obj[0].(string); ok {
args.Passphrase = passhrase
return nil
}
return shared.NewInvalidTypeError("passhrase", "not a string")
}
type DeleteAccountArgs struct {
Address string
Passphrase string
}
func (args *DeleteAccountArgs) 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)
}
if addr, ok := obj[0].(string); ok {
args.Address = addr
} else {
return shared.NewInvalidTypeError("address", "not a string")
}
if passhrase, ok := obj[1].(string); ok {
args.Passphrase = passhrase
} else {
return shared.NewInvalidTypeError("passhrase", "not a string")
}
return nil
}
type UnlockAccountArgs struct {
Address string
Passphrase string
Duration int
}
func (args *UnlockAccountArgs) UnmarshalJSON(b []byte) (err error) {
var obj []interface{}
if err := json.Unmarshal(b, &obj); err != nil {
return shared.NewDecodeParamError(err.Error())
}
args.Duration = -1
if len(obj) < 2 {
return shared.NewInsufficientParamsError(len(obj), 2)
}
if addrstr, ok := obj[0].(string); ok {
args.Address = addrstr
} else {
return shared.NewInvalidTypeError("address", "not a string")
}
if passphrasestr, ok := obj[1].(string); ok {
args.Passphrase = passphrasestr
} else {
return shared.NewInvalidTypeError("passphrase", "not a string")
}
return nil
}
package api
const Personal_JS = `
web3._extend({
property: 'personal',
methods:
[
new web3._extend.Method({
name: 'newAccount',
call: 'personal_newAccount',
params: 1,
inputFormatter: [web3._extend.formatters.formatInputString],
outputFormatter: web3._extend.formatters.formatOutputString
}),
new web3._extend.Method({
name: 'unlockAccount',
call: 'personal_unlockAccount',
params: 3,
inputFormatter: [web3._extend.formatters.formatInputString,web3._extend.formatters.formatInputString,web3._extend.formatters.formatInputInt],
outputFormatter: web3._extend.formatters.formatOutputBool
})
],
properties:
[
new web3._extend.Property({
name: 'listAccounts',
getter: 'personal_listAccounts',
outputFormatter: function(obj) { return obj; }
})
]
});
`
package api
import (
"math/big"
"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 (
ShhApiVersion = "1.0"
)
var (
// mapping between methods and handlers
shhMapping = map[string]shhhandler{
"shh_version": (*shhApi).Version,
"shh_post": (*shhApi).Post,
"shh_hasIdentity": (*shhApi).HasIdentity,
"shh_newIdentity": (*shhApi).NewIdentity,
"shh_newFilter": (*shhApi).NewFilter,
"shh_uninstallFilter": (*shhApi).UninstallFilter,
"shh_getFilterChanges": (*shhApi).GetFilterChanges,
}
)
func newWhisperOfflineError(method string) error {
return shared.NewNotAvailableError(method, "whisper offline")
}
// net callback handler
type shhhandler func(*shhApi, *shared.Request) (interface{}, error)
// shh api provider
type shhApi struct {
xeth *xeth.XEth
ethereum *eth.Ethereum
methods map[string]shhhandler
codec codec.ApiCoder
}
// create a new whisper api instance
func NewShhApi(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *shhApi {
return &shhApi{
xeth: xeth,
ethereum: eth,
methods: shhMapping,
codec: coder.New(nil),
}
}
// collection with supported methods
func (self *shhApi) 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 *shhApi) Execute(req *shared.Request) (interface{}, error) {
if callback, ok := self.methods[req.Method]; ok {
return callback(self, req)
}
return nil, shared.NewNotImplementedError(req.Method)
}
func (self *shhApi) Name() string {
return ShhApiName
}
func (self *shhApi) ApiVersion() string {
return ShhApiVersion
}
func (self *shhApi) Version(req *shared.Request) (interface{}, error) {
w := self.xeth.Whisper()
if w == nil {
return nil, newWhisperOfflineError(req.Method)
}
return w.Version(), nil
}
func (self *shhApi) Post(req *shared.Request) (interface{}, error) {
w := self.xeth.Whisper()
if w == nil {
return nil, newWhisperOfflineError(req.Method)
}
args := new(WhisperMessageArgs)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, err
}
err := w.Post(args.Payload, args.To, args.From, args.Topics, args.Priority, args.Ttl)
if err != nil {
return false, err
}
return true, nil
}
func (self *shhApi) HasIdentity(req *shared.Request) (interface{}, error) {
w := self.xeth.Whisper()
if w == nil {
return nil, newWhisperOfflineError(req.Method)
}
args := new(WhisperIdentityArgs)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, err
}
return w.HasIdentity(args.Identity), nil
}
func (self *shhApi) NewIdentity(req *shared.Request) (interface{}, error) {
w := self.xeth.Whisper()
if w == nil {
return nil, newWhisperOfflineError(req.Method)
}
return w.NewIdentity(), nil
}
func (self *shhApi) NewFilter(req *shared.Request) (interface{}, error) {
args := new(WhisperFilterArgs)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, err
}
id := self.xeth.NewWhisperFilter(args.To, args.From, args.Topics)
return newHexNum(big.NewInt(int64(id)).Bytes()), nil
}
func (self *shhApi) UninstallFilter(req *shared.Request) (interface{}, error) {
args := new(FilterIdArgs)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, err
}
return self.xeth.UninstallWhisperFilter(args.Id), nil
}
func (self *shhApi) GetFilterChanges(req *shared.Request) (interface{}, error) {
w := self.xeth.Whisper()
if w == nil {
return nil, newWhisperOfflineError(req.Method)
}
// Retrieve all the new messages arrived since the last request
args := new(FilterIdArgs)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, err
}
return self.xeth.WhisperMessagesChanged(args.Id), nil
}
func (self *shhApi) GetMessages(req *shared.Request) (interface{}, error) {
w := self.xeth.Whisper()
if w == nil {
return nil, newWhisperOfflineError(req.Method)
}
// Retrieve all the cached messages matching a specific, existing filter
args := new(FilterIdArgs)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, err
}
return self.xeth.WhisperMessages(args.Id), nil
}
package api
import (
"encoding/json"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/rpc/shared"
)
type WhisperMessageArgs struct {
Payload string
To string
From string
Topics []string
Priority uint32
Ttl uint32
}
func (args *WhisperMessageArgs) UnmarshalJSON(b []byte) (err error) {
var obj []struct {
Payload string
To string
From string
Topics []string
Priority interface{}
Ttl interface{}
}
if err = json.Unmarshal(b, &obj); err != nil {
return shared.NewDecodeParamError(err.Error())
}
if len(obj) < 1 {
return shared.NewInsufficientParamsError(len(obj), 1)
}
args.Payload = obj[0].Payload
args.To = obj[0].To
args.From = obj[0].From
args.Topics = obj[0].Topics
var num *big.Int
if num, err = numString(obj[0].Priority); err != nil {
return err
}
args.Priority = uint32(num.Int64())
if num, err = numString(obj[0].Ttl); err != nil {
return err
}
args.Ttl = uint32(num.Int64())
return nil
}
type WhisperIdentityArgs struct {
Identity string
}
func (args *WhisperIdentityArgs) 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", "not a string")
}
args.Identity = argstr
return nil
}
type WhisperFilterArgs struct {
To string
From string
Topics [][]string
}
// UnmarshalJSON implements the json.Unmarshaler interface, invoked to convert a
// JSON message blob into a WhisperFilterArgs structure.
func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) {
// Unmarshal the JSON message and sanity check
var obj []struct {
To interface{} `json:"to"`
From interface{} `json:"from"`
Topics interface{} `json:"topics"`
}
if err := json.Unmarshal(b, &obj); err != nil {
return shared.NewDecodeParamError(err.Error())
}
if len(obj) < 1 {
return shared.NewInsufficientParamsError(len(obj), 1)
}
// Retrieve the simple data contents of the filter arguments
if obj[0].To == nil {
args.To = ""
} else {
argstr, ok := obj[0].To.(string)
if !ok {
return shared.NewInvalidTypeError("to", "is not a string")
}
args.To = argstr
}
if obj[0].From == nil {
args.From = ""
} else {
argstr, ok := obj[0].From.(string)
if !ok {
return shared.NewInvalidTypeError("from", "is not a string")
}
args.From = argstr
}
// Construct the nested topic array
if obj[0].Topics != nil {
// Make sure we have an actual topic array
list, ok := obj[0].Topics.([]interface{})
if !ok {
return shared.NewInvalidTypeError("topics", "is not an array")
}
// Iterate over each topic and handle nil, string or array
topics := make([][]string, len(list))
for idx, field := range list {
switch value := field.(type) {
case nil:
topics[idx] = []string{}
case string:
topics[idx] = []string{value}
case []interface{}:
topics[idx] = make([]string, len(value))
for i, nested := range value {
switch value := nested.(type) {
case nil:
topics[idx][i] = ""
case string:
topics[idx][i] = value
default:
return shared.NewInvalidTypeError(fmt.Sprintf("topic[%d][%d]", idx, i), "is not a string")
}
}
default:
return shared.NewInvalidTypeError(fmt.Sprintf("topic[%d]", idx), "not a string or array")
}
}
args.Topics = topics
}
return nil
}
package api
const Shh_JS = `
web3._extend({
property: 'shh',
methods:
[
new web3._extend.Method({
name: 'post',
call: 'shh_post',
params: 6,
inputFormatter: [web3._extend.formatters.formatInputString,
web3._extend.formatters.formatInputString,
web3._extend.formatters.formatInputString,
,
, web3._extend.formatters.formatInputInt
, web3._extend.formatters.formatInputInt],
outputFormatter: web3._extend.formatters.formatOutputBool
}),
],
properties:
[
new web3._extend.Property({
name: 'version',
getter: 'shh_version',
outputFormatter: web3._extend.formatters.formatOutputInt
})
]
});
`
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 (
TxPoolApiVersion = "1.0"
)
var (
// mapping between methods and handlers
txpoolMapping = map[string]txpoolhandler{
"txpool_status": (*txPoolApi).Status,
}
)
// net callback handler
type txpoolhandler func(*txPoolApi, *shared.Request) (interface{}, error)
// txpool api provider
type txPoolApi struct {
xeth *xeth.XEth
ethereum *eth.Ethereum
methods map[string]txpoolhandler
codec codec.ApiCoder
}
// create a new txpool api instance
func NewTxPoolApi(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *txPoolApi {
return &txPoolApi{
xeth: xeth,
ethereum: eth,
methods: txpoolMapping,
codec: coder.New(nil),
}
}
// collection with supported methods
func (self *txPoolApi) 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 *txPoolApi) Execute(req *shared.Request) (interface{}, error) {
if callback, ok := self.methods[req.Method]; ok {
return callback(self, req)
}
return nil, shared.NewNotImplementedError(req.Method)
}
func (self *txPoolApi) Name() string {
return TxPoolApiName
}
func (self *txPoolApi) ApiVersion() string {
return TxPoolApiVersion
}
func (self *txPoolApi) Status(req *shared.Request) (interface{}, error) {
return map[string]int{
"pending": self.ethereum.TxPool().GetTransactions().Len(),
"queued": self.ethereum.TxPool().GetQueuedTransactions().Len(),
}, nil
}
package api
const TxPool_JS = `
web3._extend({
property: 'txpool',
methods:
[
],
properties:
[
new web3._extend.Property({
name: 'status',
getter: 'txpool_status',
outputFormatter: function(obj) { return obj; }
})
]
});
`
package api
import (
"strings"
"fmt"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/xeth"
)
var (
// Mapping between the different methods each api supports
AutoCompletion = map[string][]string{
"admin": []string{
"addPeer",
"peers",
"nodeInfo",
"exportChain",
"importChain",
"verbosity",
"chainSyncStatus",
"setSolc",
"datadir",
},
"debug": []string{
"dumpBlock",
"getBlockRlp",
"printBlock",
"processBlock",
"seedHash",
"setHead",
},
"eth": []string{
"accounts",
"blockNumber",
"getBalance",
"protocolVersion",
"coinbase",
"mining",
"gasPrice",
"getStorage",
"storageAt",
"getStorageAt",
"getTransactionCount",
"getBlockTransactionCountByHash",
"getBlockTransactionCountByNumber",
"getUncleCountByBlockHash",
"getUncleCountByBlockNumber",
"getData",
"getCode",
"sign",
"sendTransaction",
"transact",
"estimateGas",
"call",
"flush",
"getBlockByHash",
"getBlockByNumber",
"getTransactionByHash",
"getTransactionByBlockHashAndIndex",
"getUncleByBlockHashAndIndex",
"getUncleByBlockNumberAndIndex",
"getCompilers",
"compileSolidity",
"newFilter",
"newBlockFilter",
"newPendingTransactionFilter",
"uninstallFilter",
"getFilterChanges",
"getFilterLogs",
"getLogs",
"hashrate",
"getWork",
"submitWork",
},
"miner": []string{
"hashrate",
"makeDAG",
"setExtra",
"setGasPrice",
"startAutoDAG",
"start",
"stopAutoDAG",
"stop",
},
"net": []string{
"peerCount",
"listening",
},
"personal": []string{
"listAccounts",
"newAccount",
"deleteAccount",
"unlockAccount",
},
"shh": []string{
"version",
"post",
"hasIdentity",
"newIdentity",
"newFilter",
"uninstallFilter",
"getFilterChanges",
},
"txpool": []string{
"status",
},
"web3": []string{
"sha3",
"version",
"fromWei",
"toWei",
"toHex",
"toAscii",
"fromAscii",
"toBigNumber",
"isAddress",
},
}
)
// Parse a comma separated API string to individual api's
func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth.Ethereum) ([]EthereumApi, error) {
if len(strings.TrimSpace(apistr)) == 0 {
return nil, fmt.Errorf("Empty apistr provided")
}
names := strings.Split(apistr, ",")
apis := make([]EthereumApi, len(names))
for i, name := range names {
switch strings.ToLower(strings.TrimSpace(name)) {
case AdminApiName:
apis[i] = NewAdminApi(xeth, eth, codec)
case DebugApiName:
apis[i] = NewDebugApi(xeth, eth, codec)
case EthApiName:
apis[i] = NewEthApi(xeth, codec)
case MinerApiName:
apis[i] = NewMinerApi(eth, codec)
case NetApiName:
apis[i] = NewNetApi(xeth, eth, codec)
case ShhApiName:
apis[i] = NewShhApi(xeth, eth, codec)
case TxPoolApiName:
apis[i] = NewTxPoolApi(xeth, eth, codec)
case PersonalApiName:
apis[i] = NewPersonalApi(xeth, eth, codec)
case Web3ApiName:
apis[i] = NewWeb3Api(xeth, codec)
default:
return nil, fmt.Errorf("Unknown API '%s'", name)
}
}
return apis, nil
}
func Javascript(name string) string {
switch strings.ToLower(strings.TrimSpace(name)) {
case AdminApiName:
return Admin_JS
case DebugApiName:
return Debug_JS
case MinerApiName:
return Miner_JS
case NetApiName:
return Net_JS
case ShhApiName:
return Shh_JS
case TxPoolApiName:
return TxPool_JS
case PersonalApiName:
return Personal_JS
}
return ""
}
package api
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/shared"
"github.com/ethereum/go-ethereum/xeth"
)
const (
Web3ApiVersion = "1.0"
)
var (
// mapping between methods and handlers
Web3Mapping = map[string]web3handler{
"web3_sha3": (*web3Api).Sha3,
"web3_clientVersion": (*web3Api).ClientVersion,
}
)
// web3 callback handler
type web3handler func(*web3Api, *shared.Request) (interface{}, error)
// web3 api provider
type web3Api struct {
xeth *xeth.XEth
methods map[string]web3handler
codec codec.ApiCoder
}
// create a new web3 api instance
func NewWeb3Api(xeth *xeth.XEth, coder codec.Codec) *web3Api {
return &web3Api{
xeth: xeth,
methods: Web3Mapping,
codec: coder.New(nil),
}
}
// collection with supported methods
func (self *web3Api) 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 *web3Api) 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 *web3Api) Name() string {
return Web3ApiName
}
func (self *web3Api) ApiVersion() string {
return Web3ApiVersion
}
// Calculates the sha3 over req.Params.Data
func (self *web3Api) Sha3(req *shared.Request) (interface{}, error) {
args := new(Sha3Args)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, err
}
return common.ToHex(crypto.Sha3(common.FromHex(args.Data))), nil
}
// returns the xeth client vrsion
func (self *web3Api) ClientVersion(req *shared.Request) (interface{}, error) {
return self.xeth.ClientVersion(), nil
}
package api
type Sha3Args struct {
Data string
}
package codec
import (
"net"
"strconv"
"github.com/ethereum/go-ethereum/rpc/shared"
)
type Codec int
// (de)serialization support for rpc interface
type ApiCoder interface {
// Parse message to request from underlying stream
ReadRequest() (*shared.Request, error)
// Parse response message from underlying stream
ReadResponse() (interface{}, error)
// Encode response to encoded form in underlying stream
WriteResponse(interface{}) error
// Decode single message from data
Decode([]byte, interface{}) error
// Encode msg to encoded form
Encode(msg interface{}) ([]byte, error)
// close the underlying stream
Close()
}
// supported codecs
const (
JSON Codec = iota
nCodecs
)
var (
// collection with supported coders
coders = make([]func(net.Conn) ApiCoder, nCodecs)
)
// create a new coder instance
func (c Codec) New(conn net.Conn) ApiCoder {
switch c {
case JSON:
return NewJsonCoder(conn)
}
panic("codec: request for codec #" + strconv.Itoa(int(c)) + " is unavailable")
}
package codec
import (
"encoding/json"
"net"
"github.com/ethereum/go-ethereum/rpc/shared"
)
const (
MAX_RESPONSE_SIZE = 64 * 1024
)
// Json serialization support
type JsonCodec struct {
c net.Conn
d *json.Decoder
e *json.Encoder
}
// Create new JSON coder instance
func NewJsonCoder(conn net.Conn) ApiCoder {
return &JsonCodec{
c: conn,
d: json.NewDecoder(conn),
e: json.NewEncoder(conn),
}
}
// Serialize obj to JSON and write it to conn
func (self *JsonCodec) ReadRequest() (*shared.Request, error) {
req := shared.Request{}
err := self.d.Decode(&req)
if err == nil {
return &req, nil
}
return nil, err
}
func (self *JsonCodec) ReadResponse() (interface{}, error) {
var err error
buf := make([]byte, MAX_RESPONSE_SIZE)
n, _ := self.c.Read(buf)
var failure shared.ErrorResponse
if err = json.Unmarshal(buf[:n], &failure); err == nil && failure.Error != nil {
return failure, nil
}
var success shared.SuccessResponse
if err = json.Unmarshal(buf[:n], &success); err == nil {
return success, nil
}
return nil, err
}
// Encode response to encoded form in underlying stream
func (self *JsonCodec) Decode(data []byte, msg interface{}) error {
return json.Unmarshal(data, msg)
}
func (self *JsonCodec) Encode(msg interface{}) ([]byte, error) {
return json.Marshal(msg)
}
// Parse JSON data from conn to obj
func (self *JsonCodec) WriteResponse(res interface{}) error {
return self.e.Encode(&res)
}
// Close decoder and encoder
func (self *JsonCodec) Close() {
self.c.Close()
}
package comms
type EthereumClient interface {
Close()
Send(interface{}) error
Recv() (interface{}, error)
}
package comms
import (
"github.com/ethereum/go-ethereum/rpc/api"
"github.com/ethereum/go-ethereum/rpc/codec"
)
type IpcConfig struct {
Endpoint string
}
type ipcClient struct {
c codec.ApiCoder
}
func (self *ipcClient) Close() {
self.c.Close()
}
func (self *ipcClient) Send(req interface{}) error {
return self.c.WriteResponse(req)
}
func (self *ipcClient) Recv() (interface{}, error) {
return self.c.ReadResponse()
}
// Create a new IPC client, UNIX domain socket on posix, named pipe on Windows
func NewIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) {
return newIpcClient(cfg, codec)
}
// Start IPC server
func StartIpc(cfg IpcConfig, codec codec.Codec, apis ...api.EthereumApi) error {
offeredApi := api.Merge(apis...)
return startIpc(cfg, codec, offeredApi)
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment