diff options
Diffstat (limited to 'rpc/api')
-rw-r--r-- | rpc/api/api.go | 3 | ||||
-rw-r--r-- | rpc/api/debug.go | 169 | ||||
-rw-r--r-- | rpc/api/debug_args.go | 47 | ||||
-rw-r--r-- | rpc/api/debug_js.go | 48 | ||||
-rw-r--r-- | rpc/api/utils.go | 4 |
5 files changed, 270 insertions, 1 deletions
diff --git a/rpc/api/api.go b/rpc/api/api.go index e4f0e7446..067a4d4e8 100644 --- a/rpc/api/api.go +++ b/rpc/api/api.go @@ -4,9 +4,10 @@ import "github.com/ethereum/go-ethereum/rpc/shared" const ( // List with all API's which are offered over the IPC interface by default - DefaultIpcApis = "eth,miner,net,web3" + DefaultIpcApis = "debug,eth,miner,net,web3" EthApiName = "eth" + DebugApiName = "debug" MergedApiName = "merged" MinerApiName = "miner" NetApiName = "net" diff --git a/rpc/api/debug.go b/rpc/api/debug.go new file mode 100644 index 000000000..26f43fe74 --- /dev/null +++ b/rpc/api/debug.go @@ -0,0 +1,169 @@ +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 ( + DebugVersion = "1.0.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) 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.Dump(), 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 + } +} diff --git a/rpc/api/debug_args.go b/rpc/api/debug_args.go new file mode 100644 index 000000000..b9b5aa27e --- /dev/null +++ b/rpc/api/debug_args.go @@ -0,0 +1,47 @@ +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 +} diff --git a/rpc/api/debug_js.go b/rpc/api/debug_js.go new file mode 100644 index 000000000..43c545b2a --- /dev/null +++ b/rpc/api/debug_js.go @@ -0,0 +1,48 @@ +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 + }) + ], + properties: + [ + ] +}); +` diff --git a/rpc/api/utils.go b/rpc/api/utils.go index 173a880d4..6e6d5c7b0 100644 --- a/rpc/api/utils.go +++ b/rpc/api/utils.go @@ -21,6 +21,8 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth. for i, name := range names { switch strings.ToLower(strings.TrimSpace(name)) { + case DebugApiName: + apis[i] = NewDebugApi(xeth, eth, codec) case EthApiName: apis[i] = NewEthApi(xeth, codec) case MinerApiName: @@ -39,6 +41,8 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth. func Javascript(name string) string { switch strings.ToLower(strings.TrimSpace(name)) { + case DebugApiName: + return Debug_JS case MinerApiName: return Miner_JS case NetApiName: |