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 diff is collapsed.
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
})
]
});
`
This diff is collapsed.
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.
This diff is collapsed.
This diff is collapsed.
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; }
})
]
});
`
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
package api
type Sha3Args struct {
Data string
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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