rpc: migrated the RPC insterface to a new reflection based RPC layer
This commit is contained in:
committed by
Jeffrey Wilcke
parent
f2ab351e8d
commit
19b2640e89
465
rpc/api/admin.go
465
rpc/api/admin.go
@ -1,465 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/compiler"
|
||||
"github.com/ethereum/go-ethereum/common/natspec"
|
||||
"github.com/ethereum/go-ethereum/common/registrar"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||
"github.com/ethereum/go-ethereum/rpc/comms"
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
"github.com/ethereum/go-ethereum/rpc/useragent"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
)
|
||||
|
||||
const (
|
||||
AdminApiversion = "1.0"
|
||||
importBatchSize = 2500
|
||||
)
|
||||
|
||||
var (
|
||||
// mapping between methods and handlers
|
||||
AdminMapping = map[string]adminhandler{
|
||||
"admin_addPeer": (*adminApi).AddPeer,
|
||||
"admin_peers": (*adminApi).Peers,
|
||||
"admin_nodeInfo": (*adminApi).NodeInfo,
|
||||
"admin_exportChain": (*adminApi).ExportChain,
|
||||
"admin_importChain": (*adminApi).ImportChain,
|
||||
"admin_setSolc": (*adminApi).SetSolc,
|
||||
"admin_datadir": (*adminApi).DataDir,
|
||||
"admin_startRPC": (*adminApi).StartRPC,
|
||||
"admin_stopRPC": (*adminApi).StopRPC,
|
||||
"admin_setGlobalRegistrar": (*adminApi).SetGlobalRegistrar,
|
||||
"admin_setHashReg": (*adminApi).SetHashReg,
|
||||
"admin_setUrlHint": (*adminApi).SetUrlHint,
|
||||
"admin_saveInfo": (*adminApi).SaveInfo,
|
||||
"admin_register": (*adminApi).Register,
|
||||
"admin_registerUrl": (*adminApi).RegisterUrl,
|
||||
"admin_startNatSpec": (*adminApi).StartNatSpec,
|
||||
"admin_stopNatSpec": (*adminApi).StopNatSpec,
|
||||
"admin_getContractInfo": (*adminApi).GetContractInfo,
|
||||
"admin_httpGet": (*adminApi).HttpGet,
|
||||
"admin_sleepBlocks": (*adminApi).SleepBlocks,
|
||||
"admin_sleep": (*adminApi).Sleep,
|
||||
"admin_enableUserAgent": (*adminApi).EnableUserAgent,
|
||||
}
|
||||
)
|
||||
|
||||
// admin callback handler
|
||||
type adminhandler func(*adminApi, *shared.Request) (interface{}, error)
|
||||
|
||||
// admin api provider
|
||||
type adminApi struct {
|
||||
xeth *xeth.XEth
|
||||
stack *node.Node
|
||||
ethereum *eth.Ethereum
|
||||
codec codec.Codec
|
||||
coder codec.ApiCoder
|
||||
}
|
||||
|
||||
// create a new admin api instance
|
||||
func NewAdminApi(xeth *xeth.XEth, stack *node.Node, codec codec.Codec) *adminApi {
|
||||
api := &adminApi{
|
||||
xeth: xeth,
|
||||
stack: stack,
|
||||
codec: codec,
|
||||
coder: codec.New(nil),
|
||||
}
|
||||
if stack != nil {
|
||||
stack.Service(&api.ethereum)
|
||||
}
|
||||
return api
|
||||
}
|
||||
|
||||
// collection with supported methods
|
||||
func (self *adminApi) Methods() []string {
|
||||
methods := make([]string, len(AdminMapping))
|
||||
i := 0
|
||||
for k := range AdminMapping {
|
||||
methods[i] = k
|
||||
i++
|
||||
}
|
||||
return methods
|
||||
}
|
||||
|
||||
// Execute given request
|
||||
func (self *adminApi) Execute(req *shared.Request) (interface{}, error) {
|
||||
if callback, ok := AdminMapping[req.Method]; ok {
|
||||
return callback(self, req)
|
||||
}
|
||||
|
||||
return nil, &shared.NotImplementedError{req.Method}
|
||||
}
|
||||
|
||||
func (self *adminApi) Name() string {
|
||||
return shared.AdminApiName
|
||||
}
|
||||
|
||||
func (self *adminApi) ApiVersion() string {
|
||||
return AdminApiversion
|
||||
}
|
||||
|
||||
func (self *adminApi) AddPeer(req *shared.Request) (interface{}, error) {
|
||||
args := new(AddPeerArgs)
|
||||
if err := self.coder.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
node, err := discover.ParseNode(args.Url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid node URL: %v", err)
|
||||
}
|
||||
self.stack.Server().AddPeer(node)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (self *adminApi) Peers(req *shared.Request) (interface{}, error) {
|
||||
return self.stack.Server().PeersInfo(), nil
|
||||
}
|
||||
|
||||
func (self *adminApi) NodeInfo(req *shared.Request) (interface{}, error) {
|
||||
return self.stack.Server().NodeInfo(), nil
|
||||
}
|
||||
|
||||
func (self *adminApi) DataDir(req *shared.Request) (interface{}, error) {
|
||||
return self.stack.DataDir(), nil
|
||||
}
|
||||
|
||||
func hasAllBlocks(chain *core.BlockChain, 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.coder.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.BlockChain(), blocks[:i]) {
|
||||
continue
|
||||
}
|
||||
if _, err := self.ethereum.BlockChain().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.coder.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.BlockChain().Export(fh); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (self *adminApi) SetSolc(req *shared.Request) (interface{}, error) {
|
||||
args := new(SetSolcArgs)
|
||||
if err := self.coder.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
|
||||
}
|
||||
|
||||
func (self *adminApi) StartRPC(req *shared.Request) (interface{}, error) {
|
||||
args := new(StartRPCArgs)
|
||||
if err := self.coder.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
cfg := comms.HttpConfig{
|
||||
ListenAddress: args.ListenAddress,
|
||||
ListenPort: args.ListenPort,
|
||||
CorsDomain: args.CorsDomain,
|
||||
}
|
||||
|
||||
apis, err := ParseApiString(args.Apis, self.codec, self.xeth, self.stack)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
err = comms.StartHttp(cfg, self.codec, Merge(apis...))
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
func (self *adminApi) StopRPC(req *shared.Request) (interface{}, error) {
|
||||
comms.StopHttp()
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (self *adminApi) SleepBlocks(req *shared.Request) (interface{}, error) {
|
||||
args := new(SleepBlocksArgs)
|
||||
if err := self.coder.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
var timer <-chan time.Time
|
||||
var height *big.Int
|
||||
var err error
|
||||
if args.Timeout > 0 {
|
||||
timer = time.NewTimer(time.Duration(args.Timeout) * time.Second).C
|
||||
}
|
||||
|
||||
height = new(big.Int).Add(self.xeth.CurrentBlock().Number(), big.NewInt(args.N))
|
||||
height, err = sleepBlocks(self.xeth.UpdateState(), height, timer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return height.Uint64(), nil
|
||||
}
|
||||
|
||||
func sleepBlocks(wait chan *big.Int, height *big.Int, timer <-chan time.Time) (newHeight *big.Int, err error) {
|
||||
wait <- height
|
||||
select {
|
||||
case <-timer:
|
||||
// if times out make sure the xeth loop does not block
|
||||
go func() {
|
||||
select {
|
||||
case wait <- nil:
|
||||
case <-wait:
|
||||
}
|
||||
}()
|
||||
return nil, fmt.Errorf("timeout")
|
||||
case newHeight = <-wait:
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *adminApi) Sleep(req *shared.Request) (interface{}, error) {
|
||||
args := new(SleepArgs)
|
||||
if err := self.coder.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
time.Sleep(time.Duration(args.S) * time.Second)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (self *adminApi) SetGlobalRegistrar(req *shared.Request) (interface{}, error) {
|
||||
args := new(SetGlobalRegistrarArgs)
|
||||
if err := self.coder.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
sender := common.HexToAddress(args.ContractAddress)
|
||||
|
||||
reg := registrar.New(self.xeth)
|
||||
txhash, err := reg.SetGlobalRegistrar(args.NameReg, sender)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return txhash, nil
|
||||
}
|
||||
|
||||
func (self *adminApi) SetHashReg(req *shared.Request) (interface{}, error) {
|
||||
args := new(SetHashRegArgs)
|
||||
if err := self.coder.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
reg := registrar.New(self.xeth)
|
||||
sender := common.HexToAddress(args.Sender)
|
||||
txhash, err := reg.SetHashReg(args.HashReg, sender)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return txhash, nil
|
||||
}
|
||||
|
||||
func (self *adminApi) SetUrlHint(req *shared.Request) (interface{}, error) {
|
||||
args := new(SetUrlHintArgs)
|
||||
if err := self.coder.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
urlHint := args.UrlHint
|
||||
sender := common.HexToAddress(args.Sender)
|
||||
|
||||
reg := registrar.New(self.xeth)
|
||||
txhash, err := reg.SetUrlHint(urlHint, sender)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return txhash, nil
|
||||
}
|
||||
|
||||
func (self *adminApi) SaveInfo(req *shared.Request) (interface{}, error) {
|
||||
args := new(SaveInfoArgs)
|
||||
if err := self.coder.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
contenthash, err := compiler.SaveInfo(&args.ContractInfo, args.Filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return contenthash.Hex(), nil
|
||||
}
|
||||
|
||||
func (self *adminApi) Register(req *shared.Request) (interface{}, error) {
|
||||
args := new(RegisterArgs)
|
||||
if err := self.coder.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
sender := common.HexToAddress(args.Sender)
|
||||
// sender and contract address are passed as hex strings
|
||||
codeb := self.xeth.CodeAtBytes(args.Address)
|
||||
codeHash := common.BytesToHash(crypto.Sha3(codeb))
|
||||
contentHash := common.HexToHash(args.ContentHashHex)
|
||||
registry := registrar.New(self.xeth)
|
||||
|
||||
_, err := registry.SetHashToHash(sender, codeHash, contentHash)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (self *adminApi) RegisterUrl(req *shared.Request) (interface{}, error) {
|
||||
args := new(RegisterUrlArgs)
|
||||
if err := self.coder.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
sender := common.HexToAddress(args.Sender)
|
||||
registry := registrar.New(self.xeth)
|
||||
_, err := registry.SetUrlToHash(sender, common.HexToHash(args.ContentHash), args.Url)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (self *adminApi) StartNatSpec(req *shared.Request) (interface{}, error) {
|
||||
self.ethereum.NatSpec = true
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (self *adminApi) StopNatSpec(req *shared.Request) (interface{}, error) {
|
||||
self.ethereum.NatSpec = false
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (self *adminApi) GetContractInfo(req *shared.Request) (interface{}, error) {
|
||||
args := new(GetContractInfoArgs)
|
||||
if err := self.coder.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
infoDoc, err := natspec.FetchDocsForContract(args.Contract, self.xeth, self.ethereum.HTTPClient())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var info interface{}
|
||||
err = self.coder.Decode(infoDoc, &info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (self *adminApi) HttpGet(req *shared.Request) (interface{}, error) {
|
||||
args := new(HttpGetArgs)
|
||||
if err := self.coder.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
resp, err := self.ethereum.HTTPClient().Get(args.Uri, args.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return string(resp), nil
|
||||
}
|
||||
|
||||
func (self *adminApi) EnableUserAgent(req *shared.Request) (interface{}, error) {
|
||||
if fe, ok := self.xeth.Frontend().(*useragent.RemoteFrontend); ok {
|
||||
fe.Enable()
|
||||
}
|
||||
return true, nil
|
||||
}
|
@ -1,468 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/compiler"
|
||||
"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 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")
|
||||
}
|
||||
|
||||
type StartRPCArgs struct {
|
||||
ListenAddress string
|
||||
ListenPort uint
|
||||
CorsDomain string
|
||||
Apis string
|
||||
}
|
||||
|
||||
func (args *StartRPCArgs) UnmarshalJSON(b []byte) (err error) {
|
||||
var obj []interface{}
|
||||
if err := json.Unmarshal(b, &obj); err != nil {
|
||||
return shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
args.ListenAddress = "127.0.0.1"
|
||||
args.ListenPort = 8545
|
||||
args.Apis = "net,eth,web3"
|
||||
|
||||
if len(obj) >= 1 && obj[0] != nil {
|
||||
if addr, ok := obj[0].(string); ok {
|
||||
args.ListenAddress = addr
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("listenAddress", "not a string")
|
||||
}
|
||||
}
|
||||
|
||||
if len(obj) >= 2 && obj[1] != nil {
|
||||
if port, ok := obj[1].(float64); ok && port >= 0 && port <= 64*1024 {
|
||||
args.ListenPort = uint(port)
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("listenPort", "not a valid port number")
|
||||
}
|
||||
}
|
||||
|
||||
if len(obj) >= 3 && obj[2] != nil {
|
||||
if corsDomain, ok := obj[2].(string); ok {
|
||||
args.CorsDomain = corsDomain
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("corsDomain", "not a string")
|
||||
}
|
||||
}
|
||||
|
||||
if len(obj) >= 4 && obj[3] != nil {
|
||||
if apis, ok := obj[3].(string); ok {
|
||||
args.Apis = apis
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("apis", "not a string")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type SleepArgs struct {
|
||||
S int
|
||||
}
|
||||
|
||||
func (args *SleepArgs) 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 {
|
||||
if obj[0] != nil {
|
||||
if n, err := numString(obj[0]); err == nil {
|
||||
args.S = int(n.Int64())
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("N", "not an integer: "+err.Error())
|
||||
}
|
||||
} else {
|
||||
return shared.NewInsufficientParamsError(0, 1)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SleepBlocksArgs struct {
|
||||
N int64
|
||||
Timeout int64
|
||||
}
|
||||
|
||||
func (args *SleepBlocksArgs) UnmarshalJSON(b []byte) (err error) {
|
||||
|
||||
var obj []interface{}
|
||||
if err := json.Unmarshal(b, &obj); err != nil {
|
||||
return shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
args.N = 1
|
||||
args.Timeout = 0
|
||||
if len(obj) >= 1 && obj[0] != nil {
|
||||
if n, err := numString(obj[0]); err == nil {
|
||||
args.N = n.Int64()
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("N", "not an integer: "+err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if len(obj) >= 2 && obj[1] != nil {
|
||||
if n, err := numString(obj[1]); err == nil {
|
||||
args.Timeout = n.Int64()
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("Timeout", "not an integer: "+err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type SetGlobalRegistrarArgs struct {
|
||||
NameReg string
|
||||
ContractAddress string
|
||||
}
|
||||
|
||||
func (args *SetGlobalRegistrarArgs) 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 {
|
||||
return shared.NewDecodeParamError("Expected namereg address")
|
||||
}
|
||||
|
||||
if len(obj) >= 1 {
|
||||
if namereg, ok := obj[0].(string); ok {
|
||||
args.NameReg = namereg
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("NameReg", "not a string")
|
||||
}
|
||||
}
|
||||
|
||||
if len(obj) >= 2 && obj[1] != nil {
|
||||
if addr, ok := obj[1].(string); ok {
|
||||
args.ContractAddress = addr
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("ContractAddress", "not a string")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type SetHashRegArgs struct {
|
||||
HashReg string
|
||||
Sender string
|
||||
}
|
||||
|
||||
func (args *SetHashRegArgs) 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 && obj[0] != nil {
|
||||
if hashreg, ok := obj[0].(string); ok {
|
||||
args.HashReg = hashreg
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("HashReg", "not a string")
|
||||
}
|
||||
}
|
||||
|
||||
if len(obj) >= 2 && obj[1] != nil {
|
||||
if sender, ok := obj[1].(string); ok {
|
||||
args.Sender = sender
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("Sender", "not a string")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type SetUrlHintArgs struct {
|
||||
UrlHint string
|
||||
Sender string
|
||||
}
|
||||
|
||||
func (args *SetUrlHintArgs) 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 && obj[0] != nil {
|
||||
if urlhint, ok := obj[0].(string); ok {
|
||||
args.UrlHint = urlhint
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("UrlHint", "not a string")
|
||||
}
|
||||
}
|
||||
|
||||
if len(obj) >= 2 && obj[1] != nil {
|
||||
if sender, ok := obj[1].(string); ok {
|
||||
args.Sender = sender
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("Sender", "not a string")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type SaveInfoArgs struct {
|
||||
ContractInfo compiler.ContractInfo
|
||||
Filename string
|
||||
}
|
||||
|
||||
func (args *SaveInfoArgs) 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 jsonraw, err := json.Marshal(obj[0]); err == nil {
|
||||
if err = json.Unmarshal(jsonraw, &args.ContractInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
|
||||
if filename, ok := obj[1].(string); ok {
|
||||
args.Filename = filename
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("Filename", "not a string")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type RegisterArgs struct {
|
||||
Sender string
|
||||
Address string
|
||||
ContentHashHex string
|
||||
}
|
||||
|
||||
func (args *RegisterArgs) UnmarshalJSON(b []byte) (err error) {
|
||||
var obj []interface{}
|
||||
if err := json.Unmarshal(b, &obj); err != nil {
|
||||
return shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
if len(obj) < 3 {
|
||||
return shared.NewInsufficientParamsError(len(obj), 3)
|
||||
}
|
||||
|
||||
if len(obj) >= 1 {
|
||||
if sender, ok := obj[0].(string); ok {
|
||||
args.Sender = sender
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("Sender", "not a string")
|
||||
}
|
||||
}
|
||||
|
||||
if len(obj) >= 2 {
|
||||
if address, ok := obj[1].(string); ok {
|
||||
args.Address = address
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("Address", "not a string")
|
||||
}
|
||||
}
|
||||
|
||||
if len(obj) >= 3 {
|
||||
if hex, ok := obj[2].(string); ok {
|
||||
args.ContentHashHex = hex
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("ContentHashHex", "not a string")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type RegisterUrlArgs struct {
|
||||
Sender string
|
||||
ContentHash string
|
||||
Url string
|
||||
}
|
||||
|
||||
func (args *RegisterUrlArgs) 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 {
|
||||
if sender, ok := obj[0].(string); ok {
|
||||
args.Sender = sender
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("Sender", "not a string")
|
||||
}
|
||||
}
|
||||
|
||||
if len(obj) >= 2 {
|
||||
if sender, ok := obj[1].(string); ok {
|
||||
args.ContentHash = sender
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("ContentHash", "not a string")
|
||||
}
|
||||
}
|
||||
|
||||
if len(obj) >= 3 {
|
||||
if sender, ok := obj[2].(string); ok {
|
||||
args.Url = sender
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("Url", "not a string")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type GetContractInfoArgs struct {
|
||||
Contract string
|
||||
}
|
||||
|
||||
func (args *GetContractInfoArgs) 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 len(obj) >= 1 {
|
||||
if contract, ok := obj[0].(string); ok {
|
||||
args.Contract = contract
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("Contract", "not a string")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type HttpGetArgs struct {
|
||||
Uri string
|
||||
Path string
|
||||
}
|
||||
|
||||
func (args *HttpGetArgs) 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 len(obj) >= 1 {
|
||||
if uri, ok := obj[0].(string); ok {
|
||||
args.Uri = uri
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("Uri", "not a string")
|
||||
}
|
||||
}
|
||||
|
||||
if len(obj) >= 2 && obj[1] != nil {
|
||||
if path, ok := obj[1].(string); ok {
|
||||
args.Path = path
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("Path", "not a string")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,143 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
const Admin_JS = `
|
||||
web3._extend({
|
||||
property: 'admin',
|
||||
methods:
|
||||
[
|
||||
new web3._extend.Method({
|
||||
name: 'addPeer',
|
||||
call: 'admin_addPeer',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'exportChain',
|
||||
call: 'admin_exportChain',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'importChain',
|
||||
call: 'admin_importChain',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'sleepBlocks',
|
||||
call: 'admin_sleepBlocks',
|
||||
params: 2,
|
||||
inputFormatter: [null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setSolc',
|
||||
call: 'admin_setSolc',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'startRPC',
|
||||
call: 'admin_startRPC',
|
||||
params: 4,
|
||||
inputFormatter: [null, null, null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'stopRPC',
|
||||
call: 'admin_stopRPC',
|
||||
params: 0,
|
||||
inputFormatter: []
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setGlobalRegistrar',
|
||||
call: 'admin_setGlobalRegistrar',
|
||||
params: 2,
|
||||
inputFormatter: [null,null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setHashReg',
|
||||
call: 'admin_setHashReg',
|
||||
params: 2,
|
||||
inputFormatter: [null,null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setUrlHint',
|
||||
call: 'admin_setUrlHint',
|
||||
params: 2,
|
||||
inputFormatter: [null,null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'saveInfo',
|
||||
call: 'admin_saveInfo',
|
||||
params: 2,
|
||||
inputFormatter: [null,null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'register',
|
||||
call: 'admin_register',
|
||||
params: 3,
|
||||
inputFormatter: [null,null,null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'registerUrl',
|
||||
call: 'admin_registerUrl',
|
||||
params: 3,
|
||||
inputFormatter: [null,null,null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'startNatSpec',
|
||||
call: 'admin_startNatSpec',
|
||||
params: 0,
|
||||
inputFormatter: []
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'stopNatSpec',
|
||||
call: 'admin_stopNatSpec',
|
||||
params: 0,
|
||||
inputFormatter: []
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getContractInfo',
|
||||
call: 'admin_getContractInfo',
|
||||
params: 1,
|
||||
inputFormatter: [null],
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'httpGet',
|
||||
call: 'admin_httpGet',
|
||||
params: 2,
|
||||
inputFormatter: [null, null]
|
||||
})
|
||||
],
|
||||
properties:
|
||||
[
|
||||
new web3._extend.Property({
|
||||
name: 'nodeInfo',
|
||||
getter: 'admin_nodeInfo'
|
||||
}),
|
||||
new web3._extend.Property({
|
||||
name: 'peers',
|
||||
getter: 'admin_peers'
|
||||
}),
|
||||
new web3._extend.Property({
|
||||
name: 'datadir',
|
||||
getter: 'admin_datadir'
|
||||
})
|
||||
]
|
||||
});
|
||||
`
|
@ -1,26 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
)
|
||||
|
||||
// Merge multiple API's to a single API instance
|
||||
func Merge(apis ...shared.EthereumApi) shared.EthereumApi {
|
||||
return newMergedApi(apis...)
|
||||
}
|
@ -1,170 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/compiler"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
)
|
||||
|
||||
func TestParseApiString(t *testing.T) {
|
||||
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")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const solcVersion = "0.9.23"
|
||||
|
||||
func TestCompileSolidity(t *testing.T) {
|
||||
|
||||
solc, err := compiler.New("")
|
||||
if solc == nil {
|
||||
t.Skip("no solc found: skip")
|
||||
} else if solc.Version() != solcVersion {
|
||||
t.Skip("WARNING: skipping test because of solc different version (%v, test written for %v, may need to update)", solc.Version(), solcVersion)
|
||||
}
|
||||
source := `contract test {\n` +
|
||||
" /// @notice Will multiply `a` by 7." + `\n` +
|
||||
` function multiply(uint a) returns(uint d) {\n` +
|
||||
` return a * 7;\n` +
|
||||
` }\n` +
|
||||
`}\n`
|
||||
|
||||
jsonstr := `{"jsonrpc":"2.0","method":"eth_compileSolidity","params":["` + source + `"],"id":64}`
|
||||
|
||||
expCode := "0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056"
|
||||
expAbiDefinition := `[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]`
|
||||
expUserDoc := `{"methods":{"multiply(uint256)":{"notice":"Will multiply ` + "`a`" + ` by 7."}}}`
|
||||
expDeveloperDoc := `{"methods":{}}`
|
||||
expCompilerVersion := solc.Version()
|
||||
expLanguage := "Solidity"
|
||||
expLanguageVersion := "0"
|
||||
expSource := source
|
||||
|
||||
eth := ð.Ethereum{}
|
||||
xeth := xeth.NewTest(nil, nil)
|
||||
api := NewEthApi(xeth, eth, codec.JSON)
|
||||
|
||||
var rpcRequest shared.Request
|
||||
json.Unmarshal([]byte(jsonstr), &rpcRequest)
|
||||
|
||||
response, err := api.CompileSolidity(&rpcRequest)
|
||||
if err != nil {
|
||||
t.Errorf("Execution failed, %v", err)
|
||||
}
|
||||
|
||||
respjson, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
var contracts = make(map[string]*compiler.Contract)
|
||||
err = json.Unmarshal(respjson, &contracts)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
if len(contracts) != 1 {
|
||||
t.Errorf("expected one contract, got %v", len(contracts))
|
||||
}
|
||||
|
||||
contract := contracts["test"]
|
||||
|
||||
if contract.Code != expCode {
|
||||
t.Errorf("Expected \n%s got \n%s", expCode, contract.Code)
|
||||
}
|
||||
|
||||
if strconv.Quote(contract.Info.Source) != `"`+expSource+`"` {
|
||||
t.Errorf("Expected \n'%s' got \n'%s'", expSource, strconv.Quote(contract.Info.Source))
|
||||
}
|
||||
|
||||
if contract.Info.Language != expLanguage {
|
||||
t.Errorf("Expected %s got %s", expLanguage, contract.Info.Language)
|
||||
}
|
||||
|
||||
if contract.Info.LanguageVersion != expLanguageVersion {
|
||||
t.Errorf("Expected %s got %s", expLanguageVersion, contract.Info.LanguageVersion)
|
||||
}
|
||||
|
||||
if contract.Info.CompilerVersion != expCompilerVersion {
|
||||
t.Errorf("Expected %s got %s", expCompilerVersion, contract.Info.CompilerVersion)
|
||||
}
|
||||
|
||||
userdoc, err := json.Marshal(contract.Info.UserDoc)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
devdoc, err := json.Marshal(contract.Info.DeveloperDoc)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
abidef, err := json.Marshal(contract.Info.AbiDefinition)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
if string(abidef) != expAbiDefinition {
|
||||
t.Errorf("Expected \n'%s' got \n'%s'", expAbiDefinition, string(abidef))
|
||||
}
|
||||
|
||||
if string(userdoc) != expUserDoc {
|
||||
t.Errorf("Expected \n'%s' got \n'%s'", expUserDoc, string(userdoc))
|
||||
}
|
||||
|
||||
if string(devdoc) != expDeveloperDoc {
|
||||
t.Errorf("Expected %s got %s", expDeveloperDoc, string(devdoc))
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
)
|
||||
|
||||
type CompileArgs struct {
|
||||
Source string
|
||||
}
|
||||
|
||||
func (args *CompileArgs) UnmarshalJSON(b []byte) (err error) {
|
||||
var obj []interface{}
|
||||
if err := json.Unmarshal(b, &obj); err != nil {
|
||||
return shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
if len(obj) < 1 {
|
||||
return shared.NewInsufficientParamsError(len(obj), 1)
|
||||
}
|
||||
argstr, ok := obj[0].(string)
|
||||
if !ok {
|
||||
return shared.NewInvalidTypeError("arg0", "is not a string")
|
||||
}
|
||||
args.Source = argstr
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type FilterStringArgs struct {
|
||||
Word string
|
||||
}
|
||||
|
||||
func (args *FilterStringArgs) UnmarshalJSON(b []byte) (err error) {
|
||||
var obj []interface{}
|
||||
if err := json.Unmarshal(b, &obj); err != nil {
|
||||
return shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
if len(obj) < 1 {
|
||||
return shared.NewInsufficientParamsError(len(obj), 1)
|
||||
}
|
||||
|
||||
var argstr string
|
||||
argstr, ok := obj[0].(string)
|
||||
if !ok {
|
||||
return shared.NewInvalidTypeError("filter", "not a string")
|
||||
}
|
||||
switch argstr {
|
||||
case "latest", "pending":
|
||||
break
|
||||
default:
|
||||
return shared.NewValidationError("Word", "Must be `latest` or `pending`")
|
||||
}
|
||||
args.Word = argstr
|
||||
return nil
|
||||
}
|
2649
rpc/api/args_test.go
2649
rpc/api/args_test.go
File diff suppressed because it is too large
Load Diff
144
rpc/api/db.go
144
rpc/api/db.go
@ -1,144 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
)
|
||||
|
||||
const (
|
||||
DbApiversion = "1.0"
|
||||
)
|
||||
|
||||
var (
|
||||
// mapping between methods and handlers
|
||||
DbMapping = map[string]dbhandler{
|
||||
"db_getString": (*dbApi).GetString,
|
||||
"db_putString": (*dbApi).PutString,
|
||||
"db_getHex": (*dbApi).GetHex,
|
||||
"db_putHex": (*dbApi).PutHex,
|
||||
}
|
||||
)
|
||||
|
||||
// db callback handler
|
||||
type dbhandler func(*dbApi, *shared.Request) (interface{}, error)
|
||||
|
||||
// db api provider
|
||||
type dbApi struct {
|
||||
xeth *xeth.XEth
|
||||
ethereum *eth.Ethereum
|
||||
methods map[string]dbhandler
|
||||
codec codec.ApiCoder
|
||||
}
|
||||
|
||||
// create a new db api instance
|
||||
func NewDbApi(xeth *xeth.XEth, ethereum *eth.Ethereum, coder codec.Codec) *dbApi {
|
||||
return &dbApi{
|
||||
xeth: xeth,
|
||||
ethereum: ethereum,
|
||||
methods: DbMapping,
|
||||
codec: coder.New(nil),
|
||||
}
|
||||
}
|
||||
|
||||
// collection with supported methods
|
||||
func (self *dbApi) Methods() []string {
|
||||
methods := make([]string, len(self.methods))
|
||||
i := 0
|
||||
for k := range self.methods {
|
||||
methods[i] = k
|
||||
i++
|
||||
}
|
||||
return methods
|
||||
}
|
||||
|
||||
// Execute given request
|
||||
func (self *dbApi) Execute(req *shared.Request) (interface{}, error) {
|
||||
if callback, ok := self.methods[req.Method]; ok {
|
||||
return callback(self, req)
|
||||
}
|
||||
|
||||
return nil, &shared.NotImplementedError{req.Method}
|
||||
}
|
||||
|
||||
func (self *dbApi) Name() string {
|
||||
return shared.DbApiName
|
||||
}
|
||||
|
||||
func (self *dbApi) ApiVersion() string {
|
||||
return DbApiversion
|
||||
}
|
||||
|
||||
func (self *dbApi) GetString(req *shared.Request) (interface{}, error) {
|
||||
args := new(DbArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
if err := args.requirements(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err := self.xeth.DbGet([]byte(args.Database + args.Key))
|
||||
return string(ret), err
|
||||
}
|
||||
|
||||
func (self *dbApi) PutString(req *shared.Request) (interface{}, error) {
|
||||
args := new(DbArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
if err := args.requirements(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return self.xeth.DbPut([]byte(args.Database+args.Key), args.Value), nil
|
||||
}
|
||||
|
||||
func (self *dbApi) GetHex(req *shared.Request) (interface{}, error) {
|
||||
args := new(DbHexArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
if err := args.requirements(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if res, err := self.xeth.DbGet([]byte(args.Database + args.Key)); err == nil {
|
||||
return newHexData(res), nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (self *dbApi) PutHex(req *shared.Request) (interface{}, error) {
|
||||
args := new(DbHexArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
if err := args.requirements(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return self.xeth.DbPut([]byte(args.Database+args.Key), args.Value), nil
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
)
|
||||
|
||||
type DbArgs struct {
|
||||
Database string
|
||||
Key string
|
||||
Value []byte
|
||||
}
|
||||
|
||||
func (args *DbArgs) UnmarshalJSON(b []byte) (err error) {
|
||||
var obj []interface{}
|
||||
if err := json.Unmarshal(b, &obj); err != nil {
|
||||
return shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
if len(obj) < 2 {
|
||||
return shared.NewInsufficientParamsError(len(obj), 2)
|
||||
}
|
||||
|
||||
var objstr string
|
||||
var ok bool
|
||||
|
||||
if objstr, ok = obj[0].(string); !ok {
|
||||
return shared.NewInvalidTypeError("database", "not a string")
|
||||
}
|
||||
args.Database = objstr
|
||||
|
||||
if objstr, ok = obj[1].(string); !ok {
|
||||
return shared.NewInvalidTypeError("key", "not a string")
|
||||
}
|
||||
args.Key = objstr
|
||||
|
||||
if len(obj) > 2 {
|
||||
objstr, ok = obj[2].(string)
|
||||
if !ok {
|
||||
return shared.NewInvalidTypeError("value", "not a string")
|
||||
}
|
||||
|
||||
args.Value = []byte(objstr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *DbArgs) requirements() error {
|
||||
if len(a.Database) == 0 {
|
||||
return shared.NewValidationError("Database", "cannot be blank")
|
||||
}
|
||||
if len(a.Key) == 0 {
|
||||
return shared.NewValidationError("Key", "cannot be blank")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DbHexArgs struct {
|
||||
Database string
|
||||
Key string
|
||||
Value []byte
|
||||
}
|
||||
|
||||
func (args *DbHexArgs) UnmarshalJSON(b []byte) (err error) {
|
||||
var obj []interface{}
|
||||
if err := json.Unmarshal(b, &obj); err != nil {
|
||||
return shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
if len(obj) < 2 {
|
||||
return shared.NewInsufficientParamsError(len(obj), 2)
|
||||
}
|
||||
|
||||
var objstr string
|
||||
var ok bool
|
||||
|
||||
if objstr, ok = obj[0].(string); !ok {
|
||||
return shared.NewInvalidTypeError("database", "not a string")
|
||||
}
|
||||
args.Database = objstr
|
||||
|
||||
if objstr, ok = obj[1].(string); !ok {
|
||||
return shared.NewInvalidTypeError("key", "not a string")
|
||||
}
|
||||
args.Key = objstr
|
||||
|
||||
if len(obj) > 2 {
|
||||
objstr, ok = obj[2].(string)
|
||||
if !ok {
|
||||
return shared.NewInvalidTypeError("value", "not a string")
|
||||
}
|
||||
|
||||
args.Value = common.FromHex(objstr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *DbHexArgs) requirements() error {
|
||||
if len(a.Database) == 0 {
|
||||
return shared.NewValidationError("Database", "cannot be blank")
|
||||
}
|
||||
if len(a.Key) == 0 {
|
||||
return shared.NewValidationError("Key", "cannot be blank")
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
const Db_JS = `
|
||||
web3._extend({
|
||||
property: 'db',
|
||||
methods:
|
||||
[
|
||||
],
|
||||
properties:
|
||||
[
|
||||
]
|
||||
});
|
||||
`
|
303
rpc/api/debug.go
303
rpc/api/debug.go
@ -1,303 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/ethash"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"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"
|
||||
"github.com/rcrowley/go-metrics"
|
||||
)
|
||||
|
||||
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_metrics": (*debugApi).Metrics,
|
||||
}
|
||||
)
|
||||
|
||||
// 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 shared.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, err := state.New(block.Root(), self.ethereum.ChainDb())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
self.ethereum.BlockChain().SetHead(uint64(args.BlockNumber))
|
||||
|
||||
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
|
||||
|
||||
var (
|
||||
blockchain = self.ethereum.BlockChain()
|
||||
validator = blockchain.Validator()
|
||||
processor = blockchain.Processor()
|
||||
)
|
||||
|
||||
err := core.ValidateHeader(blockchain.AuxValidator(), block.Header(), blockchain.GetHeader(block.ParentHash()), true, false)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
statedb, err := state.New(blockchain.GetBlock(block.ParentHash()).Root(), self.ethereum.ChainDb())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
receipts, _, usedGas, err := processor.Process(block, statedb)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
err = validator.ValidateState(block, blockchain.GetBlock(block.ParentHash()), statedb, receipts, usedGas)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
func (self *debugApi) Metrics(req *shared.Request) (interface{}, error) {
|
||||
args := new(MetricsArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
// Create a rate formatter
|
||||
units := []string{"", "K", "M", "G", "T", "E", "P"}
|
||||
round := func(value float64, prec int) string {
|
||||
unit := 0
|
||||
for value >= 1000 {
|
||||
unit, value, prec = unit+1, value/1000, 2
|
||||
}
|
||||
return fmt.Sprintf(fmt.Sprintf("%%.%df%s", prec, units[unit]), value)
|
||||
}
|
||||
format := func(total float64, rate float64) string {
|
||||
return fmt.Sprintf("%s (%s/s)", round(total, 0), round(rate, 2))
|
||||
}
|
||||
// Iterate over all the metrics, and just dump for now
|
||||
counters := make(map[string]interface{})
|
||||
metrics.DefaultRegistry.Each(func(name string, metric interface{}) {
|
||||
// Create or retrieve the counter hierarchy for this metric
|
||||
root, parts := counters, strings.Split(name, "/")
|
||||
for _, part := range parts[:len(parts)-1] {
|
||||
if _, ok := root[part]; !ok {
|
||||
root[part] = make(map[string]interface{})
|
||||
}
|
||||
root = root[part].(map[string]interface{})
|
||||
}
|
||||
name = parts[len(parts)-1]
|
||||
|
||||
// Fill the counter with the metric details, formatting if requested
|
||||
if args.Raw {
|
||||
switch metric := metric.(type) {
|
||||
case metrics.Meter:
|
||||
root[name] = map[string]interface{}{
|
||||
"AvgRate01Min": metric.Rate1(),
|
||||
"AvgRate05Min": metric.Rate5(),
|
||||
"AvgRate15Min": metric.Rate15(),
|
||||
"MeanRate": metric.RateMean(),
|
||||
"Overall": float64(metric.Count()),
|
||||
}
|
||||
|
||||
case metrics.Timer:
|
||||
root[name] = map[string]interface{}{
|
||||
"AvgRate01Min": metric.Rate1(),
|
||||
"AvgRate05Min": metric.Rate5(),
|
||||
"AvgRate15Min": metric.Rate15(),
|
||||
"MeanRate": metric.RateMean(),
|
||||
"Overall": float64(metric.Count()),
|
||||
"Percentiles": map[string]interface{}{
|
||||
"5": metric.Percentile(0.05),
|
||||
"20": metric.Percentile(0.2),
|
||||
"50": metric.Percentile(0.5),
|
||||
"80": metric.Percentile(0.8),
|
||||
"95": metric.Percentile(0.95),
|
||||
},
|
||||
}
|
||||
|
||||
default:
|
||||
root[name] = "Unknown metric type"
|
||||
}
|
||||
} else {
|
||||
switch metric := metric.(type) {
|
||||
case metrics.Meter:
|
||||
root[name] = map[string]interface{}{
|
||||
"Avg01Min": format(metric.Rate1()*60, metric.Rate1()),
|
||||
"Avg05Min": format(metric.Rate5()*300, metric.Rate5()),
|
||||
"Avg15Min": format(metric.Rate15()*900, metric.Rate15()),
|
||||
"Overall": format(float64(metric.Count()), metric.RateMean()),
|
||||
}
|
||||
|
||||
case metrics.Timer:
|
||||
root[name] = map[string]interface{}{
|
||||
"Avg01Min": format(metric.Rate1()*60, metric.Rate1()),
|
||||
"Avg05Min": format(metric.Rate5()*300, metric.Rate5()),
|
||||
"Avg15Min": format(metric.Rate15()*900, metric.Rate15()),
|
||||
"Overall": format(float64(metric.Count()), metric.RateMean()),
|
||||
"Maximum": time.Duration(metric.Max()).String(),
|
||||
"Minimum": time.Duration(metric.Min()).String(),
|
||||
"Percentiles": map[string]interface{}{
|
||||
"5": time.Duration(metric.Percentile(0.05)).String(),
|
||||
"20": time.Duration(metric.Percentile(0.2)).String(),
|
||||
"50": time.Duration(metric.Percentile(0.5)).String(),
|
||||
"80": time.Duration(metric.Percentile(0.8)).String(),
|
||||
"95": time.Duration(metric.Percentile(0.95)).String(),
|
||||
},
|
||||
}
|
||||
|
||||
default:
|
||||
root[name] = "Unknown metric type"
|
||||
}
|
||||
}
|
||||
})
|
||||
return counters, nil
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
|
||||
"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
|
||||
}
|
||||
|
||||
type MetricsArgs struct {
|
||||
Raw bool
|
||||
}
|
||||
|
||||
func (args *MetricsArgs) 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 fmt.Errorf("metricsArgs needs 0, 1 arguments")
|
||||
}
|
||||
// default values when not provided
|
||||
if len(obj) >= 1 && obj[0] != nil {
|
||||
if value, ok := obj[0].(bool); !ok {
|
||||
return fmt.Errorf("invalid argument %v", reflect.TypeOf(obj[0]))
|
||||
} else {
|
||||
args.Raw = value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
const Debug_JS = `
|
||||
web3._extend({
|
||||
property: 'debug',
|
||||
methods:
|
||||
[
|
||||
new web3._extend.Method({
|
||||
name: 'printBlock',
|
||||
call: 'debug_printBlock',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getBlockRlp',
|
||||
call: 'debug_getBlockRlp',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setHead',
|
||||
call: 'debug_setHead',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'processBlock',
|
||||
call: 'debug_processBlock',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'seedHash',
|
||||
call: 'debug_seedHash',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'dumpBlock',
|
||||
call: 'debug_dumpBlock',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'metrics',
|
||||
call: 'debug_metrics',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'verbosity',
|
||||
call: 'debug_verbosity',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.utils.fromDecimal]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'vmodule',
|
||||
call: 'debug_vmodule',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
],
|
||||
properties:
|
||||
[
|
||||
]
|
||||
});
|
||||
`
|
721
rpc/api/eth.go
721
rpc/api/eth.go
@ -1,721 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/natspec"
|
||||
"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"
|
||||
"gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
const (
|
||||
EthApiVersion = "1.0"
|
||||
)
|
||||
|
||||
// eth api provider
|
||||
// See https://github.com/ethereum/wiki/wiki/JSON-RPC
|
||||
type ethApi struct {
|
||||
xeth *xeth.XEth
|
||||
ethereum *eth.Ethereum
|
||||
methods map[string]ethhandler
|
||||
codec codec.ApiCoder
|
||||
}
|
||||
|
||||
// eth callback handler
|
||||
type ethhandler func(*ethApi, *shared.Request) (interface{}, error)
|
||||
|
||||
var (
|
||||
ethMapping = map[string]ethhandler{
|
||||
"eth_accounts": (*ethApi).Accounts,
|
||||
"eth_blockNumber": (*ethApi).BlockNumber,
|
||||
"eth_getBalance": (*ethApi).GetBalance,
|
||||
"eth_protocolVersion": (*ethApi).ProtocolVersion,
|
||||
"eth_coinbase": (*ethApi).Coinbase,
|
||||
"eth_mining": (*ethApi).IsMining,
|
||||
"eth_syncing": (*ethApi).IsSyncing,
|
||||
"eth_gasPrice": (*ethApi).GasPrice,
|
||||
"eth_getStorage": (*ethApi).GetStorage,
|
||||
"eth_storageAt": (*ethApi).GetStorage,
|
||||
"eth_getStorageAt": (*ethApi).GetStorageAt,
|
||||
"eth_getTransactionCount": (*ethApi).GetTransactionCount,
|
||||
"eth_getBlockTransactionCountByHash": (*ethApi).GetBlockTransactionCountByHash,
|
||||
"eth_getBlockTransactionCountByNumber": (*ethApi).GetBlockTransactionCountByNumber,
|
||||
"eth_getUncleCountByBlockHash": (*ethApi).GetUncleCountByBlockHash,
|
||||
"eth_getUncleCountByBlockNumber": (*ethApi).GetUncleCountByBlockNumber,
|
||||
"eth_getData": (*ethApi).GetData,
|
||||
"eth_getCode": (*ethApi).GetData,
|
||||
"eth_getNatSpec": (*ethApi).GetNatSpec,
|
||||
"eth_sign": (*ethApi).Sign,
|
||||
"eth_sendRawTransaction": (*ethApi).SubmitTransaction,
|
||||
"eth_submitTransaction": (*ethApi).SubmitTransaction,
|
||||
"eth_sendTransaction": (*ethApi).SendTransaction,
|
||||
"eth_signTransaction": (*ethApi).SignTransaction,
|
||||
"eth_transact": (*ethApi).SendTransaction,
|
||||
"eth_estimateGas": (*ethApi).EstimateGas,
|
||||
"eth_call": (*ethApi).Call,
|
||||
"eth_flush": (*ethApi).Flush,
|
||||
"eth_getBlockByHash": (*ethApi).GetBlockByHash,
|
||||
"eth_getBlockByNumber": (*ethApi).GetBlockByNumber,
|
||||
"eth_getTransactionByHash": (*ethApi).GetTransactionByHash,
|
||||
"eth_getTransactionByBlockNumberAndIndex": (*ethApi).GetTransactionByBlockNumberAndIndex,
|
||||
"eth_getTransactionByBlockHashAndIndex": (*ethApi).GetTransactionByBlockHashAndIndex,
|
||||
"eth_getUncleByBlockHashAndIndex": (*ethApi).GetUncleByBlockHashAndIndex,
|
||||
"eth_getUncleByBlockNumberAndIndex": (*ethApi).GetUncleByBlockNumberAndIndex,
|
||||
"eth_getCompilers": (*ethApi).GetCompilers,
|
||||
"eth_compileSolidity": (*ethApi).CompileSolidity,
|
||||
"eth_newFilter": (*ethApi).NewFilter,
|
||||
"eth_newBlockFilter": (*ethApi).NewBlockFilter,
|
||||
"eth_newPendingTransactionFilter": (*ethApi).NewPendingTransactionFilter,
|
||||
"eth_uninstallFilter": (*ethApi).UninstallFilter,
|
||||
"eth_getFilterChanges": (*ethApi).GetFilterChanges,
|
||||
"eth_getFilterLogs": (*ethApi).GetFilterLogs,
|
||||
"eth_getLogs": (*ethApi).GetLogs,
|
||||
"eth_hashrate": (*ethApi).Hashrate,
|
||||
"eth_getWork": (*ethApi).GetWork,
|
||||
"eth_submitWork": (*ethApi).SubmitWork,
|
||||
"eth_submitHashrate": (*ethApi).SubmitHashrate,
|
||||
"eth_resend": (*ethApi).Resend,
|
||||
"eth_pendingTransactions": (*ethApi).PendingTransactions,
|
||||
"eth_getTransactionReceipt": (*ethApi).GetTransactionReceipt,
|
||||
}
|
||||
)
|
||||
|
||||
// create new ethApi instance
|
||||
func NewEthApi(xeth *xeth.XEth, eth *eth.Ethereum, codec codec.Codec) *ethApi {
|
||||
return ðApi{xeth, eth, ethMapping, codec.New(nil)}
|
||||
}
|
||||
|
||||
// collection with supported methods
|
||||
func (self *ethApi) 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 *ethApi) 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 *ethApi) Name() string {
|
||||
return shared.EthApiName
|
||||
}
|
||||
|
||||
func (self *ethApi) ApiVersion() string {
|
||||
return EthApiVersion
|
||||
}
|
||||
|
||||
func (self *ethApi) Accounts(req *shared.Request) (interface{}, error) {
|
||||
return self.xeth.Accounts(), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) Hashrate(req *shared.Request) (interface{}, error) {
|
||||
return newHexNum(self.xeth.HashRate()), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) BlockNumber(req *shared.Request) (interface{}, error) {
|
||||
num := self.xeth.CurrentBlock().Number()
|
||||
return newHexNum(num.Bytes()), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) GetBalance(req *shared.Request) (interface{}, error) {
|
||||
args := new(GetBalanceArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
return self.xeth.AtStateNum(args.BlockNumber).BalanceAt(args.Address), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) ProtocolVersion(req *shared.Request) (interface{}, error) {
|
||||
return self.xeth.EthVersion(), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) Coinbase(req *shared.Request) (interface{}, error) {
|
||||
return newHexData(self.xeth.Coinbase()), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) IsMining(req *shared.Request) (interface{}, error) {
|
||||
return self.xeth.IsMining(), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) IsSyncing(req *shared.Request) (interface{}, error) {
|
||||
origin, current, height := self.ethereum.Downloader().Progress()
|
||||
if current < height {
|
||||
return map[string]interface{}{
|
||||
"startingBlock": newHexNum(big.NewInt(int64(origin)).Bytes()),
|
||||
"currentBlock": newHexNum(big.NewInt(int64(current)).Bytes()),
|
||||
"highestBlock": newHexNum(big.NewInt(int64(height)).Bytes()),
|
||||
}, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (self *ethApi) GasPrice(req *shared.Request) (interface{}, error) {
|
||||
return newHexNum(self.xeth.DefaultGasPrice().Bytes()), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) GetStorage(req *shared.Request) (interface{}, error) {
|
||||
args := new(GetStorageArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
return self.xeth.AtStateNum(args.BlockNumber).State().SafeGet(args.Address).Storage(), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) GetStorageAt(req *shared.Request) (interface{}, error) {
|
||||
args := new(GetStorageAtArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
return self.xeth.AtStateNum(args.BlockNumber).StorageAt(args.Address, args.Key), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) GetTransactionCount(req *shared.Request) (interface{}, error) {
|
||||
args := new(GetTxCountArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
count := self.xeth.AtStateNum(args.BlockNumber).TxCountAt(args.Address)
|
||||
return fmt.Sprintf("%#x", count), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) GetBlockTransactionCountByHash(req *shared.Request) (interface{}, error) {
|
||||
args := new(HashArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
block := self.xeth.EthBlockByHash(args.Hash)
|
||||
if block == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return fmt.Sprintf("%#x", len(block.Transactions())), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) GetBlockTransactionCountByNumber(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, nil
|
||||
}
|
||||
return fmt.Sprintf("%#x", len(block.Transactions())), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) GetUncleCountByBlockHash(req *shared.Request) (interface{}, error) {
|
||||
args := new(HashArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
block := self.xeth.EthBlockByHash(args.Hash)
|
||||
if block == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return fmt.Sprintf("%#x", len(block.Uncles())), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) GetUncleCountByBlockNumber(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, nil
|
||||
}
|
||||
return fmt.Sprintf("%#x", len(block.Uncles())), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) GetData(req *shared.Request) (interface{}, error) {
|
||||
args := new(GetDataArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
v := self.xeth.AtStateNum(args.BlockNumber).CodeAtBytes(args.Address)
|
||||
return newHexData(v), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) Sign(req *shared.Request) (interface{}, error) {
|
||||
args := new(NewSigArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
v, err := self.xeth.Sign(args.From, args.Data, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (self *ethApi) SubmitTransaction(req *shared.Request) (interface{}, error) {
|
||||
args := new(NewDataArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
v, err := self.xeth.PushTx(args.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// JsonTransaction is returned as response by the JSON RPC. It contains the
|
||||
// signed RLP encoded transaction as Raw and the signed transaction object as Tx.
|
||||
type JsonTransaction struct {
|
||||
Raw string `json:"raw"`
|
||||
Tx *tx `json:"tx"`
|
||||
}
|
||||
|
||||
func (self *ethApi) SignTransaction(req *shared.Request) (interface{}, error) {
|
||||
args := new(NewTxArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
// nonce may be nil ("guess" mode)
|
||||
var nonce string
|
||||
if args.Nonce != nil {
|
||||
nonce = args.Nonce.String()
|
||||
}
|
||||
|
||||
var gas, price string
|
||||
if args.Gas != nil {
|
||||
gas = args.Gas.String()
|
||||
}
|
||||
if args.GasPrice != nil {
|
||||
price = args.GasPrice.String()
|
||||
}
|
||||
tx, err := self.xeth.SignTransaction(args.From, args.To, nonce, args.Value.String(), gas, price, args.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := rlp.EncodeToBytes(tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return JsonTransaction{"0x" + common.Bytes2Hex(data), newTx(tx)}, nil
|
||||
}
|
||||
|
||||
func (self *ethApi) SendTransaction(req *shared.Request) (interface{}, error) {
|
||||
args := new(NewTxArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
// nonce may be nil ("guess" mode)
|
||||
var nonce string
|
||||
if args.Nonce != nil {
|
||||
nonce = args.Nonce.String()
|
||||
}
|
||||
|
||||
var gas, price string
|
||||
if args.Gas != nil {
|
||||
gas = args.Gas.String()
|
||||
}
|
||||
if args.GasPrice != nil {
|
||||
price = args.GasPrice.String()
|
||||
}
|
||||
v, err := self.xeth.Transact(args.From, args.To, nonce, args.Value.String(), gas, price, args.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (self *ethApi) GetNatSpec(req *shared.Request) (interface{}, error) {
|
||||
args := new(NewTxArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
var jsontx = fmt.Sprintf(`{"params":[{"to":"%s","data": "%s"}]}`, args.To, args.Data)
|
||||
notice := natspec.GetNotice(self.xeth, jsontx, self.ethereum.HTTPClient())
|
||||
|
||||
return notice, nil
|
||||
}
|
||||
|
||||
func (self *ethApi) EstimateGas(req *shared.Request) (interface{}, error) {
|
||||
_, gas, err := self.doCall(req.Params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO unwrap the parent method's ToHex call
|
||||
if len(gas) == 0 {
|
||||
return newHexNum(0), nil
|
||||
} else {
|
||||
return newHexNum(common.String2Big(gas)), err
|
||||
}
|
||||
}
|
||||
|
||||
func (self *ethApi) Call(req *shared.Request) (interface{}, error) {
|
||||
v, _, err := self.doCall(req.Params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO unwrap the parent method's ToHex call
|
||||
if v == "0x0" {
|
||||
return newHexData([]byte{}), nil
|
||||
} else {
|
||||
return newHexData(common.FromHex(v)), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *ethApi) Flush(req *shared.Request) (interface{}, error) {
|
||||
return nil, shared.NewNotImplementedError(req.Method)
|
||||
}
|
||||
|
||||
func (self *ethApi) doCall(params json.RawMessage) (string, string, error) {
|
||||
args := new(CallArgs)
|
||||
if err := self.codec.Decode(params, &args); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return self.xeth.AtStateNum(args.BlockNumber).Call(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data)
|
||||
}
|
||||
|
||||
func (self *ethApi) GetBlockByHash(req *shared.Request) (interface{}, error) {
|
||||
args := new(GetBlockByHashArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
block := self.xeth.EthBlockByHash(args.BlockHash)
|
||||
if block == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return NewBlockRes(block, self.xeth.Td(block.Hash()), args.IncludeTxs), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) GetBlockByNumber(req *shared.Request) (interface{}, error) {
|
||||
args := new(GetBlockByNumberArgs)
|
||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
block := self.xeth.EthBlockByNumber(args.BlockNumber)
|
||||
if block == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return NewBlockRes(block, self.xeth.Td(block.Hash()), args.IncludeTxs), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) GetTransactionByHash(req *shared.Request) (interface{}, error) {
|
||||
args := new(HashArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
tx, bhash, bnum, txi := self.xeth.EthTransactionByHash(args.Hash)
|
||||
if tx != nil {
|
||||
v := NewTransactionRes(tx)
|
||||
// if the blockhash is 0, assume this is a pending transaction
|
||||
if bytes.Compare(bhash.Bytes(), bytes.Repeat([]byte{0}, 32)) != 0 {
|
||||
v.BlockHash = newHexData(bhash)
|
||||
v.BlockNumber = newHexNum(bnum)
|
||||
v.TxIndex = newHexNum(txi)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (self *ethApi) GetTransactionByBlockHashAndIndex(req *shared.Request) (interface{}, error) {
|
||||
args := new(HashIndexArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
raw := self.xeth.EthBlockByHash(args.Hash)
|
||||
if raw == nil {
|
||||
return nil, nil
|
||||
}
|
||||
block := NewBlockRes(raw, self.xeth.Td(raw.Hash()), true)
|
||||
if args.Index >= int64(len(block.Transactions)) || args.Index < 0 {
|
||||
return nil, nil
|
||||
} else {
|
||||
return block.Transactions[args.Index], nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *ethApi) GetTransactionByBlockNumberAndIndex(req *shared.Request) (interface{}, error) {
|
||||
args := new(BlockNumIndexArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
raw := self.xeth.EthBlockByNumber(args.BlockNumber)
|
||||
if raw == nil {
|
||||
return nil, nil
|
||||
}
|
||||
block := NewBlockRes(raw, self.xeth.Td(raw.Hash()), true)
|
||||
if args.Index >= int64(len(block.Transactions)) || args.Index < 0 {
|
||||
// return NewValidationError("Index", "does not exist")
|
||||
return nil, nil
|
||||
}
|
||||
return block.Transactions[args.Index], nil
|
||||
}
|
||||
|
||||
func (self *ethApi) GetUncleByBlockHashAndIndex(req *shared.Request) (interface{}, error) {
|
||||
args := new(HashIndexArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
raw := self.xeth.EthBlockByHash(args.Hash)
|
||||
if raw == nil {
|
||||
return nil, nil
|
||||
}
|
||||
block := NewBlockRes(raw, self.xeth.Td(raw.Hash()), false)
|
||||
if args.Index >= int64(len(block.Uncles)) || args.Index < 0 {
|
||||
// return NewValidationError("Index", "does not exist")
|
||||
return nil, nil
|
||||
}
|
||||
return block.Uncles[args.Index], nil
|
||||
}
|
||||
|
||||
func (self *ethApi) GetUncleByBlockNumberAndIndex(req *shared.Request) (interface{}, error) {
|
||||
args := new(BlockNumIndexArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
raw := self.xeth.EthBlockByNumber(args.BlockNumber)
|
||||
if raw == nil {
|
||||
return nil, nil
|
||||
}
|
||||
block := NewBlockRes(raw, self.xeth.Td(raw.Hash()), true)
|
||||
if args.Index >= int64(len(block.Uncles)) || args.Index < 0 {
|
||||
return nil, nil
|
||||
} else {
|
||||
return block.Uncles[args.Index], nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *ethApi) GetCompilers(req *shared.Request) (interface{}, error) {
|
||||
var lang string
|
||||
if solc, _ := self.xeth.Solc(); solc != nil {
|
||||
lang = "Solidity"
|
||||
}
|
||||
c := []string{lang}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (self *ethApi) CompileSolidity(req *shared.Request) (interface{}, error) {
|
||||
solc, _ := self.xeth.Solc()
|
||||
if solc == nil {
|
||||
return nil, shared.NewNotAvailableError(req.Method, "solc (solidity compiler) not found")
|
||||
}
|
||||
|
||||
args := new(SourceArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
contracts, err := solc.Compile(args.Source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return contracts, nil
|
||||
}
|
||||
|
||||
func (self *ethApi) NewFilter(req *shared.Request) (interface{}, error) {
|
||||
args := new(BlockFilterArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
id := self.xeth.NewLogFilter(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics)
|
||||
return newHexNum(big.NewInt(int64(id)).Bytes()), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) NewBlockFilter(req *shared.Request) (interface{}, error) {
|
||||
return newHexNum(self.xeth.NewBlockFilter()), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) NewPendingTransactionFilter(req *shared.Request) (interface{}, error) {
|
||||
return newHexNum(self.xeth.NewTransactionFilter()), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) UninstallFilter(req *shared.Request) (interface{}, error) {
|
||||
args := new(FilterIdArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
return self.xeth.UninstallFilter(args.Id), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) GetFilterChanges(req *shared.Request) (interface{}, error) {
|
||||
args := new(FilterIdArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
switch self.xeth.GetFilterType(args.Id) {
|
||||
case xeth.BlockFilterTy:
|
||||
return NewHashesRes(self.xeth.BlockFilterChanged(args.Id)), nil
|
||||
case xeth.TransactionFilterTy:
|
||||
return NewHashesRes(self.xeth.TransactionFilterChanged(args.Id)), nil
|
||||
case xeth.LogFilterTy:
|
||||
return NewLogsRes(self.xeth.LogFilterChanged(args.Id)), nil
|
||||
default:
|
||||
return []string{}, nil // reply empty string slice
|
||||
}
|
||||
}
|
||||
|
||||
func (self *ethApi) GetFilterLogs(req *shared.Request) (interface{}, error) {
|
||||
args := new(FilterIdArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
return NewLogsRes(self.xeth.Logs(args.Id)), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) GetLogs(req *shared.Request) (interface{}, error) {
|
||||
args := new(BlockFilterArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
return NewLogsRes(self.xeth.AllLogs(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics)), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) GetWork(req *shared.Request) (interface{}, error) {
|
||||
self.xeth.SetMining(true, 0)
|
||||
ret, err := self.xeth.RemoteMining().GetWork()
|
||||
if err != nil {
|
||||
return nil, shared.NewNotReadyError("mining work")
|
||||
} else {
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *ethApi) SubmitWork(req *shared.Request) (interface{}, error) {
|
||||
args := new(SubmitWorkArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
return self.xeth.RemoteMining().SubmitWork(args.Nonce, common.HexToHash(args.Digest), common.HexToHash(args.Header)), nil
|
||||
}
|
||||
|
||||
func (self *ethApi) SubmitHashrate(req *shared.Request) (interface{}, error) {
|
||||
args := new(SubmitHashRateArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return false, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
self.xeth.RemoteMining().SubmitHashrate(common.HexToHash(args.Id), args.Rate)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (self *ethApi) Resend(req *shared.Request) (interface{}, error) {
|
||||
args := new(ResendArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
from := common.HexToAddress(args.Tx.From)
|
||||
|
||||
pending := self.ethereum.TxPool().GetTransactions()
|
||||
for _, p := range pending {
|
||||
if pFrom, err := p.From(); err == nil && pFrom == from && p.SigHash() == args.Tx.tx.SigHash() {
|
||||
self.ethereum.TxPool().RemoveTx(common.HexToHash(args.Tx.Hash))
|
||||
return self.xeth.Transact(args.Tx.From, args.Tx.To, args.Tx.Nonce, args.Tx.Value, args.GasLimit, args.GasPrice, args.Tx.Data)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Transaction %s not found", args.Tx.Hash)
|
||||
}
|
||||
|
||||
func (self *ethApi) PendingTransactions(req *shared.Request) (interface{}, error) {
|
||||
txs := self.ethereum.TxPool().GetTransactions()
|
||||
|
||||
// grab the accounts from the account manager. This will help with determining which
|
||||
// transactions should be returned.
|
||||
accounts, err := self.ethereum.AccountManager().Accounts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add the accouns to a new set
|
||||
accountSet := set.New()
|
||||
for _, account := range accounts {
|
||||
accountSet.Add(account.Address)
|
||||
}
|
||||
|
||||
var ltxs []*tx
|
||||
for _, tx := range txs {
|
||||
if from, _ := tx.From(); accountSet.Has(from) {
|
||||
ltxs = append(ltxs, newTx(tx))
|
||||
}
|
||||
}
|
||||
|
||||
return ltxs, nil
|
||||
}
|
||||
|
||||
func (self *ethApi) GetTransactionReceipt(req *shared.Request) (interface{}, error) {
|
||||
args := new(HashArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return nil, shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
txhash := common.BytesToHash(common.FromHex(args.Hash))
|
||||
tx, bhash, bnum, txi := self.xeth.EthTransactionByHash(args.Hash)
|
||||
rec := self.xeth.GetTxReceipt(txhash)
|
||||
// We could have an error of "not found". Should disambiguate
|
||||
// if err != nil {
|
||||
// return err, nil
|
||||
// }
|
||||
if rec != nil && tx != nil {
|
||||
v := NewReceiptRes(rec)
|
||||
v.BlockHash = newHexData(bhash)
|
||||
v.BlockNumber = newHexNum(bnum)
|
||||
v.TransactionIndex = newHexNum(txi)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
1104
rpc/api/eth_args.go
1104
rpc/api/eth_args.go
File diff suppressed because it is too large
Load Diff
@ -1,66 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
// JS api provided by web3.js
|
||||
// eth_sign not standard
|
||||
|
||||
const Eth_JS = `
|
||||
web3._extend({
|
||||
property: 'eth',
|
||||
methods:
|
||||
[
|
||||
new web3._extend.Method({
|
||||
name: 'sign',
|
||||
call: 'eth_sign',
|
||||
params: 2,
|
||||
inputFormatter: [web3._extend.utils.toAddress, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'resend',
|
||||
call: 'eth_resend',
|
||||
params: 3,
|
||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter, web3._extend.utils.fromDecimal, web3._extend.utils.fromDecimal]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getNatSpec',
|
||||
call: 'eth_getNatSpec',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'signTransaction',
|
||||
call: 'eth_signTransaction',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'submitTransaction',
|
||||
call: 'eth_submitTransaction',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter]
|
||||
})
|
||||
],
|
||||
properties:
|
||||
[
|
||||
new web3._extend.Property({
|
||||
name: 'pendingTransactions',
|
||||
getter: 'eth_pendingTransactions'
|
||||
})
|
||||
]
|
||||
});
|
||||
`
|
@ -1,88 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"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]shared.EthereumApi
|
||||
}
|
||||
|
||||
// create new merged api instance
|
||||
func newMergedApi(apis ...shared.EthereumApi) *MergedApi {
|
||||
mergedApi := new(MergedApi)
|
||||
mergedApi.apis = make(map[string]string, len(apis))
|
||||
mergedApi.methods = make(map[string]shared.EthereumApi)
|
||||
|
||||
for _, api := range apis {
|
||||
if api != nil {
|
||||
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) {
|
||||
glog.V(logger.Detail).Infof("%s %s", req.Method, req.Params)
|
||||
|
||||
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 shared.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
|
||||
}
|
177
rpc/api/miner.go
177
rpc/api/miner.go
@ -1,177 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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_setEtherbase": (*minerApi).SetEtherbase,
|
||||
"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 shared.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
|
||||
}
|
||||
|
||||
if err := self.ethereum.Miner().SetExtra([]byte(args.Data)); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
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) SetEtherbase(req *shared.Request) (interface{}, error) {
|
||||
args := new(SetEtherbaseArgs)
|
||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
||||
return false, err
|
||||
}
|
||||
self.ethereum.SetEtherbase(args.Etherbase)
|
||||
return nil, 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
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"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 SetEtherbaseArgs struct {
|
||||
Etherbase common.Address
|
||||
}
|
||||
|
||||
func (args *SetEtherbaseArgs) 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 addr, ok := obj[0].(string); ok {
|
||||
args.Etherbase = common.HexToAddress(addr)
|
||||
if (args.Etherbase == common.Address{}) {
|
||||
return shared.NewInvalidTypeError("Etherbase", "not a valid address")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return shared.NewInvalidTypeError("Etherbase", "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
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
const Miner_JS = `
|
||||
web3._extend({
|
||||
property: 'miner',
|
||||
methods:
|
||||
[
|
||||
new web3._extend.Method({
|
||||
name: 'start',
|
||||
call: 'miner_start',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'stop',
|
||||
call: 'miner_stop',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setEtherbase',
|
||||
call: 'miner_setEtherbase',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.formatInputInt],
|
||||
outputFormatter: web3._extend.formatters.formatOutputBool
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setExtra',
|
||||
call: 'miner_setExtra',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setGasPrice',
|
||||
call: 'miner_setGasPrice',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.utils.fromDecial]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'startAutoDAG',
|
||||
call: 'miner_startAutoDAG',
|
||||
params: 0,
|
||||
inputFormatter: []
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'stopAutoDAG',
|
||||
call: 'miner_stopAutoDAG',
|
||||
params: 0,
|
||||
inputFormatter: []
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'makeDAG',
|
||||
call: 'miner_makeDAG',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.inputDefaultBlockNumberFormatter]
|
||||
})
|
||||
],
|
||||
properties:
|
||||
[
|
||||
new web3._extend.Property({
|
||||
name: 'hashrate',
|
||||
getter: 'miner_hashrate',
|
||||
outputFormatter: web3._extend.utils.toDecimal
|
||||
})
|
||||
]
|
||||
});
|
||||
`
|
@ -1,99 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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_peerCount": (*netApi).PeerCount,
|
||||
"net_listening": (*netApi).IsListening,
|
||||
"net_version": (*netApi).Version,
|
||||
}
|
||||
)
|
||||
|
||||
// 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 shared.NetApiName
|
||||
}
|
||||
|
||||
func (self *netApi) ApiVersion() string {
|
||||
return NetApiVersion
|
||||
}
|
||||
|
||||
// Number of connected peers
|
||||
func (self *netApi) PeerCount(req *shared.Request) (interface{}, error) {
|
||||
return newHexNum(self.xeth.PeerCount()), nil
|
||||
}
|
||||
|
||||
func (self *netApi) IsListening(req *shared.Request) (interface{}, error) {
|
||||
return self.xeth.IsListening(), nil
|
||||
}
|
||||
|
||||
func (self *netApi) Version(req *shared.Request) (interface{}, error) {
|
||||
return self.xeth.NetworkVersion(), nil
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
const Net_JS = `
|
||||
web3._extend({
|
||||
property: 'net',
|
||||
methods:
|
||||
[
|
||||
new web3._extend.Method({
|
||||
name: 'addPeer',
|
||||
call: 'net_addPeer',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
})
|
||||
],
|
||||
properties:
|
||||
[
|
||||
new web3._extend.Property({
|
||||
name: 'version',
|
||||
getter: 'net_version'
|
||||
})
|
||||
]
|
||||
});
|
||||
`
|
@ -1,522 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
)
|
||||
|
||||
type hexdata struct {
|
||||
data []byte
|
||||
isNil bool
|
||||
}
|
||||
|
||||
func (d *hexdata) String() string {
|
||||
return "0x" + common.Bytes2Hex(d.data)
|
||||
}
|
||||
|
||||
func (d *hexdata) MarshalJSON() ([]byte, error) {
|
||||
if d.isNil {
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
return json.Marshal(d.String())
|
||||
}
|
||||
|
||||
func newHexData(input interface{}) *hexdata {
|
||||
d := new(hexdata)
|
||||
|
||||
if input == nil {
|
||||
d.isNil = true
|
||||
return d
|
||||
}
|
||||
switch input := input.(type) {
|
||||
case []byte:
|
||||
d.data = input
|
||||
case common.Hash:
|
||||
d.data = input.Bytes()
|
||||
case *common.Hash:
|
||||
if input == nil {
|
||||
d.isNil = true
|
||||
} else {
|
||||
d.data = input.Bytes()
|
||||
}
|
||||
case common.Address:
|
||||
d.data = input.Bytes()
|
||||
case *common.Address:
|
||||
if input == nil {
|
||||
d.isNil = true
|
||||
} else {
|
||||
d.data = input.Bytes()
|
||||
}
|
||||
case types.Bloom:
|
||||
d.data = input.Bytes()
|
||||
case *types.Bloom:
|
||||
if input == nil {
|
||||
d.isNil = true
|
||||
} else {
|
||||
d.data = input.Bytes()
|
||||
}
|
||||
case *big.Int:
|
||||
if input == nil {
|
||||
d.isNil = true
|
||||
} else {
|
||||
d.data = input.Bytes()
|
||||
}
|
||||
case int64:
|
||||
d.data = big.NewInt(input).Bytes()
|
||||
case uint64:
|
||||
buff := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(buff, input)
|
||||
d.data = buff
|
||||
case int:
|
||||
d.data = big.NewInt(int64(input)).Bytes()
|
||||
case uint:
|
||||
d.data = big.NewInt(int64(input)).Bytes()
|
||||
case int8:
|
||||
d.data = big.NewInt(int64(input)).Bytes()
|
||||
case uint8:
|
||||
d.data = big.NewInt(int64(input)).Bytes()
|
||||
case int16:
|
||||
d.data = big.NewInt(int64(input)).Bytes()
|
||||
case uint16:
|
||||
buff := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(buff, input)
|
||||
d.data = buff
|
||||
case int32:
|
||||
d.data = big.NewInt(int64(input)).Bytes()
|
||||
case uint32:
|
||||
buff := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(buff, input)
|
||||
d.data = buff
|
||||
case string: // hexstring
|
||||
// aaargh ffs TODO: avoid back-and-forth hex encodings where unneeded
|
||||
bytes, err := hex.DecodeString(strings.TrimPrefix(input, "0x"))
|
||||
if err != nil {
|
||||
d.isNil = true
|
||||
} else {
|
||||
d.data = bytes
|
||||
}
|
||||
default:
|
||||
d.isNil = true
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
type hexnum struct {
|
||||
data []byte
|
||||
isNil bool
|
||||
}
|
||||
|
||||
func (d *hexnum) String() string {
|
||||
// Get hex string from bytes
|
||||
out := common.Bytes2Hex(d.data)
|
||||
// Trim leading 0s
|
||||
out = strings.TrimLeft(out, "0")
|
||||
// Output "0x0" when value is 0
|
||||
if len(out) == 0 {
|
||||
out = "0"
|
||||
}
|
||||
return "0x" + out
|
||||
}
|
||||
|
||||
func (d *hexnum) MarshalJSON() ([]byte, error) {
|
||||
if d.isNil {
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
return json.Marshal(d.String())
|
||||
}
|
||||
|
||||
func newHexNum(input interface{}) *hexnum {
|
||||
d := new(hexnum)
|
||||
|
||||
d.data = newHexData(input).data
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
type BlockRes struct {
|
||||
fullTx bool
|
||||
|
||||
BlockNumber *hexnum `json:"number"`
|
||||
BlockHash *hexdata `json:"hash"`
|
||||
ParentHash *hexdata `json:"parentHash"`
|
||||
Nonce *hexdata `json:"nonce"`
|
||||
Sha3Uncles *hexdata `json:"sha3Uncles"`
|
||||
LogsBloom *hexdata `json:"logsBloom"`
|
||||
TransactionRoot *hexdata `json:"transactionsRoot"`
|
||||
StateRoot *hexdata `json:"stateRoot"`
|
||||
ReceiptRoot *hexdata `json:"receiptRoot"`
|
||||
Miner *hexdata `json:"miner"`
|
||||
Difficulty *hexnum `json:"difficulty"`
|
||||
TotalDifficulty *hexnum `json:"totalDifficulty"`
|
||||
Size *hexnum `json:"size"`
|
||||
ExtraData *hexdata `json:"extraData"`
|
||||
GasLimit *hexnum `json:"gasLimit"`
|
||||
GasUsed *hexnum `json:"gasUsed"`
|
||||
UnixTimestamp *hexnum `json:"timestamp"`
|
||||
Transactions []*TransactionRes `json:"transactions"`
|
||||
Uncles []*UncleRes `json:"uncles"`
|
||||
}
|
||||
|
||||
func (b *BlockRes) MarshalJSON() ([]byte, error) {
|
||||
if b.fullTx {
|
||||
var ext struct {
|
||||
BlockNumber *hexnum `json:"number"`
|
||||
BlockHash *hexdata `json:"hash"`
|
||||
ParentHash *hexdata `json:"parentHash"`
|
||||
Nonce *hexdata `json:"nonce"`
|
||||
Sha3Uncles *hexdata `json:"sha3Uncles"`
|
||||
LogsBloom *hexdata `json:"logsBloom"`
|
||||
TransactionRoot *hexdata `json:"transactionsRoot"`
|
||||
StateRoot *hexdata `json:"stateRoot"`
|
||||
ReceiptRoot *hexdata `json:"receiptRoot"`
|
||||
Miner *hexdata `json:"miner"`
|
||||
Difficulty *hexnum `json:"difficulty"`
|
||||
TotalDifficulty *hexnum `json:"totalDifficulty"`
|
||||
Size *hexnum `json:"size"`
|
||||
ExtraData *hexdata `json:"extraData"`
|
||||
GasLimit *hexnum `json:"gasLimit"`
|
||||
GasUsed *hexnum `json:"gasUsed"`
|
||||
UnixTimestamp *hexnum `json:"timestamp"`
|
||||
Transactions []*TransactionRes `json:"transactions"`
|
||||
Uncles []*hexdata `json:"uncles"`
|
||||
}
|
||||
|
||||
ext.BlockNumber = b.BlockNumber
|
||||
ext.BlockHash = b.BlockHash
|
||||
ext.ParentHash = b.ParentHash
|
||||
ext.Nonce = b.Nonce
|
||||
ext.Sha3Uncles = b.Sha3Uncles
|
||||
ext.LogsBloom = b.LogsBloom
|
||||
ext.TransactionRoot = b.TransactionRoot
|
||||
ext.StateRoot = b.StateRoot
|
||||
ext.ReceiptRoot = b.ReceiptRoot
|
||||
ext.Miner = b.Miner
|
||||
ext.Difficulty = b.Difficulty
|
||||
ext.TotalDifficulty = b.TotalDifficulty
|
||||
ext.Size = b.Size
|
||||
ext.ExtraData = b.ExtraData
|
||||
ext.GasLimit = b.GasLimit
|
||||
ext.GasUsed = b.GasUsed
|
||||
ext.UnixTimestamp = b.UnixTimestamp
|
||||
ext.Transactions = b.Transactions
|
||||
ext.Uncles = make([]*hexdata, len(b.Uncles))
|
||||
for i, u := range b.Uncles {
|
||||
ext.Uncles[i] = u.BlockHash
|
||||
}
|
||||
return json.Marshal(ext)
|
||||
} else {
|
||||
var ext struct {
|
||||
BlockNumber *hexnum `json:"number"`
|
||||
BlockHash *hexdata `json:"hash"`
|
||||
ParentHash *hexdata `json:"parentHash"`
|
||||
Nonce *hexdata `json:"nonce"`
|
||||
Sha3Uncles *hexdata `json:"sha3Uncles"`
|
||||
LogsBloom *hexdata `json:"logsBloom"`
|
||||
TransactionRoot *hexdata `json:"transactionsRoot"`
|
||||
StateRoot *hexdata `json:"stateRoot"`
|
||||
ReceiptRoot *hexdata `json:"receiptRoot"`
|
||||
Miner *hexdata `json:"miner"`
|
||||
Difficulty *hexnum `json:"difficulty"`
|
||||
TotalDifficulty *hexnum `json:"totalDifficulty"`
|
||||
Size *hexnum `json:"size"`
|
||||
ExtraData *hexdata `json:"extraData"`
|
||||
GasLimit *hexnum `json:"gasLimit"`
|
||||
GasUsed *hexnum `json:"gasUsed"`
|
||||
UnixTimestamp *hexnum `json:"timestamp"`
|
||||
Transactions []*hexdata `json:"transactions"`
|
||||
Uncles []*hexdata `json:"uncles"`
|
||||
}
|
||||
|
||||
ext.BlockNumber = b.BlockNumber
|
||||
ext.BlockHash = b.BlockHash
|
||||
ext.ParentHash = b.ParentHash
|
||||
ext.Nonce = b.Nonce
|
||||
ext.Sha3Uncles = b.Sha3Uncles
|
||||
ext.LogsBloom = b.LogsBloom
|
||||
ext.TransactionRoot = b.TransactionRoot
|
||||
ext.StateRoot = b.StateRoot
|
||||
ext.ReceiptRoot = b.ReceiptRoot
|
||||
ext.Miner = b.Miner
|
||||
ext.Difficulty = b.Difficulty
|
||||
ext.TotalDifficulty = b.TotalDifficulty
|
||||
ext.Size = b.Size
|
||||
ext.ExtraData = b.ExtraData
|
||||
ext.GasLimit = b.GasLimit
|
||||
ext.GasUsed = b.GasUsed
|
||||
ext.UnixTimestamp = b.UnixTimestamp
|
||||
ext.Transactions = make([]*hexdata, len(b.Transactions))
|
||||
for i, tx := range b.Transactions {
|
||||
ext.Transactions[i] = tx.Hash
|
||||
}
|
||||
ext.Uncles = make([]*hexdata, len(b.Uncles))
|
||||
for i, u := range b.Uncles {
|
||||
ext.Uncles[i] = u.BlockHash
|
||||
}
|
||||
return json.Marshal(ext)
|
||||
}
|
||||
}
|
||||
|
||||
func NewBlockRes(block *types.Block, td *big.Int, fullTx bool) *BlockRes {
|
||||
if block == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
res := new(BlockRes)
|
||||
res.fullTx = fullTx
|
||||
res.BlockNumber = newHexNum(block.Number())
|
||||
res.BlockHash = newHexData(block.Hash())
|
||||
res.ParentHash = newHexData(block.ParentHash())
|
||||
res.Nonce = newHexData(block.Nonce())
|
||||
res.Sha3Uncles = newHexData(block.UncleHash())
|
||||
res.LogsBloom = newHexData(block.Bloom())
|
||||
res.TransactionRoot = newHexData(block.TxHash())
|
||||
res.StateRoot = newHexData(block.Root())
|
||||
res.ReceiptRoot = newHexData(block.ReceiptHash())
|
||||
res.Miner = newHexData(block.Coinbase())
|
||||
res.Difficulty = newHexNum(block.Difficulty())
|
||||
res.TotalDifficulty = newHexNum(td)
|
||||
res.Size = newHexNum(block.Size().Int64())
|
||||
res.ExtraData = newHexData(block.Extra())
|
||||
res.GasLimit = newHexNum(block.GasLimit())
|
||||
res.GasUsed = newHexNum(block.GasUsed())
|
||||
res.UnixTimestamp = newHexNum(block.Time())
|
||||
|
||||
txs := block.Transactions()
|
||||
res.Transactions = make([]*TransactionRes, len(txs))
|
||||
for i, tx := range txs {
|
||||
res.Transactions[i] = NewTransactionRes(tx)
|
||||
res.Transactions[i].BlockHash = res.BlockHash
|
||||
res.Transactions[i].BlockNumber = res.BlockNumber
|
||||
res.Transactions[i].TxIndex = newHexNum(i)
|
||||
}
|
||||
|
||||
uncles := block.Uncles()
|
||||
res.Uncles = make([]*UncleRes, len(uncles))
|
||||
for i, uncle := range uncles {
|
||||
res.Uncles[i] = NewUncleRes(uncle)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
type TransactionRes struct {
|
||||
Hash *hexdata `json:"hash"`
|
||||
Nonce *hexnum `json:"nonce"`
|
||||
BlockHash *hexdata `json:"blockHash"`
|
||||
BlockNumber *hexnum `json:"blockNumber"`
|
||||
TxIndex *hexnum `json:"transactionIndex"`
|
||||
From *hexdata `json:"from"`
|
||||
To *hexdata `json:"to"`
|
||||
Value *hexnum `json:"value"`
|
||||
Gas *hexnum `json:"gas"`
|
||||
GasPrice *hexnum `json:"gasPrice"`
|
||||
Input *hexdata `json:"input"`
|
||||
}
|
||||
|
||||
func NewTransactionRes(tx *types.Transaction) *TransactionRes {
|
||||
if tx == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var v = new(TransactionRes)
|
||||
v.Hash = newHexData(tx.Hash())
|
||||
v.Nonce = newHexNum(tx.Nonce())
|
||||
// v.BlockHash =
|
||||
// v.BlockNumber =
|
||||
// v.TxIndex =
|
||||
from, _ := tx.From()
|
||||
v.From = newHexData(from)
|
||||
v.To = newHexData(tx.To())
|
||||
v.Value = newHexNum(tx.Value())
|
||||
v.Gas = newHexNum(tx.Gas())
|
||||
v.GasPrice = newHexNum(tx.GasPrice())
|
||||
v.Input = newHexData(tx.Data())
|
||||
return v
|
||||
}
|
||||
|
||||
type UncleRes struct {
|
||||
BlockNumber *hexnum `json:"number"`
|
||||
BlockHash *hexdata `json:"hash"`
|
||||
ParentHash *hexdata `json:"parentHash"`
|
||||
Nonce *hexdata `json:"nonce"`
|
||||
Sha3Uncles *hexdata `json:"sha3Uncles"`
|
||||
ReceiptHash *hexdata `json:"receiptHash"`
|
||||
LogsBloom *hexdata `json:"logsBloom"`
|
||||
TransactionRoot *hexdata `json:"transactionsRoot"`
|
||||
StateRoot *hexdata `json:"stateRoot"`
|
||||
Miner *hexdata `json:"miner"`
|
||||
Difficulty *hexnum `json:"difficulty"`
|
||||
ExtraData *hexdata `json:"extraData"`
|
||||
GasLimit *hexnum `json:"gasLimit"`
|
||||
GasUsed *hexnum `json:"gasUsed"`
|
||||
UnixTimestamp *hexnum `json:"timestamp"`
|
||||
}
|
||||
|
||||
func NewUncleRes(h *types.Header) *UncleRes {
|
||||
if h == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var v = new(UncleRes)
|
||||
v.BlockNumber = newHexNum(h.Number)
|
||||
v.BlockHash = newHexData(h.Hash())
|
||||
v.ParentHash = newHexData(h.ParentHash)
|
||||
v.Sha3Uncles = newHexData(h.UncleHash)
|
||||
v.Nonce = newHexData(h.Nonce[:])
|
||||
v.LogsBloom = newHexData(h.Bloom)
|
||||
v.TransactionRoot = newHexData(h.TxHash)
|
||||
v.StateRoot = newHexData(h.Root)
|
||||
v.Miner = newHexData(h.Coinbase)
|
||||
v.Difficulty = newHexNum(h.Difficulty)
|
||||
v.ExtraData = newHexData(h.Extra)
|
||||
v.GasLimit = newHexNum(h.GasLimit)
|
||||
v.GasUsed = newHexNum(h.GasUsed)
|
||||
v.UnixTimestamp = newHexNum(h.Time)
|
||||
v.ReceiptHash = newHexData(h.ReceiptHash)
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// type FilterLogRes struct {
|
||||
// Hash string `json:"hash"`
|
||||
// Address string `json:"address"`
|
||||
// Data string `json:"data"`
|
||||
// BlockNumber string `json:"blockNumber"`
|
||||
// TransactionHash string `json:"transactionHash"`
|
||||
// BlockHash string `json:"blockHash"`
|
||||
// TransactionIndex string `json:"transactionIndex"`
|
||||
// LogIndex string `json:"logIndex"`
|
||||
// }
|
||||
|
||||
// type FilterWhisperRes struct {
|
||||
// Hash string `json:"hash"`
|
||||
// From string `json:"from"`
|
||||
// To string `json:"to"`
|
||||
// Expiry string `json:"expiry"`
|
||||
// Sent string `json:"sent"`
|
||||
// Ttl string `json:"ttl"`
|
||||
// Topics string `json:"topics"`
|
||||
// Payload string `json:"payload"`
|
||||
// WorkProved string `json:"workProved"`
|
||||
// }
|
||||
|
||||
type ReceiptRes struct {
|
||||
TransactionHash *hexdata `json:"transactionHash"`
|
||||
TransactionIndex *hexnum `json:"transactionIndex"`
|
||||
BlockNumber *hexnum `json:"blockNumber"`
|
||||
BlockHash *hexdata `json:"blockHash"`
|
||||
CumulativeGasUsed *hexnum `json:"cumulativeGasUsed"`
|
||||
GasUsed *hexnum `json:"gasUsed"`
|
||||
ContractAddress *hexdata `json:"contractAddress"`
|
||||
Logs *[]interface{} `json:"logs"`
|
||||
}
|
||||
|
||||
func NewReceiptRes(rec *types.Receipt) *ReceiptRes {
|
||||
if rec == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var v = new(ReceiptRes)
|
||||
v.TransactionHash = newHexData(rec.TxHash)
|
||||
if rec.GasUsed != nil {
|
||||
v.GasUsed = newHexNum(rec.GasUsed.Bytes())
|
||||
}
|
||||
v.CumulativeGasUsed = newHexNum(rec.CumulativeGasUsed)
|
||||
|
||||
// If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
|
||||
if bytes.Compare(rec.ContractAddress.Bytes(), bytes.Repeat([]byte{0}, 20)) != 0 {
|
||||
v.ContractAddress = newHexData(rec.ContractAddress)
|
||||
}
|
||||
|
||||
logs := make([]interface{}, len(rec.Logs))
|
||||
for i, log := range rec.Logs {
|
||||
logs[i] = NewLogRes(log)
|
||||
}
|
||||
v.Logs = &logs
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func numString(raw interface{}) (*big.Int, error) {
|
||||
var number *big.Int
|
||||
// Parse as integer
|
||||
num, ok := raw.(float64)
|
||||
if ok {
|
||||
number = big.NewInt(int64(num))
|
||||
return number, nil
|
||||
}
|
||||
|
||||
// Parse as string/hexstring
|
||||
str, ok := raw.(string)
|
||||
if ok {
|
||||
number = common.String2Big(str)
|
||||
return number, nil
|
||||
}
|
||||
|
||||
return nil, shared.NewInvalidTypeError("", "not a number or string")
|
||||
}
|
||||
|
||||
func blockHeight(raw interface{}, number *int64) error {
|
||||
// Parse as integer
|
||||
num, ok := raw.(float64)
|
||||
if ok {
|
||||
*number = int64(num)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parse as string/hexstring
|
||||
str, ok := raw.(string)
|
||||
if !ok {
|
||||
return shared.NewInvalidTypeError("", "not a number or string")
|
||||
}
|
||||
|
||||
switch str {
|
||||
case "earliest":
|
||||
*number = 0
|
||||
case "latest":
|
||||
*number = -1
|
||||
case "pending":
|
||||
*number = -2
|
||||
default:
|
||||
if common.HasHexPrefix(str) {
|
||||
*number = common.String2Big(str).Int64()
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("blockNumber", "is not a valid string")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func blockHeightFromJson(msg json.RawMessage, number *int64) error {
|
||||
var raw interface{}
|
||||
if err := json.Unmarshal(msg, &raw); err != nil {
|
||||
return shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
return blockHeight(raw, number)
|
||||
}
|
@ -1,139 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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_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 shared.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())
|
||||
}
|
||||
var passwd string
|
||||
if args.Passphrase == nil {
|
||||
fe := self.xeth.Frontend()
|
||||
if fe == nil {
|
||||
return false, fmt.Errorf("unable to create account: unable to interact with user")
|
||||
}
|
||||
var ok bool
|
||||
passwd, ok = fe.AskPassword()
|
||||
if !ok {
|
||||
return false, fmt.Errorf("unable to create account: no password given")
|
||||
}
|
||||
} else {
|
||||
passwd = *args.Passphrase
|
||||
}
|
||||
am := self.ethereum.AccountManager()
|
||||
acc, err := am.NewAccount(passwd)
|
||||
return acc.Address.Hex(), 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())
|
||||
}
|
||||
|
||||
if args.Passphrase == nil {
|
||||
fe := self.xeth.Frontend()
|
||||
if fe == nil {
|
||||
return false, fmt.Errorf("No password provided")
|
||||
}
|
||||
return fe.UnlockAccount(common.HexToAddress(args.Address).Bytes()), nil
|
||||
}
|
||||
|
||||
am := self.ethereum.AccountManager()
|
||||
addr := common.HexToAddress(args.Address)
|
||||
|
||||
err := am.TimedUnlock(addr, *args.Passphrase, time.Duration(args.Duration)*time.Second)
|
||||
return err == nil, err
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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 && obj[0] != nil {
|
||||
if passphrasestr, ok := obj[0].(string); ok {
|
||||
args.Passphrase = &passphrasestr
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("passphrase", "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 = 0
|
||||
|
||||
if len(obj) < 1 {
|
||||
return shared.NewInsufficientParamsError(len(obj), 1)
|
||||
}
|
||||
|
||||
if addrstr, ok := obj[0].(string); ok {
|
||||
args.Address = addrstr
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("address", "not a string")
|
||||
}
|
||||
|
||||
if len(obj) >= 2 && obj[1] != nil {
|
||||
if passphrasestr, ok := obj[1].(string); ok {
|
||||
args.Passphrase = &passphrasestr
|
||||
} else {
|
||||
return shared.NewInvalidTypeError("passphrase", "not a string")
|
||||
}
|
||||
}
|
||||
|
||||
if len(obj) >= 3 && obj[2] != nil {
|
||||
if duration, ok := obj[2].(float64); ok {
|
||||
args.Duration = int(duration)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
const Personal_JS = `
|
||||
web3._extend({
|
||||
property: 'personal',
|
||||
methods:
|
||||
[
|
||||
new web3._extend.Method({
|
||||
name: 'newAccount',
|
||||
call: 'personal_newAccount',
|
||||
params: 1,
|
||||
inputFormatter: [null],
|
||||
outputFormatter: web3._extend.utils.toAddress
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'unlockAccount',
|
||||
call: 'personal_unlockAccount',
|
||||
params: 3,
|
||||
inputFormatter: [null, null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'lockAccount',
|
||||
call: 'personal_lockAccount',
|
||||
params: 1
|
||||
})
|
||||
],
|
||||
properties:
|
||||
[
|
||||
new web3._extend.Property({
|
||||
name: 'listAccounts',
|
||||
getter: 'personal_listAccounts'
|
||||
})
|
||||
]
|
||||
});
|
||||
`
|
196
rpc/api/shh.go
196
rpc/api/shh.go
@ -1,196 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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_getMessages": (*shhApi).GetMessages,
|
||||
"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 shared.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
|
||||
}
|
@ -1,174 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
const Shh_JS = `
|
||||
web3._extend({
|
||||
property: 'shh',
|
||||
methods:
|
||||
[
|
||||
|
||||
],
|
||||
properties:
|
||||
[
|
||||
new web3._extend.Property({
|
||||
name: 'version',
|
||||
getter: 'shh_version'
|
||||
})
|
||||
]
|
||||
});
|
||||
`
|
@ -1,92 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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 shared.TxPoolApiName
|
||||
}
|
||||
|
||||
func (self *txPoolApi) ApiVersion() string {
|
||||
return TxPoolApiVersion
|
||||
}
|
||||
|
||||
func (self *txPoolApi) Status(req *shared.Request) (interface{}, error) {
|
||||
pending, queue := self.ethereum.TxPool().Stats()
|
||||
return map[string]int{
|
||||
"pending": pending,
|
||||
"queued": queue,
|
||||
}, nil
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
const TxPool_JS = `
|
||||
web3._extend({
|
||||
property: 'txpool',
|
||||
methods:
|
||||
[
|
||||
],
|
||||
properties:
|
||||
[
|
||||
new web3._extend.Property({
|
||||
name: 'status',
|
||||
getter: 'txpool_status'
|
||||
})
|
||||
]
|
||||
});
|
||||
`
|
226
rpc/api/utils.go
226
rpc/api/utils.go
@ -1,226 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
)
|
||||
|
||||
var (
|
||||
// Mapping between the different methods each api supports
|
||||
AutoCompletion = map[string][]string{
|
||||
"admin": []string{
|
||||
"addPeer",
|
||||
"datadir",
|
||||
"enableUserAgent",
|
||||
"exportChain",
|
||||
"getContractInfo",
|
||||
"httpGet",
|
||||
"importChain",
|
||||
"nodeInfo",
|
||||
"peers",
|
||||
"register",
|
||||
"registerUrl",
|
||||
"saveInfo",
|
||||
"setGlobalRegistrar",
|
||||
"setHashReg",
|
||||
"setUrlHint",
|
||||
"setSolc",
|
||||
"sleep",
|
||||
"sleepBlocks",
|
||||
"startNatSpec",
|
||||
"startRPC",
|
||||
"stopNatSpec",
|
||||
"stopRPC",
|
||||
"verbosity",
|
||||
},
|
||||
"db": []string{
|
||||
"getString",
|
||||
"putString",
|
||||
"getHex",
|
||||
"putHex",
|
||||
},
|
||||
"debug": []string{
|
||||
"dumpBlock",
|
||||
"getBlockRlp",
|
||||
"metrics",
|
||||
"printBlock",
|
||||
"processBlock",
|
||||
"seedHash",
|
||||
"setHead",
|
||||
},
|
||||
"eth": []string{
|
||||
"accounts",
|
||||
"blockNumber",
|
||||
"call",
|
||||
"contract",
|
||||
"coinbase",
|
||||
"compile.lll",
|
||||
"compile.serpent",
|
||||
"compile.solidity",
|
||||
"contract",
|
||||
"defaultAccount",
|
||||
"defaultBlock",
|
||||
"estimateGas",
|
||||
"filter",
|
||||
"getBalance",
|
||||
"getBlock",
|
||||
"getBlockTransactionCount",
|
||||
"getBlockUncleCount",
|
||||
"getCode",
|
||||
"getNatSpec",
|
||||
"getCompilers",
|
||||
"gasPrice",
|
||||
"getStorageAt",
|
||||
"getTransaction",
|
||||
"getTransactionCount",
|
||||
"getTransactionFromBlock",
|
||||
"getTransactionReceipt",
|
||||
"getUncle",
|
||||
"hashrate",
|
||||
"mining",
|
||||
"namereg",
|
||||
"pendingTransactions",
|
||||
"resend",
|
||||
"sendRawTransaction",
|
||||
"sendTransaction",
|
||||
"sign",
|
||||
"syncing",
|
||||
},
|
||||
"miner": []string{
|
||||
"hashrate",
|
||||
"makeDAG",
|
||||
"setEtherbase",
|
||||
"setExtra",
|
||||
"setGasPrice",
|
||||
"startAutoDAG",
|
||||
"start",
|
||||
"stopAutoDAG",
|
||||
"stop",
|
||||
},
|
||||
"net": []string{
|
||||
"peerCount",
|
||||
"listening",
|
||||
},
|
||||
"personal": []string{
|
||||
"listAccounts",
|
||||
"newAccount",
|
||||
"unlockAccount",
|
||||
},
|
||||
"shh": []string{
|
||||
"post",
|
||||
"newIdentity",
|
||||
"hasIdentity",
|
||||
"newGroup",
|
||||
"addToGroup",
|
||||
"filter",
|
||||
},
|
||||
"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, stack *node.Node) ([]shared.EthereumApi, error) {
|
||||
if len(strings.TrimSpace(apistr)) == 0 {
|
||||
return nil, fmt.Errorf("Empty apistr provided")
|
||||
}
|
||||
|
||||
names := strings.Split(apistr, ",")
|
||||
apis := make([]shared.EthereumApi, len(names))
|
||||
|
||||
var eth *eth.Ethereum
|
||||
if stack != nil {
|
||||
if err := stack.Service(ð); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for i, name := range names {
|
||||
switch strings.ToLower(strings.TrimSpace(name)) {
|
||||
case shared.AdminApiName:
|
||||
apis[i] = NewAdminApi(xeth, stack, codec)
|
||||
case shared.DebugApiName:
|
||||
apis[i] = NewDebugApi(xeth, eth, codec)
|
||||
case shared.DbApiName:
|
||||
apis[i] = NewDbApi(xeth, eth, codec)
|
||||
case shared.EthApiName:
|
||||
apis[i] = NewEthApi(xeth, eth, codec)
|
||||
case shared.MinerApiName:
|
||||
apis[i] = NewMinerApi(eth, codec)
|
||||
case shared.NetApiName:
|
||||
apis[i] = NewNetApi(xeth, eth, codec)
|
||||
case shared.ShhApiName:
|
||||
apis[i] = NewShhApi(xeth, eth, codec)
|
||||
case shared.TxPoolApiName:
|
||||
apis[i] = NewTxPoolApi(xeth, eth, codec)
|
||||
case shared.PersonalApiName:
|
||||
apis[i] = NewPersonalApi(xeth, eth, codec)
|
||||
case shared.Web3ApiName:
|
||||
apis[i] = NewWeb3Api(xeth, codec)
|
||||
case "rpc": // gives information about the RPC interface
|
||||
continue
|
||||
default:
|
||||
return nil, fmt.Errorf("Unknown API '%s'", name)
|
||||
}
|
||||
}
|
||||
return apis, nil
|
||||
}
|
||||
|
||||
func Javascript(name string) string {
|
||||
switch strings.ToLower(strings.TrimSpace(name)) {
|
||||
case shared.AdminApiName:
|
||||
return Admin_JS
|
||||
case shared.DebugApiName:
|
||||
return Debug_JS
|
||||
case shared.DbApiName:
|
||||
return Db_JS
|
||||
case shared.EthApiName:
|
||||
return Eth_JS
|
||||
case shared.MinerApiName:
|
||||
return Miner_JS
|
||||
case shared.NetApiName:
|
||||
return Net_JS
|
||||
case shared.ShhApiName:
|
||||
return Shh_JS
|
||||
case shared.TxPoolApiName:
|
||||
return TxPool_JS
|
||||
case shared.PersonalApiName:
|
||||
return Personal_JS
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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 shared.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
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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, bool, error)
|
||||
// Parse response message from underlying stream
|
||||
ReadResponse() (interface{}, error)
|
||||
// Read raw message from underlying stream
|
||||
Recv() (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")
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package codec
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
)
|
||||
|
||||
const (
|
||||
READ_TIMEOUT = 60 // in seconds
|
||||
MAX_REQUEST_SIZE = 1024 * 1024
|
||||
MAX_RESPONSE_SIZE = 1024 * 1024
|
||||
)
|
||||
|
||||
// Json serialization support
|
||||
type JsonCodec struct {
|
||||
c net.Conn
|
||||
d *json.Decoder
|
||||
}
|
||||
|
||||
// Create new JSON coder instance
|
||||
func NewJsonCoder(conn net.Conn) ApiCoder {
|
||||
return &JsonCodec{
|
||||
c: conn,
|
||||
d: json.NewDecoder(conn),
|
||||
}
|
||||
}
|
||||
|
||||
// Read incoming request and parse it to RPC request
|
||||
func (self *JsonCodec) ReadRequest() (requests []*shared.Request, isBatch bool, err error) {
|
||||
deadline := time.Now().Add(READ_TIMEOUT * time.Second)
|
||||
if err := self.c.SetDeadline(deadline); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
var incoming json.RawMessage
|
||||
err = self.d.Decode(&incoming)
|
||||
if err == nil {
|
||||
isBatch = incoming[0] == '['
|
||||
if isBatch {
|
||||
requests = make([]*shared.Request, 0)
|
||||
err = json.Unmarshal(incoming, &requests)
|
||||
} else {
|
||||
requests = make([]*shared.Request, 1)
|
||||
var singleRequest shared.Request
|
||||
if err = json.Unmarshal(incoming, &singleRequest); err == nil {
|
||||
requests[0] = &singleRequest
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
self.c.Close()
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
func (self *JsonCodec) Recv() (interface{}, error) {
|
||||
var msg json.RawMessage
|
||||
err := self.d.Decode(&msg)
|
||||
if err != nil {
|
||||
self.c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return msg, err
|
||||
}
|
||||
|
||||
func (self *JsonCodec) ReadResponse() (interface{}, error) {
|
||||
in, err := self.Recv()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if msg, ok := in.(json.RawMessage); ok {
|
||||
var req *shared.Request
|
||||
if err = json.Unmarshal(msg, &req); err == nil && strings.HasPrefix(req.Method, "agent_") {
|
||||
return req, nil
|
||||
}
|
||||
|
||||
var failure *shared.ErrorResponse
|
||||
if err = json.Unmarshal(msg, &failure); err == nil && failure.Error != nil {
|
||||
return failure, fmt.Errorf(failure.Error.Message)
|
||||
}
|
||||
|
||||
var success *shared.SuccessResponse
|
||||
if err = json.Unmarshal(msg, &success); err == nil {
|
||||
return success, nil
|
||||
}
|
||||
}
|
||||
|
||||
return in, err
|
||||
}
|
||||
|
||||
// Decode data
|
||||
func (self *JsonCodec) Decode(data []byte, msg interface{}) error {
|
||||
return json.Unmarshal(data, msg)
|
||||
}
|
||||
|
||||
// Encode message
|
||||
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 {
|
||||
data, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
self.c.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
bytesWritten := 0
|
||||
|
||||
for bytesWritten < len(data) {
|
||||
n, err := self.c.Write(data[bytesWritten:])
|
||||
if err != nil {
|
||||
self.c.Close()
|
||||
return err
|
||||
}
|
||||
bytesWritten += n
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close decoder and encoder
|
||||
func (self *JsonCodec) Close() {
|
||||
self.c.Close()
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package codec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type jsonTestConn struct {
|
||||
buffer *bytes.Buffer
|
||||
}
|
||||
|
||||
func newJsonTestConn(data []byte) *jsonTestConn {
|
||||
return &jsonTestConn{
|
||||
buffer: bytes.NewBuffer(data),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *jsonTestConn) Read(p []byte) (n int, err error) {
|
||||
return self.buffer.Read(p)
|
||||
}
|
||||
|
||||
func (self *jsonTestConn) Write(p []byte) (n int, err error) {
|
||||
return self.buffer.Write(p)
|
||||
}
|
||||
|
||||
func (self *jsonTestConn) Close() error {
|
||||
// not implemented
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *jsonTestConn) LocalAddr() net.Addr {
|
||||
// not implemented
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *jsonTestConn) RemoteAddr() net.Addr {
|
||||
// not implemented
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *jsonTestConn) SetDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *jsonTestConn) SetReadDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *jsonTestConn) SetWriteDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestJsonDecoderWithValidRequest(t *testing.T) {
|
||||
reqdata := []byte(`{"jsonrpc":"2.0","method":"modules","params":[],"id":64}`)
|
||||
decoder := newJsonTestConn(reqdata)
|
||||
|
||||
jsonDecoder := NewJsonCoder(decoder)
|
||||
requests, batch, err := jsonDecoder.ReadRequest()
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Read valid request failed - %v", err)
|
||||
}
|
||||
|
||||
if len(requests) != 1 {
|
||||
t.Errorf("Expected to get a single request but got %d", len(requests))
|
||||
}
|
||||
|
||||
if batch {
|
||||
t.Errorf("Got batch indication while expecting single request")
|
||||
}
|
||||
|
||||
if requests[0].Id != float64(64) {
|
||||
t.Errorf("Expected req.Id == 64 but got %v", requests[0].Id)
|
||||
}
|
||||
|
||||
if requests[0].Method != "modules" {
|
||||
t.Errorf("Expected req.Method == 'modules' got '%s'", requests[0].Method)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJsonDecoderWithValidBatchRequest(t *testing.T) {
|
||||
reqdata := []byte(`[{"jsonrpc":"2.0","method":"modules","params":[],"id":64},
|
||||
{"jsonrpc":"2.0","method":"modules","params":[],"id":64}]`)
|
||||
decoder := newJsonTestConn(reqdata)
|
||||
|
||||
jsonDecoder := NewJsonCoder(decoder)
|
||||
requests, batch, err := jsonDecoder.ReadRequest()
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Read valid batch request failed - %v", err)
|
||||
}
|
||||
|
||||
if len(requests) != 2 {
|
||||
t.Errorf("Expected to get two requests but got %d", len(requests))
|
||||
}
|
||||
|
||||
if !batch {
|
||||
t.Errorf("Got no batch indication while expecting batch request")
|
||||
}
|
||||
|
||||
for i := 0; i < len(requests); i++ {
|
||||
if requests[i].Id != float64(64) {
|
||||
t.Errorf("Expected req.Id == 64 but got %v", requests[i].Id)
|
||||
}
|
||||
|
||||
if requests[i].Method != "modules" {
|
||||
t.Errorf("Expected req.Method == 'modules' got '%s'", requests[i].Method)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestJsonDecoderWithInvalidIncompleteMessage(t *testing.T) {
|
||||
reqdata := []byte(`{"jsonrpc":"2.0","method":"modules","pa`)
|
||||
decoder := newJsonTestConn(reqdata)
|
||||
|
||||
jsonDecoder := NewJsonCoder(decoder)
|
||||
requests, batch, err := jsonDecoder.ReadRequest()
|
||||
|
||||
if err != io.ErrUnexpectedEOF {
|
||||
t.Errorf("Expected to read an incomplete request err but got %v", err)
|
||||
}
|
||||
|
||||
// remaining message
|
||||
decoder.Write([]byte(`rams":[],"id:64"}`))
|
||||
requests, batch, err = jsonDecoder.ReadRequest()
|
||||
|
||||
if err == nil {
|
||||
t.Errorf("Expected an error but got nil")
|
||||
}
|
||||
|
||||
if len(requests) != 0 {
|
||||
t.Errorf("Expected to get no requests but got %d", len(requests))
|
||||
}
|
||||
|
||||
if batch {
|
||||
t.Errorf("Got batch indication while expecting non batch")
|
||||
}
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package comms
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"strconv"
|
||||
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
)
|
||||
|
||||
const (
|
||||
maxHttpSizeReqLength = 1024 * 1024 // 1MB
|
||||
)
|
||||
|
||||
var (
|
||||
// List with all API's which are offered over the in proc interface by default
|
||||
DefaultInProcApis = shared.AllApis
|
||||
|
||||
// List with all API's which are offered over the IPC interface by default
|
||||
DefaultIpcApis = shared.AllApis
|
||||
|
||||
// List with API's which are offered over thr HTTP/RPC interface by default
|
||||
DefaultHttpRpcApis = strings.Join([]string{
|
||||
shared.DbApiName, shared.EthApiName, shared.NetApiName, shared.Web3ApiName,
|
||||
}, ",")
|
||||
)
|
||||
|
||||
type EthereumClient interface {
|
||||
// Close underlying connection
|
||||
Close()
|
||||
// Send request
|
||||
Send(interface{}) error
|
||||
// Receive response
|
||||
Recv() (interface{}, error)
|
||||
// List with modules this client supports
|
||||
SupportedModules() (map[string]string, error)
|
||||
}
|
||||
|
||||
func handle(id int, conn net.Conn, api shared.EthereumApi, c codec.Codec) {
|
||||
codec := c.New(conn)
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
glog.Errorf("panic: %v\n", r)
|
||||
}
|
||||
codec.Close()
|
||||
}()
|
||||
|
||||
for {
|
||||
requests, isBatch, err := codec.ReadRequest()
|
||||
if err == io.EOF {
|
||||
return
|
||||
} else if err != nil {
|
||||
glog.V(logger.Debug).Infof("Closed IPC Conn %06d recv err - %v\n", id, err)
|
||||
return
|
||||
}
|
||||
|
||||
if isBatch {
|
||||
responses := make([]*interface{}, len(requests))
|
||||
responseCount := 0
|
||||
for _, req := range requests {
|
||||
res, err := api.Execute(req)
|
||||
if req.Id != nil {
|
||||
rpcResponse := shared.NewRpcResponse(req.Id, req.Jsonrpc, res, err)
|
||||
responses[responseCount] = rpcResponse
|
||||
responseCount += 1
|
||||
}
|
||||
}
|
||||
|
||||
err = codec.WriteResponse(responses[:responseCount])
|
||||
if err != nil {
|
||||
glog.V(logger.Debug).Infof("Closed IPC Conn %06d send err - %v\n", id, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
var rpcResponse interface{}
|
||||
res, err := api.Execute(requests[0])
|
||||
|
||||
rpcResponse = shared.NewRpcResponse(requests[0].Id, requests[0].Jsonrpc, res, err)
|
||||
err = codec.WriteResponse(rpcResponse)
|
||||
if err != nil {
|
||||
glog.V(logger.Debug).Infof("Closed IPC Conn %06d send err - %v\n", id, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Endpoint must be in the form of:
|
||||
// ${protocol}:${path}
|
||||
// e.g. ipc:/tmp/geth.ipc
|
||||
// rpc:localhost:8545
|
||||
func ClientFromEndpoint(endpoint string, c codec.Codec) (EthereumClient, error) {
|
||||
if strings.HasPrefix(endpoint, "ipc:") {
|
||||
cfg := IpcConfig{
|
||||
Endpoint: endpoint[4:],
|
||||
}
|
||||
return NewIpcClient(cfg, codec.JSON)
|
||||
}
|
||||
|
||||
if strings.HasPrefix(endpoint, "rpc:") {
|
||||
parts := strings.Split(endpoint, ":")
|
||||
addr := "http://localhost"
|
||||
port := uint(8545)
|
||||
if len(parts) >= 3 {
|
||||
addr = parts[1] + ":" + parts[2]
|
||||
}
|
||||
|
||||
if len(parts) >= 4 {
|
||||
p, err := strconv.Atoi(parts[3])
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
port = uint(p)
|
||||
}
|
||||
|
||||
cfg := HttpConfig{
|
||||
ListenAddress: addr,
|
||||
ListenPort: port,
|
||||
}
|
||||
|
||||
return NewHttpClient(cfg, codec.JSON), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Invalid endpoint")
|
||||
}
|
@ -1,345 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package comms
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
"github.com/rs/cors"
|
||||
)
|
||||
|
||||
const (
|
||||
serverIdleTimeout = 10 * time.Second // idle keep-alive connections
|
||||
serverReadTimeout = 15 * time.Second // per-request read timeout
|
||||
serverWriteTimeout = 15 * time.Second // per-request read timeout
|
||||
)
|
||||
|
||||
var (
|
||||
httpServerMu sync.Mutex
|
||||
httpServer *stopServer
|
||||
)
|
||||
|
||||
type HttpConfig struct {
|
||||
ListenAddress string
|
||||
ListenPort uint
|
||||
CorsDomain string
|
||||
}
|
||||
|
||||
// stopServer augments http.Server with idle connection tracking.
|
||||
// Idle keep-alive connections are shut down when Close is called.
|
||||
type stopServer struct {
|
||||
*http.Server
|
||||
l net.Listener
|
||||
// connection tracking state
|
||||
mu sync.Mutex
|
||||
shutdown bool // true when Stop has returned
|
||||
idle map[net.Conn]struct{}
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
codec codec.Codec
|
||||
api shared.EthereumApi
|
||||
}
|
||||
|
||||
// StartHTTP starts listening for RPC requests sent via HTTP.
|
||||
func StartHttp(cfg HttpConfig, codec codec.Codec, api shared.EthereumApi) error {
|
||||
httpServerMu.Lock()
|
||||
defer httpServerMu.Unlock()
|
||||
|
||||
addr := fmt.Sprintf("%s:%d", cfg.ListenAddress, cfg.ListenPort)
|
||||
if httpServer != nil {
|
||||
if addr != httpServer.Addr {
|
||||
return fmt.Errorf("RPC service already running on %s ", httpServer.Addr)
|
||||
}
|
||||
return nil // RPC service already running on given host/port
|
||||
}
|
||||
// Set up the request handler, wrapping it with CORS headers if configured.
|
||||
handler := http.Handler(&handler{codec, api})
|
||||
if len(cfg.CorsDomain) > 0 {
|
||||
opts := cors.Options{
|
||||
AllowedMethods: []string{"POST"},
|
||||
AllowedOrigins: strings.Split(cfg.CorsDomain, " "),
|
||||
}
|
||||
handler = cors.New(opts).Handler(handler)
|
||||
}
|
||||
// Start the server.
|
||||
s, err := listenHTTP(addr, handler)
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Can't listen on %s:%d: %v", cfg.ListenAddress, cfg.ListenPort, err)
|
||||
return err
|
||||
}
|
||||
httpServer = s
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
// Limit request size to resist DoS
|
||||
if req.ContentLength > maxHttpSizeReqLength {
|
||||
err := fmt.Errorf("Request too large")
|
||||
response := shared.NewRpcErrorResponse(-1, shared.JsonRpcVersion, -32700, err)
|
||||
sendJSON(w, &response)
|
||||
return
|
||||
}
|
||||
|
||||
defer req.Body.Close()
|
||||
payload, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Could not read request body")
|
||||
response := shared.NewRpcErrorResponse(-1, shared.JsonRpcVersion, -32700, err)
|
||||
sendJSON(w, &response)
|
||||
return
|
||||
}
|
||||
|
||||
c := h.codec.New(nil)
|
||||
var rpcReq shared.Request
|
||||
if err = c.Decode(payload, &rpcReq); err == nil {
|
||||
reply, err := h.api.Execute(&rpcReq)
|
||||
res := shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err)
|
||||
sendJSON(w, &res)
|
||||
return
|
||||
}
|
||||
|
||||
var reqBatch []shared.Request
|
||||
if err = c.Decode(payload, &reqBatch); err == nil {
|
||||
resBatch := make([]*interface{}, len(reqBatch))
|
||||
resCount := 0
|
||||
for i, rpcReq := range reqBatch {
|
||||
reply, err := h.api.Execute(&rpcReq)
|
||||
if rpcReq.Id != nil { // this leaves nil entries in the response batch for later removal
|
||||
resBatch[i] = shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err)
|
||||
resCount += 1
|
||||
}
|
||||
}
|
||||
// make response omitting nil entries
|
||||
sendJSON(w, resBatch[:resCount])
|
||||
return
|
||||
}
|
||||
|
||||
// invalid request
|
||||
err = fmt.Errorf("Could not decode request")
|
||||
res := shared.NewRpcErrorResponse(-1, shared.JsonRpcVersion, -32600, err)
|
||||
sendJSON(w, res)
|
||||
}
|
||||
|
||||
func sendJSON(w io.Writer, v interface{}) {
|
||||
if glog.V(logger.Detail) {
|
||||
if payload, err := json.MarshalIndent(v, "", "\t"); err == nil {
|
||||
glog.Infof("Sending payload: %s", payload)
|
||||
}
|
||||
}
|
||||
if err := json.NewEncoder(w).Encode(v); err != nil {
|
||||
glog.V(logger.Error).Infoln("Error sending JSON:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Stop closes all active HTTP connections and shuts down the server.
|
||||
func StopHttp() {
|
||||
httpServerMu.Lock()
|
||||
defer httpServerMu.Unlock()
|
||||
if httpServer != nil {
|
||||
httpServer.Close()
|
||||
httpServer = nil
|
||||
}
|
||||
}
|
||||
|
||||
func listenHTTP(addr string, h http.Handler) (*stopServer, error) {
|
||||
l, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := &stopServer{l: l, idle: make(map[net.Conn]struct{})}
|
||||
s.Server = &http.Server{
|
||||
Addr: addr,
|
||||
Handler: h,
|
||||
ReadTimeout: serverReadTimeout,
|
||||
WriteTimeout: serverWriteTimeout,
|
||||
ConnState: s.connState,
|
||||
}
|
||||
go s.Serve(l)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *stopServer) connState(c net.Conn, state http.ConnState) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
// Close c immediately if we're past shutdown.
|
||||
if s.shutdown {
|
||||
if state != http.StateClosed {
|
||||
c.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
if state == http.StateIdle {
|
||||
s.idle[c] = struct{}{}
|
||||
} else {
|
||||
delete(s.idle, c)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stopServer) Close() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
// Shut down the acceptor. No new connections can be created.
|
||||
s.l.Close()
|
||||
// Drop all idle connections. Non-idle connections will be
|
||||
// closed by connState as soon as they become idle.
|
||||
s.shutdown = true
|
||||
for c := range s.idle {
|
||||
glog.V(logger.Detail).Infof("closing idle connection %v", c.RemoteAddr())
|
||||
c.Close()
|
||||
delete(s.idle, c)
|
||||
}
|
||||
}
|
||||
|
||||
type httpClient struct {
|
||||
address string
|
||||
port uint
|
||||
codec codec.ApiCoder
|
||||
lastRes interface{}
|
||||
lastErr error
|
||||
}
|
||||
|
||||
// Create a new in process client
|
||||
func NewHttpClient(cfg HttpConfig, c codec.Codec) *httpClient {
|
||||
return &httpClient{
|
||||
address: cfg.ListenAddress,
|
||||
port: cfg.ListenPort,
|
||||
codec: c.New(nil),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *httpClient) Close() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
func (self *httpClient) Send(req interface{}) error {
|
||||
var body []byte
|
||||
var err error
|
||||
|
||||
self.lastRes = nil
|
||||
self.lastErr = nil
|
||||
|
||||
if body, err = self.codec.Encode(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
httpReq, err := http.NewRequest("POST", fmt.Sprintf("%s:%d", self.address, self.port), bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
httpReq.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client := http.Client{}
|
||||
resp, err := client.Do(httpReq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.Status == "200 OK" {
|
||||
reply, _ := ioutil.ReadAll(resp.Body)
|
||||
var rpcSuccessResponse shared.SuccessResponse
|
||||
if err = self.codec.Decode(reply, &rpcSuccessResponse); err == nil {
|
||||
self.lastRes = &rpcSuccessResponse
|
||||
self.lastErr = err
|
||||
return nil
|
||||
} else {
|
||||
var rpcErrorResponse shared.ErrorResponse
|
||||
if err = self.codec.Decode(reply, &rpcErrorResponse); err == nil {
|
||||
self.lastRes = &rpcErrorResponse
|
||||
self.lastErr = err
|
||||
return nil
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (self *httpClient) Recv() (interface{}, error) {
|
||||
return self.lastRes, self.lastErr
|
||||
}
|
||||
|
||||
func (self *httpClient) SupportedModules() (map[string]string, error) {
|
||||
var body []byte
|
||||
var err error
|
||||
|
||||
payload := shared.Request{
|
||||
Id: 1,
|
||||
Jsonrpc: "2.0",
|
||||
Method: "modules",
|
||||
}
|
||||
|
||||
if body, err = self.codec.Encode(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", fmt.Sprintf("%s:%d", self.address, self.port), bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client := http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.Status == "200 OK" {
|
||||
reply, _ := ioutil.ReadAll(resp.Body)
|
||||
var rpcRes shared.SuccessResponse
|
||||
if err = self.codec.Decode(reply, &rpcRes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make(map[string]string)
|
||||
if modules, ok := rpcRes.Result.(map[string]interface{}); ok {
|
||||
for a, v := range modules {
|
||||
result[a] = fmt.Sprintf("%s", v)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
err = fmt.Errorf("Unable to parse module response - %v", rpcRes.Result)
|
||||
} else {
|
||||
fmt.Printf("resp.Status = %s\n", resp.Status)
|
||||
fmt.Printf("err = %v\n", err)
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package comms
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
)
|
||||
|
||||
type InProcClient struct {
|
||||
api shared.EthereumApi
|
||||
codec codec.Codec
|
||||
lastId interface{}
|
||||
lastJsonrpc string
|
||||
lastErr error
|
||||
lastRes interface{}
|
||||
}
|
||||
|
||||
// Create a new in process client
|
||||
func NewInProcClient(codec codec.Codec) *InProcClient {
|
||||
return &InProcClient{
|
||||
codec: codec,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *InProcClient) Close() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
// Need to setup api support
|
||||
func (self *InProcClient) Initialize(offeredApi shared.EthereumApi) {
|
||||
self.api = offeredApi
|
||||
}
|
||||
|
||||
func (self *InProcClient) Send(req interface{}) error {
|
||||
if r, ok := req.(*shared.Request); ok {
|
||||
self.lastId = r.Id
|
||||
self.lastJsonrpc = r.Jsonrpc
|
||||
self.lastRes, self.lastErr = self.api.Execute(r)
|
||||
return self.lastErr
|
||||
}
|
||||
|
||||
return fmt.Errorf("Invalid request (%T)", req)
|
||||
}
|
||||
|
||||
func (self *InProcClient) Recv() (interface{}, error) {
|
||||
return *shared.NewRpcResponse(self.lastId, self.lastJsonrpc, self.lastRes, self.lastErr), nil
|
||||
}
|
||||
|
||||
func (self *InProcClient) SupportedModules() (map[string]string, error) {
|
||||
req := shared.Request{
|
||||
Id: 1,
|
||||
Jsonrpc: "2.0",
|
||||
Method: "modules",
|
||||
}
|
||||
|
||||
if res, err := self.api.Execute(&req); err == nil {
|
||||
if result, ok := res.(map[string]string); ok {
|
||||
return result, nil
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Invalid response")
|
||||
}
|
158
rpc/comms/ipc.go
158
rpc/comms/ipc.go
@ -1,158 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package comms
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
)
|
||||
|
||||
type Stopper interface {
|
||||
Stop()
|
||||
}
|
||||
|
||||
type InitFunc func(conn net.Conn) (Stopper, shared.EthereumApi, error)
|
||||
|
||||
type IpcConfig struct {
|
||||
Endpoint string
|
||||
}
|
||||
|
||||
type ipcClient struct {
|
||||
endpoint string
|
||||
c net.Conn
|
||||
codec codec.Codec
|
||||
coder codec.ApiCoder
|
||||
}
|
||||
|
||||
func (self *ipcClient) Close() {
|
||||
self.coder.Close()
|
||||
}
|
||||
|
||||
func (self *ipcClient) Send(msg interface{}) error {
|
||||
var err error
|
||||
if err = self.coder.WriteResponse(msg); err != nil {
|
||||
if err = self.reconnect(); err == nil {
|
||||
err = self.coder.WriteResponse(msg)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *ipcClient) Recv() (interface{}, error) {
|
||||
return self.coder.ReadResponse()
|
||||
}
|
||||
|
||||
func (self *ipcClient) SupportedModules() (map[string]string, error) {
|
||||
req := shared.Request{
|
||||
Id: 1,
|
||||
Jsonrpc: "2.0",
|
||||
Method: "rpc_modules",
|
||||
}
|
||||
|
||||
if err := self.coder.WriteResponse(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, _ := self.coder.ReadResponse()
|
||||
if sucRes, ok := res.(*shared.SuccessResponse); ok {
|
||||
data, _ := json.Marshal(sucRes.Result)
|
||||
modules := make(map[string]string)
|
||||
if err := json.Unmarshal(data, &modules); err == nil {
|
||||
return modules, nil
|
||||
}
|
||||
}
|
||||
|
||||
// old version uses modules instead of rpc_modules, this can be removed after full migration
|
||||
req.Method = "modules"
|
||||
if err := self.coder.WriteResponse(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := self.coder.ReadResponse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if sucRes, ok := res.(*shared.SuccessResponse); ok {
|
||||
data, _ := json.Marshal(sucRes.Result)
|
||||
modules := make(map[string]string)
|
||||
err = json.Unmarshal(data, &modules)
|
||||
if err == nil {
|
||||
return modules, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Invalid response")
|
||||
}
|
||||
|
||||
// Create a new IPC client, UNIX domain socket on posix, named pipe on Windows
|
||||
func NewIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) {
|
||||
return newIpcClient(cfg, codec)
|
||||
}
|
||||
|
||||
// Start IPC server
|
||||
func StartIpc(cfg IpcConfig, codec codec.Codec, initializer InitFunc) error {
|
||||
l, err := ipcListen(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go ipcLoop(cfg, codec, initializer, l)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateListener creates an listener, on Unix platforms this is a unix socket, on Windows this is a named pipe
|
||||
func CreateListener(cfg IpcConfig) (net.Listener, error) {
|
||||
return ipcListen(cfg)
|
||||
}
|
||||
|
||||
func ipcLoop(cfg IpcConfig, codec codec.Codec, initializer InitFunc, l net.Listener) {
|
||||
glog.V(logger.Info).Infof("IPC service started (%s)\n", cfg.Endpoint)
|
||||
defer os.Remove(cfg.Endpoint)
|
||||
defer l.Close()
|
||||
for {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
glog.V(logger.Debug).Infof("accept: %v", err)
|
||||
return
|
||||
}
|
||||
id := newIpcConnId()
|
||||
go func() {
|
||||
defer conn.Close()
|
||||
glog.V(logger.Debug).Infof("new connection with id %06d started", id)
|
||||
stopper, api, err := initializer(conn)
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Unable to initialize IPC connection: %v", err)
|
||||
return
|
||||
}
|
||||
defer stopper.Stop()
|
||||
handle(id, conn, api, codec)
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func newIpcConnId() int {
|
||||
return rand.Int() % 1000000
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
|
||||
|
||||
package comms
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
"github.com/ethereum/go-ethereum/rpc/useragent"
|
||||
)
|
||||
|
||||
func newIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) {
|
||||
c, err := net.DialUnix("unix", nil, &net.UnixAddr{cfg.Endpoint, "unix"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
coder := codec.New(c)
|
||||
msg := shared.Request{
|
||||
Id: 0,
|
||||
Method: useragent.EnableUserAgentMethod,
|
||||
Jsonrpc: shared.JsonRpcVersion,
|
||||
Params: []byte("[]"),
|
||||
}
|
||||
|
||||
coder.WriteResponse(msg)
|
||||
coder.Recv()
|
||||
|
||||
return &ipcClient{cfg.Endpoint, c, codec, coder}, nil
|
||||
}
|
||||
|
||||
func (self *ipcClient) reconnect() error {
|
||||
self.coder.Close()
|
||||
c, err := net.DialUnix("unix", nil, &net.UnixAddr{self.endpoint, "unix"})
|
||||
if err == nil {
|
||||
self.coder = self.codec.New(c)
|
||||
|
||||
msg := shared.Request{
|
||||
Id: 0,
|
||||
Method: useragent.EnableUserAgentMethod,
|
||||
Jsonrpc: shared.JsonRpcVersion,
|
||||
Params: []byte("[]"),
|
||||
}
|
||||
self.coder.WriteResponse(msg)
|
||||
self.coder.Recv()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func ipcListen(cfg IpcConfig) (net.Listener, error) {
|
||||
// Ensure the IPC path exists and remove any previous leftover
|
||||
if err := os.MkdirAll(filepath.Dir(cfg.Endpoint), 0751); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
os.Remove(cfg.Endpoint)
|
||||
l, err := net.Listen("unix", cfg.Endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
os.Chmod(cfg.Endpoint, 0600)
|
||||
return l, nil
|
||||
}
|
@ -99,4 +99,130 @@ Subscriptions are deleted when:
|
||||
- the user sends an unsubscribe request
|
||||
- the connection which was used to create the subscription is closed
|
||||
*/
|
||||
package v2
|
||||
package rpc
|
||||
|
||||
var (
|
||||
// Mapping between the different methods each api supports
|
||||
AutoCompletion = map[string][]string{
|
||||
"admin": []string{
|
||||
"addPeer",
|
||||
"datadir",
|
||||
"enableUserAgent",
|
||||
"exportChain",
|
||||
"getContractInfo",
|
||||
"httpGet",
|
||||
"importChain",
|
||||
"nodeInfo",
|
||||
"peers",
|
||||
"register",
|
||||
"registerUrl",
|
||||
"saveInfo",
|
||||
"setGlobalRegistrar",
|
||||
"setHashReg",
|
||||
"setUrlHint",
|
||||
"setSolc",
|
||||
"sleep",
|
||||
"sleepBlocks",
|
||||
"startNatSpec",
|
||||
"startRPC",
|
||||
"stopNatSpec",
|
||||
"stopRPC",
|
||||
"verbosity",
|
||||
},
|
||||
"db": []string{
|
||||
"getString",
|
||||
"putString",
|
||||
"getHex",
|
||||
"putHex",
|
||||
},
|
||||
"debug": []string{
|
||||
"dumpBlock",
|
||||
"getBlockRlp",
|
||||
"metrics",
|
||||
"printBlock",
|
||||
"processBlock",
|
||||
"seedHash",
|
||||
"setHead",
|
||||
},
|
||||
"eth": []string{
|
||||
"accounts",
|
||||
"blockNumber",
|
||||
"call",
|
||||
"contract",
|
||||
"coinbase",
|
||||
"compile.lll",
|
||||
"compile.serpent",
|
||||
"compile.solidity",
|
||||
"contract",
|
||||
"defaultAccount",
|
||||
"defaultBlock",
|
||||
"estimateGas",
|
||||
"filter",
|
||||
"getBalance",
|
||||
"getBlock",
|
||||
"getBlockTransactionCount",
|
||||
"getBlockUncleCount",
|
||||
"getCode",
|
||||
"getNatSpec",
|
||||
"getCompilers",
|
||||
"gasPrice",
|
||||
"getStorageAt",
|
||||
"getTransaction",
|
||||
"getTransactionCount",
|
||||
"getTransactionFromBlock",
|
||||
"getTransactionReceipt",
|
||||
"getUncle",
|
||||
"hashrate",
|
||||
"mining",
|
||||
"namereg",
|
||||
"pendingTransactions",
|
||||
"resend",
|
||||
"sendRawTransaction",
|
||||
"sendTransaction",
|
||||
"sign",
|
||||
"syncing",
|
||||
},
|
||||
"miner": []string{
|
||||
"hashrate",
|
||||
"makeDAG",
|
||||
"setEtherbase",
|
||||
"setExtra",
|
||||
"setGasPrice",
|
||||
"startAutoDAG",
|
||||
"start",
|
||||
"stopAutoDAG",
|
||||
"stop",
|
||||
},
|
||||
"net": []string{
|
||||
"peerCount",
|
||||
"listening",
|
||||
},
|
||||
"personal": []string{
|
||||
"listAccounts",
|
||||
"newAccount",
|
||||
"unlockAccount",
|
||||
},
|
||||
"shh": []string{
|
||||
"post",
|
||||
"newIdentity",
|
||||
"hasIdentity",
|
||||
"newGroup",
|
||||
"addToGroup",
|
||||
"filter",
|
||||
},
|
||||
"txpool": []string{
|
||||
"status",
|
||||
},
|
||||
"web3": []string{
|
||||
"sha3",
|
||||
"version",
|
||||
"fromWei",
|
||||
"toWei",
|
||||
"toHex",
|
||||
"toAscii",
|
||||
"fromAscii",
|
||||
"toBigNumber",
|
||||
"isAddress",
|
||||
},
|
||||
}
|
||||
)
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package v2
|
||||
package rpc
|
||||
|
||||
import "fmt"
|
||||
|
||||
@ -83,3 +83,15 @@ func (e *callbackError) Code() int {
|
||||
func (e *callbackError) Error() string {
|
||||
return e.message
|
||||
}
|
||||
|
||||
// issued when a request is received after the server is issued to stop.
|
||||
type shutdownError struct {
|
||||
}
|
||||
|
||||
func (e *shutdownError) Code() int {
|
||||
return -32000
|
||||
}
|
||||
|
||||
func (e *shutdownError) Error() string {
|
||||
return "server is shutting down"
|
||||
}
|
368
rpc/http.go
Normal file
368
rpc/http.go
Normal file
@ -0,0 +1,368 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
const (
|
||||
httpReadDeadLine = 60 * time.Second // wait max httpReadDeadeline for next request
|
||||
)
|
||||
|
||||
var (
|
||||
httpServerMu sync.Mutex // prevent concurrent access to the httpListener and httpServer
|
||||
httpListener net.Listener // listener for the http server
|
||||
httpRPCServer *Server // the node can only start 1 HTTP RPC server instance
|
||||
)
|
||||
|
||||
// httpMessageStream is the glue between a HTTP connection which is message based
|
||||
// and the RPC codecs that expect json requests to be read from a stream. It will
|
||||
// parse HTTP messages and offer the bodies of these requests as a stream through
|
||||
// the Read method. This will require full control of the connection and thus need
|
||||
// a "hijacked" HTTP connection.
|
||||
type httpMessageStream struct {
|
||||
conn net.Conn // TCP connection
|
||||
rw *bufio.ReadWriter // buffered where HTTP requests/responses are read/written from/to
|
||||
currentReq *http.Request // pending request, codec can pass in a too small buffer for a single read
|
||||
// we need to keep track of the current requests if it was not read at once
|
||||
payloadBytesRead int64 // number of bytes which are read from the current request
|
||||
allowedOrigins *set.Set // allowed CORS domains
|
||||
origin string // origin of this connection/request
|
||||
}
|
||||
|
||||
// NewHttpMessageStream will create a new http message stream parser that can be
|
||||
// used by the codes in the RPC package. It will take full control of the given
|
||||
// connection and thus needs to be hijacked. It will read and write HTTP messages
|
||||
// from the passed rwbuf. The allowed origins are the RPC CORS domains the user has supplied.
|
||||
func NewHTTPMessageStream(c net.Conn, rwbuf *bufio.ReadWriter, initialReq *http.Request, allowdOrigins []string) *httpMessageStream {
|
||||
r := &httpMessageStream{conn: c, rw: rwbuf, currentReq: initialReq, allowedOrigins: set.New()}
|
||||
for _, origin := range allowdOrigins {
|
||||
r.allowedOrigins.Add(origin)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// handleOptionsRequest handles the HTTP preflight requests (OPTIONS) that browsers
|
||||
// make to enforce CORS rules. Only the POST method is allowed and the origin must
|
||||
// be on the rpccorsdomain list the user has specified.
|
||||
func (h *httpMessageStream) handleOptionsRequest(req *http.Request) error {
|
||||
headers := req.Header
|
||||
|
||||
if !strings.EqualFold(req.Method, "OPTIONS") {
|
||||
return fmt.Errorf("preflight aborted: %s!=OPTIONS", req.Method)
|
||||
}
|
||||
|
||||
origin := headers.Get("Origin")
|
||||
if origin == "" {
|
||||
return fmt.Errorf("preflight aborted: empty origin")
|
||||
}
|
||||
|
||||
responseHeaders := make(http.Header)
|
||||
responseHeaders.Set("Access-Control-Allow-Methods", "POST")
|
||||
if h.allowedOrigins.Has(origin) || h.allowedOrigins.Has("*") {
|
||||
responseHeaders.Set("Access-Control-Allow-Origin", origin)
|
||||
} else {
|
||||
glog.V(logger.Info).Infof("origin '%s' not allowed", origin)
|
||||
}
|
||||
responseHeaders.Set("Access-Control-Allow-Headers", "Content-Type")
|
||||
responseHeaders.Set("Date", string(httpTimestamp(time.Now())))
|
||||
responseHeaders.Set("Content-Type", "text/plain; charset=utf-8")
|
||||
responseHeaders.Set("Content-Length", "0")
|
||||
responseHeaders.Set("Vary", "Origin")
|
||||
|
||||
defer h.rw.Flush()
|
||||
|
||||
if _, err := h.rw.WriteString("HTTP/1.1 200 OK\r\n"); err != nil {
|
||||
glog.V(logger.Error).Infof("unable to write OPTIONS response: %v\n", err)
|
||||
return err
|
||||
}
|
||||
if err := responseHeaders.Write(h.rw); err != nil {
|
||||
glog.V(logger.Error).Infof("unable to write OPTIONS headers: %v\n", err)
|
||||
}
|
||||
if _, err := h.rw.WriteString("\r\n"); err != nil {
|
||||
glog.V(logger.Error).Infof("unable to write OPTIONS response: %v\n", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read will read incoming HTTP requests and reads the body data from these requests
|
||||
// as an endless stream of data.
|
||||
func (h *httpMessageStream) Read(buf []byte) (n int, err error) {
|
||||
h.conn.SetReadDeadline(time.Now().Add(httpReadDeadLine))
|
||||
for {
|
||||
// if the last request was read completely try to read the next request
|
||||
if h.currentReq == nil {
|
||||
if h.currentReq, err = http.ReadRequest(bufio.NewReader(h.rw)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
// The "options" method is http specific and not interested for the RPC server.
|
||||
// Handle it internally and wait for the next request.
|
||||
if strings.EqualFold(h.currentReq.Method, "OPTIONS") {
|
||||
if err = h.handleOptionsRequest(h.currentReq); err != nil {
|
||||
glog.V(logger.Info).Infof("RPC/HTTP OPTIONS error: %v\n", err)
|
||||
h.currentReq = nil
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// processed valid request -> reset deadline
|
||||
h.conn.SetReadDeadline(time.Now().Add(httpReadDeadLine))
|
||||
h.currentReq = nil
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.EqualFold(h.currentReq.Method, "POST") {
|
||||
n, err := h.currentReq.Body.Read(buf)
|
||||
h.payloadBytesRead += int64(n)
|
||||
|
||||
// entire payload read, read new request next time
|
||||
if err == io.EOF || h.payloadBytesRead >= h.currentReq.ContentLength {
|
||||
h.origin = h.currentReq.Header.Get("origin")
|
||||
h.payloadBytesRead = 0
|
||||
h.currentReq.Body.Close()
|
||||
h.currentReq = nil
|
||||
err = nil // io.EOF is not an error
|
||||
} else if err != nil {
|
||||
// unable to read body
|
||||
h.currentReq.Body.Close()
|
||||
h.currentReq = nil
|
||||
h.payloadBytesRead = 0
|
||||
}
|
||||
|
||||
// partial read of body
|
||||
return n, err
|
||||
}
|
||||
|
||||
h.currentReq = nil
|
||||
return 0, fmt.Errorf("unsupported HTTP method '%s'", h.currentReq.Method)
|
||||
}
|
||||
}
|
||||
|
||||
// Write will create a HTTP response with the given payload and send it to the peer.
|
||||
func (h *httpMessageStream) Write(payload []byte) (int, error) {
|
||||
defer h.rw.Flush()
|
||||
|
||||
responseHeaders := make(http.Header)
|
||||
responseHeaders.Set("Content-Type", "application/json")
|
||||
responseHeaders.Set("Content-Length", strconv.Itoa(len(payload)))
|
||||
if h.origin != "" {
|
||||
responseHeaders.Set("Access-Control-Allow-Origin", h.origin)
|
||||
}
|
||||
|
||||
h.rw.WriteString("HTTP/1.1 200 OK\r\n")
|
||||
responseHeaders.Write(h.rw)
|
||||
h.rw.WriteString("\r\n")
|
||||
|
||||
return h.rw.Write(payload)
|
||||
}
|
||||
|
||||
// Close will close the underlying TCP connection this instance has taken ownership over.
|
||||
func (h *httpMessageStream) Close() error {
|
||||
h.rw.Flush()
|
||||
return h.conn.Close()
|
||||
}
|
||||
|
||||
// TimeFormat is the time format to use with time.Parse and time.Time.Format when
|
||||
// parsing or generating times in HTTP headers. It is like time.RFC1123 but hard
|
||||
// codes GMT as the time zone.
|
||||
const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT"
|
||||
|
||||
// httpTimestamp formats the given t as specified in RFC1123.
|
||||
func httpTimestamp(t time.Time) []byte {
|
||||
const days = "SunMonTueWedThuFriSat"
|
||||
const months = "JanFebMarAprMayJunJulAugSepOctNovDec"
|
||||
|
||||
b := make([]byte, 0)
|
||||
t = t.UTC()
|
||||
yy, mm, dd := t.Date()
|
||||
hh, mn, ss := t.Clock()
|
||||
day := days[3 * t.Weekday():]
|
||||
mon := months[3 * (mm - 1):]
|
||||
|
||||
return append(b,
|
||||
day[0], day[1], day[2], ',', ' ',
|
||||
byte('0' + dd / 10), byte('0' + dd % 10), ' ',
|
||||
mon[0], mon[1], mon[2], ' ',
|
||||
byte('0' + yy / 1000), byte('0' + (yy / 100) % 10), byte('0' + (yy / 10) % 10), byte('0' + yy % 10), ' ',
|
||||
byte('0' + hh / 10), byte('0' + hh % 10), ':',
|
||||
byte('0' + mn / 10), byte('0' + mn % 10), ':',
|
||||
byte('0' + ss / 10), byte('0' + ss % 10), ' ',
|
||||
'G', 'M', 'T')
|
||||
}
|
||||
|
||||
// httpConnHijacker is a http.Handler implementation that will hijack the HTTP
|
||||
// connection, wraps it in a HttpMessageStream that is then wrapped in a JSON
|
||||
// codec which will be served on the rpcServer.
|
||||
type httpConnHijacker struct {
|
||||
corsdomains []string
|
||||
rpcServer *Server
|
||||
}
|
||||
|
||||
// ServeHTTP will hijack the connection, wraps the captured connection in a
|
||||
// HttpMessageStream which is then used as codec.
|
||||
func (h *httpConnHijacker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
hj, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
http.Error(w, "webserver doesn't support hijacking", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
conn, rwbuf, err := hj.Hijack()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
httpRequestStream := NewHTTPMessageStream(conn, rwbuf, req, h.corsdomains)
|
||||
|
||||
codec := NewJSONCodec(httpRequestStream)
|
||||
go h.rpcServer.ServeCodec(codec)
|
||||
}
|
||||
|
||||
// StartHTTP will start the JSONRPC HTTP RPC interface when its not yet running.
|
||||
func StartHTTP(address string, port int, corsdomains []string, apis []API) error {
|
||||
httpServerMu.Lock()
|
||||
defer httpServerMu.Unlock()
|
||||
|
||||
if httpRPCServer != nil {
|
||||
return fmt.Errorf("HTTP RPC interface already started on %s", httpListener.Addr())
|
||||
}
|
||||
|
||||
rpcServer := NewServer()
|
||||
|
||||
for _, api := range apis {
|
||||
if err := rpcServer.RegisterName(api.Namespace, api.Service); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", address, port))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
httpServer := http.Server{Handler: &httpConnHijacker{corsdomains, rpcServer}}
|
||||
go httpServer.Serve(listener)
|
||||
|
||||
httpListener = listener
|
||||
httpRPCServer = rpcServer
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// StopHTTP will stop the running HTTP interface. If it is not running an error will be returned.
|
||||
func StopHTTP() error {
|
||||
httpServerMu.Lock()
|
||||
defer httpServerMu.Unlock()
|
||||
|
||||
if httpRPCServer == nil {
|
||||
return errors.New("HTTP RPC interface not started")
|
||||
}
|
||||
|
||||
httpListener.Close()
|
||||
httpRPCServer.Stop()
|
||||
|
||||
httpRPCServer = nil
|
||||
httpListener = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// httpClient connects to a geth RPC server over HTTP.
|
||||
type httpClient struct {
|
||||
endpoint *url.URL // HTTP-RPC server endpoint
|
||||
lastRes []byte // HTTP requests are synchronous, store last response
|
||||
}
|
||||
|
||||
// NewHTTPClient create a new RPC clients that connection to a geth RPC server
|
||||
// over HTTP.
|
||||
func NewHTTPClient(endpoint string) (*httpClient, error) {
|
||||
url, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &httpClient{endpoint: url}, nil
|
||||
}
|
||||
|
||||
// Send will serialize the given msg to JSON and sends it to the RPC server.
|
||||
// Since HTTP is synchronous the response is stored until Recv is called.
|
||||
func (client *httpClient) Send(msg interface{}) error {
|
||||
var body []byte
|
||||
var err error
|
||||
|
||||
client.lastRes = nil
|
||||
|
||||
if body, err = json.Marshal(msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
httpReq, err := http.NewRequest("POST", client.endpoint.String(), bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
httpReq.Header.Set("Content-Type", "application/json")
|
||||
|
||||
httpClient := http.Client{}
|
||||
resp, err := httpClient.Do(httpReq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
client.lastRes, err = ioutil.ReadAll(resp.Body)
|
||||
return err
|
||||
}
|
||||
|
||||
return fmt.Errorf("unable to handle request")
|
||||
}
|
||||
|
||||
// Recv will try to deserialize the last received response into the given msg.
|
||||
func (client *httpClient) Recv(msg interface{}) error {
|
||||
return json.Unmarshal(client.lastRes, &msg)
|
||||
}
|
||||
|
||||
// Close is not necessary for httpClient
|
||||
func (client *httpClient) Close() {
|
||||
}
|
||||
|
||||
// SupportedModules will return the collection of offered RPC modules.
|
||||
func (client *httpClient) SupportedModules() (map[string]string, error) {
|
||||
return SupportedModules(client)
|
||||
}
|
84
rpc/ipc.go
Normal file
84
rpc/ipc.go
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
)
|
||||
|
||||
// CreateIPCListener creates an listener, on Unix platforms this is a unix socket, on Windows this is a named pipe
|
||||
func CreateIPCListener(endpoint string) (net.Listener, error) {
|
||||
return ipcListen(endpoint)
|
||||
}
|
||||
|
||||
// ipcClient represent an IPC RPC client. It will connect to a given endpoint and tries to communicate with a node using
|
||||
// JSON serialization.
|
||||
type ipcClient struct {
|
||||
endpoint string
|
||||
conn net.Conn
|
||||
out *json.Encoder
|
||||
in *json.Decoder
|
||||
}
|
||||
|
||||
// NewIPCClient create a new IPC client that will connect on the given endpoint. Messages are JSON encoded and encoded.
|
||||
// On Unix it assumes the endpoint is the full path to a unix socket, and Windows the endpoint is an identifier for a
|
||||
// named pipe.
|
||||
func NewIPCClient(endpoint string) (*ipcClient, error) {
|
||||
conn, err := newIPCConnection(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ipcClient{endpoint: endpoint, conn: conn, in: json.NewDecoder(conn), out: json.NewEncoder(conn)}, nil
|
||||
}
|
||||
|
||||
// Send will serialize the given message and send it to the server.
|
||||
// When sending the message fails it will try to reconnect once and send the message again.
|
||||
func (client *ipcClient) Send(msg interface{}) error {
|
||||
if err := client.out.Encode(msg); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// retry once
|
||||
client.conn.Close()
|
||||
|
||||
conn, err := newIPCConnection(client.endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client.conn = conn
|
||||
client.in = json.NewDecoder(conn)
|
||||
client.out = json.NewEncoder(conn)
|
||||
|
||||
return client.out.Encode(msg)
|
||||
}
|
||||
|
||||
// Recv will read a message from the connection and tries to parse it. It assumes the received message is JSON encoded.
|
||||
func (client *ipcClient) Recv(msg interface{}) error {
|
||||
return client.in.Decode(&msg)
|
||||
}
|
||||
|
||||
// Close will close the underlying IPC connection
|
||||
func (client *ipcClient) Close() {
|
||||
client.conn.Close()
|
||||
}
|
||||
|
||||
// SupportedModules will return the collection of offered RPC modules.
|
||||
func (client *ipcClient) SupportedModules() (map[string]string, error) {
|
||||
return SupportedModules(client)
|
||||
}
|
@ -14,32 +14,32 @@
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
|
||||
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type Sha3Args struct {
|
||||
Data string
|
||||
// ipcListen will create a Unix socket on the given endpoint.
|
||||
func ipcListen(endpoint string) (net.Listener, error) {
|
||||
// Ensure the IPC path exists and remove any previous leftover
|
||||
if err := os.MkdirAll(filepath.Dir(endpoint), 0751); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
os.Remove(endpoint)
|
||||
l, err := net.Listen("unix", endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
os.Chmod(endpoint, 0600)
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (args *Sha3Args) UnmarshalJSON(b []byte) (err error) {
|
||||
var obj []interface{}
|
||||
if err := json.Unmarshal(b, &obj); err != nil {
|
||||
return shared.NewDecodeParamError(err.Error())
|
||||
}
|
||||
|
||||
if len(obj) < 1 {
|
||||
return shared.NewInsufficientParamsError(len(obj), 1)
|
||||
}
|
||||
|
||||
argstr, ok := obj[0].(string)
|
||||
if !ok {
|
||||
return shared.NewInvalidTypeError("data", "is not a string")
|
||||
}
|
||||
args.Data = argstr
|
||||
return nil
|
||||
// newIPCConnection will connect to a Unix socket on the given endpoint.
|
||||
func newIPCConnection(endpoint string) (net.Conn, error) {
|
||||
return net.DialUnix("unix", nil, &net.UnixAddr{endpoint, "unix"})
|
||||
}
|
@ -16,21 +16,16 @@
|
||||
|
||||
// +build windows
|
||||
|
||||
package comms
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
"github.com/ethereum/go-ethereum/rpc/useragent"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -649,49 +644,12 @@ func timeout(addr string) PipeError {
|
||||
return PipeError{fmt.Sprintf("Pipe IO timed out waiting for '%s'", addr), true}
|
||||
}
|
||||
|
||||
func newIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) {
|
||||
c, err := Dial(cfg.Endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
coder := codec.New(c)
|
||||
msg := shared.Request{
|
||||
Id: 0,
|
||||
Method: useragent.EnableUserAgentMethod,
|
||||
Jsonrpc: shared.JsonRpcVersion,
|
||||
Params: []byte("[]"),
|
||||
}
|
||||
|
||||
coder.WriteResponse(msg)
|
||||
coder.Recv()
|
||||
|
||||
return &ipcClient{cfg.Endpoint, c, codec, coder}, nil
|
||||
// ipcListen will create a named pipe on the given endpoint.
|
||||
func ipcListen(endpoint string) (net.Listener, error) {
|
||||
return Listen(endpoint)
|
||||
}
|
||||
|
||||
func (self *ipcClient) reconnect() error {
|
||||
c, err := Dial(self.endpoint)
|
||||
if err == nil {
|
||||
self.coder = self.codec.New(c)
|
||||
|
||||
req := shared.Request{
|
||||
Id: 0,
|
||||
Method: useragent.EnableUserAgentMethod,
|
||||
Jsonrpc: shared.JsonRpcVersion,
|
||||
Params: []byte("[]"),
|
||||
}
|
||||
self.coder.WriteResponse(req)
|
||||
self.coder.Recv()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func ipcListen(cfg IpcConfig) (net.Listener, error) {
|
||||
os.Remove(cfg.Endpoint) // in case it still exists from a previous run
|
||||
l, err := Listen(cfg.Endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
os.Chmod(cfg.Endpoint, 0600)
|
||||
return l, nil
|
||||
// newIPCConnection will connect to a named pipe with the given endpoint as name.
|
||||
func newIPCConnection(endpoint string) (net.Conn, error) {
|
||||
return Dial(endpoint)
|
||||
}
|
414
rpc/javascript.go
Normal file
414
rpc/javascript.go
Normal file
@ -0,0 +1,414 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package rpc
|
||||
|
||||
var (
|
||||
// Holds geth specific RPC extends which can be used to extend web3
|
||||
WEB3Extensions = map[string]string{
|
||||
"personal": Personal_JS,
|
||||
"txpool": TxPool_JS,
|
||||
"admin": Admin_JS,
|
||||
"db": Db_JS,
|
||||
"eth": Eth_JS,
|
||||
"miner": Miner_JS,
|
||||
"debug": Debug_JS,
|
||||
"net": Net_JS,
|
||||
}
|
||||
)
|
||||
|
||||
const Personal_JS = `
|
||||
web3._extend({
|
||||
property: 'personal',
|
||||
methods:
|
||||
[
|
||||
new web3._extend.Method({
|
||||
name: 'newAccount',
|
||||
call: 'personal_newAccount',
|
||||
params: 1,
|
||||
outputFormatter: web3._extend.utils.toAddress
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'unlockAccount',
|
||||
call: 'personal_unlockAccount',
|
||||
params: 3,
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'lockAccount',
|
||||
call: 'personal_lockAccount',
|
||||
params: 1
|
||||
})
|
||||
],
|
||||
properties:
|
||||
[
|
||||
new web3._extend.Property({
|
||||
name: 'listAccounts',
|
||||
getter: 'personal_listAccounts'
|
||||
})
|
||||
]
|
||||
});
|
||||
`
|
||||
|
||||
const TxPool_JS = `
|
||||
web3._extend({
|
||||
property: 'txpool',
|
||||
methods:
|
||||
[
|
||||
],
|
||||
properties:
|
||||
[
|
||||
new web3._extend.Property({
|
||||
name: 'status',
|
||||
getter: 'txpool_status'
|
||||
outputFormatter: function(status) {
|
||||
status.pending = web3._extend.utils.toDecimal(status.pending);
|
||||
status.queued = web3._extend.utils.toDecimal(status.queued);
|
||||
return status;
|
||||
}
|
||||
})
|
||||
]
|
||||
});
|
||||
`
|
||||
|
||||
const Admin_JS = `
|
||||
web3._extend({
|
||||
property: 'admin',
|
||||
methods:
|
||||
[
|
||||
new web3._extend.Method({
|
||||
name: 'addPeer',
|
||||
call: 'admin_addPeer',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'exportChain',
|
||||
call: 'admin_exportChain',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'importChain',
|
||||
call: 'admin_importChain',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'sleepBlocks',
|
||||
call: 'admin_sleepBlocks',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'verbosity',
|
||||
call: 'admin_verbosity',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.utils.fromDecimal]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setSolc',
|
||||
call: 'admin_setSolc',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'startRPC',
|
||||
call: 'admin_startRPC',
|
||||
params: 4
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'stopRPC',
|
||||
call: 'admin_stopRPC',
|
||||
params: 0
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'startWS',
|
||||
call: 'admin_startWS',
|
||||
params: 4
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'stopWS',
|
||||
call: 'admin_stopWS',
|
||||
params: 0
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setGlobalRegistrar',
|
||||
call: 'admin_setGlobalRegistrar',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setHashReg',
|
||||
call: 'admin_setHashReg',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setUrlHint',
|
||||
call: 'admin_setUrlHint',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'saveInfo',
|
||||
call: 'admin_saveInfo',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'register',
|
||||
call: 'admin_register',
|
||||
params: 3
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'registerUrl',
|
||||
call: 'admin_registerUrl',
|
||||
params: 3
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'startNatSpec',
|
||||
call: 'admin_startNatSpec',
|
||||
params: 0
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'stopNatSpec',
|
||||
call: 'admin_stopNatSpec',
|
||||
params: 0
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getContractInfo',
|
||||
call: 'admin_getContractInfo',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'httpGet',
|
||||
call: 'admin_httpGet',
|
||||
params: 2
|
||||
})
|
||||
],
|
||||
properties:
|
||||
[
|
||||
new web3._extend.Property({
|
||||
name: 'nodeInfo',
|
||||
getter: 'admin_nodeInfo'
|
||||
}),
|
||||
new web3._extend.Property({
|
||||
name: 'peers',
|
||||
getter: 'admin_peers'
|
||||
}),
|
||||
new web3._extend.Property({
|
||||
name: 'datadir',
|
||||
getter: 'admin_datadir'
|
||||
})
|
||||
]
|
||||
});
|
||||
`
|
||||
|
||||
const Db_JS = `
|
||||
web3._extend({
|
||||
property: 'db',
|
||||
methods:
|
||||
[
|
||||
],
|
||||
properties:
|
||||
[
|
||||
]
|
||||
});
|
||||
`
|
||||
|
||||
const Eth_JS = `
|
||||
web3._extend({
|
||||
property: 'eth',
|
||||
methods:
|
||||
[
|
||||
new web3._extend.Method({
|
||||
name: 'sign',
|
||||
call: 'eth_sign',
|
||||
params: 2,
|
||||
inputFormatter: [web3._extend.utils.toAddress, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'resend',
|
||||
call: 'eth_resend',
|
||||
params: 3,
|
||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter, web3._extend.utils.fromDecimal, web3._extend.utils.fromDecimal]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getNatSpec',
|
||||
call: 'eth_getNatSpec',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'signTransaction',
|
||||
call: 'eth_signTransaction',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'submitTransaction',
|
||||
call: 'eth_submitTransaction',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter]
|
||||
})
|
||||
],
|
||||
properties:
|
||||
[
|
||||
new web3._extend.Property({
|
||||
name: 'pendingTransactions',
|
||||
getter: 'eth_pendingTransactions'
|
||||
})
|
||||
]
|
||||
});
|
||||
`
|
||||
|
||||
const Net_JS = `
|
||||
web3._extend({
|
||||
property: 'net',
|
||||
methods:
|
||||
[
|
||||
new web3._extend.Method({
|
||||
name: 'addPeer',
|
||||
call: 'net_addPeer',
|
||||
params: 1
|
||||
})
|
||||
],
|
||||
properties:
|
||||
[
|
||||
new web3._extend.Property({
|
||||
name: 'version',
|
||||
getter: 'net_version'
|
||||
})
|
||||
]
|
||||
});
|
||||
`
|
||||
|
||||
const Debug_JS = `
|
||||
web3._extend({
|
||||
property: 'debug',
|
||||
methods:
|
||||
[
|
||||
new web3._extend.Method({
|
||||
name: 'printBlock',
|
||||
call: 'debug_printBlock',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getBlockRlp',
|
||||
call: 'debug_getBlockRlp',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setHead',
|
||||
call: 'debug_setHead',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'processBlock',
|
||||
call: 'debug_processBlock',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'seedHash',
|
||||
call: 'debug_seedHash',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'dumpBlock',
|
||||
call: 'debug_dumpBlock',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'metrics',
|
||||
call: 'debug_metrics',
|
||||
params: 1
|
||||
})
|
||||
],
|
||||
properties:
|
||||
[
|
||||
]
|
||||
});
|
||||
`
|
||||
|
||||
const Miner_JS = `
|
||||
web3._extend({
|
||||
property: 'miner',
|
||||
methods:
|
||||
[
|
||||
new web3._extend.Method({
|
||||
name: 'start',
|
||||
call: 'miner_start',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'stop',
|
||||
call: 'miner_stop',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setEtherbase',
|
||||
call: 'miner_setEtherbase',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.formatInputInt],
|
||||
outputFormatter: web3._extend.formatters.formatOutputBool
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setExtra',
|
||||
call: 'miner_setExtra',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setGasPrice',
|
||||
call: 'miner_setGasPrice',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.utils.fromDecial]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'startAutoDAG',
|
||||
call: 'miner_startAutoDAG',
|
||||
params: 0
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'stopAutoDAG',
|
||||
call: 'miner_stopAutoDAG',
|
||||
params: 0
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'makeDAG',
|
||||
call: 'miner_makeDAG',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.inputDefaultBlockNumberFormatter]
|
||||
})
|
||||
],
|
||||
properties:
|
||||
[
|
||||
new web3._extend.Property({
|
||||
name: 'hashrate',
|
||||
getter: 'miner_hashrate',
|
||||
outputFormatter: web3._extend.utils.toDecimal
|
||||
})
|
||||
]
|
||||
});
|
||||
`
|
||||
|
||||
const Shh_JS = `
|
||||
web3._extend({
|
||||
property: 'shh',
|
||||
methods:
|
||||
[
|
||||
|
||||
],
|
||||
properties:
|
||||
[
|
||||
new web3._extend.Property({
|
||||
name: 'version',
|
||||
getter: 'shh_version'
|
||||
})
|
||||
]
|
||||
});
|
||||
`
|
324
rpc/jeth.go
324
rpc/jeth.go
@ -1,324 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/jsre"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/rpc/comms"
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
"github.com/ethereum/go-ethereum/rpc/useragent"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
|
||||
"github.com/robertkrimen/otto"
|
||||
)
|
||||
|
||||
type Jeth struct {
|
||||
ethApi shared.EthereumApi
|
||||
re *jsre.JSRE
|
||||
client comms.EthereumClient
|
||||
fe xeth.Frontend
|
||||
}
|
||||
|
||||
func NewJeth(ethApi shared.EthereumApi, re *jsre.JSRE, client comms.EthereumClient, fe xeth.Frontend) *Jeth {
|
||||
return &Jeth{ethApi, re, client, fe}
|
||||
}
|
||||
|
||||
func (self *Jeth) err(call otto.FunctionCall, code int, msg string, id interface{}) (response otto.Value) {
|
||||
m := shared.NewRpcErrorResponse(id, shared.JsonRpcVersion, code, fmt.Errorf(msg))
|
||||
errObj, _ := json.Marshal(m.Error)
|
||||
errRes, _ := json.Marshal(m)
|
||||
|
||||
call.Otto.Run("ret_error = " + string(errObj))
|
||||
res, _ := call.Otto.Run("ret_response = " + string(errRes))
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// UnlockAccount asks the user for the password and than executes the jeth.UnlockAccount callback in the jsre
|
||||
func (self *Jeth) UnlockAccount(call otto.FunctionCall) (response otto.Value) {
|
||||
var cmd, account, passwd string
|
||||
timeout := int64(300)
|
||||
var ok bool
|
||||
|
||||
if len(call.ArgumentList) == 0 {
|
||||
fmt.Println("expected address of account to unlock")
|
||||
return otto.FalseValue()
|
||||
}
|
||||
|
||||
if len(call.ArgumentList) >= 1 {
|
||||
if accountExport, err := call.Argument(0).Export(); err == nil {
|
||||
if account, ok = accountExport.(string); ok {
|
||||
if len(call.ArgumentList) == 1 {
|
||||
fmt.Printf("Unlock account %s\n", account)
|
||||
passwd, err = utils.PromptPassword("Passphrase: ", true)
|
||||
if err != nil {
|
||||
return otto.FalseValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(call.ArgumentList) >= 2 {
|
||||
if passwdExport, err := call.Argument(1).Export(); err == nil {
|
||||
passwd, _ = passwdExport.(string)
|
||||
}
|
||||
}
|
||||
|
||||
if len(call.ArgumentList) >= 3 {
|
||||
if timeoutExport, err := call.Argument(2).Export(); err == nil {
|
||||
timeout, _ = timeoutExport.(int64)
|
||||
}
|
||||
}
|
||||
|
||||
cmd = fmt.Sprintf("jeth.unlockAccount('%s', '%s', %d)", account, passwd, timeout)
|
||||
if val, err := call.Otto.Run(cmd); err == nil {
|
||||
return val
|
||||
}
|
||||
|
||||
return otto.FalseValue()
|
||||
}
|
||||
|
||||
// NewAccount asks the user for the password and than executes the jeth.newAccount callback in the jsre
|
||||
func (self *Jeth) NewAccount(call otto.FunctionCall) (response otto.Value) {
|
||||
if len(call.ArgumentList) == 0 {
|
||||
passwd, err := utils.PromptPassword("Passphrase: ", true)
|
||||
if err != nil {
|
||||
return otto.FalseValue()
|
||||
}
|
||||
passwd2, err := utils.PromptPassword("Repeat passphrase: ", true)
|
||||
if err != nil {
|
||||
return otto.FalseValue()
|
||||
}
|
||||
|
||||
if passwd != passwd2 {
|
||||
fmt.Println("Passphrases don't match")
|
||||
return otto.FalseValue()
|
||||
}
|
||||
|
||||
cmd := fmt.Sprintf("jeth.newAccount('%s')", passwd)
|
||||
if val, err := call.Otto.Run(cmd); err == nil {
|
||||
return val
|
||||
}
|
||||
} else {
|
||||
fmt.Println("New account doesn't expect argument(s), you will be prompted for a password")
|
||||
}
|
||||
|
||||
return otto.FalseValue()
|
||||
}
|
||||
|
||||
func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) {
|
||||
reqif, err := call.Argument(0).Export()
|
||||
if err != nil {
|
||||
return self.err(call, -32700, err.Error(), nil)
|
||||
}
|
||||
|
||||
jsonreq, err := json.Marshal(reqif)
|
||||
var reqs []shared.Request
|
||||
batch := true
|
||||
err = json.Unmarshal(jsonreq, &reqs)
|
||||
if err != nil {
|
||||
reqs = make([]shared.Request, 1)
|
||||
err = json.Unmarshal(jsonreq, &reqs[0])
|
||||
batch = false
|
||||
}
|
||||
|
||||
call.Otto.Set("response_len", len(reqs))
|
||||
call.Otto.Run("var ret_response = new Array(response_len);")
|
||||
|
||||
for i, req := range reqs {
|
||||
var respif interface{}
|
||||
err := self.client.Send(&req)
|
||||
if err != nil {
|
||||
return self.err(call, -32603, err.Error(), req.Id)
|
||||
}
|
||||
|
||||
recv:
|
||||
respif, err = self.client.Recv()
|
||||
if err != nil {
|
||||
return self.err(call, -32603, err.Error(), req.Id)
|
||||
}
|
||||
|
||||
agentreq, isRequest := respif.(*shared.Request)
|
||||
if isRequest {
|
||||
self.handleRequest(agentreq)
|
||||
goto recv // receive response after agent interaction
|
||||
}
|
||||
|
||||
sucres, isSuccessResponse := respif.(*shared.SuccessResponse)
|
||||
errres, isErrorResponse := respif.(*shared.ErrorResponse)
|
||||
if !isSuccessResponse && !isErrorResponse {
|
||||
return self.err(call, -32603, fmt.Sprintf("Invalid response type (%T)", respif), req.Id)
|
||||
}
|
||||
|
||||
call.Otto.Set("ret_jsonrpc", shared.JsonRpcVersion)
|
||||
call.Otto.Set("ret_id", req.Id)
|
||||
|
||||
var res []byte
|
||||
if isSuccessResponse {
|
||||
res, err = json.Marshal(sucres.Result)
|
||||
} else if isErrorResponse {
|
||||
res, err = json.Marshal(errres.Error)
|
||||
}
|
||||
|
||||
call.Otto.Set("ret_result", string(res))
|
||||
call.Otto.Set("response_idx", i)
|
||||
response, err = call.Otto.Run(`
|
||||
ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, result: JSON.parse(ret_result) };
|
||||
`)
|
||||
}
|
||||
|
||||
if !batch {
|
||||
call.Otto.Run("ret_response = ret_response[0];")
|
||||
}
|
||||
|
||||
if call.Argument(1).IsObject() {
|
||||
call.Otto.Set("callback", call.Argument(1))
|
||||
call.Otto.Run(`
|
||||
if (Object.prototype.toString.call(callback) == '[object Function]') {
|
||||
callback(null, ret_response);
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// handleRequest will handle user agent requests by interacting with the user and sending
|
||||
// the user response back to the geth service
|
||||
func (self *Jeth) handleRequest(req *shared.Request) bool {
|
||||
var err error
|
||||
var args []interface{}
|
||||
if err = json.Unmarshal(req.Params, &args); err != nil {
|
||||
glog.V(logger.Info).Infof("Unable to parse agent request - %v\n", err)
|
||||
return false
|
||||
}
|
||||
|
||||
switch req.Method {
|
||||
case useragent.AskPasswordMethod:
|
||||
return self.askPassword(req.Id, req.Jsonrpc, args)
|
||||
case useragent.ConfirmTransactionMethod:
|
||||
return self.confirmTransaction(req.Id, req.Jsonrpc, args)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// askPassword will ask the user to supply the password for a given account
|
||||
func (self *Jeth) askPassword(id interface{}, jsonrpc string, args []interface{}) bool {
|
||||
var err error
|
||||
var passwd string
|
||||
if len(args) >= 1 {
|
||||
if account, ok := args[0].(string); ok {
|
||||
fmt.Printf("Unlock account %s\n", account)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
passwd, err = utils.PromptPassword("Passphrase: ", true)
|
||||
|
||||
if err = self.client.Send(shared.NewRpcResponse(id, jsonrpc, passwd, err)); err != nil {
|
||||
glog.V(logger.Info).Infof("Unable to send user agent ask password response - %v\n", err)
|
||||
}
|
||||
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (self *Jeth) confirmTransaction(id interface{}, jsonrpc string, args []interface{}) bool {
|
||||
// Accept all tx which are send from this console
|
||||
return self.client.Send(shared.NewRpcResponse(id, jsonrpc, true, nil)) == nil
|
||||
}
|
||||
|
||||
// throwJSExeception panics on an otto value, the Otto VM will then throw msg as a javascript error.
|
||||
func throwJSExeception(msg interface{}) otto.Value {
|
||||
p, _ := otto.ToValue(msg)
|
||||
panic(p)
|
||||
return p
|
||||
}
|
||||
|
||||
// Sleep will halt the console for arg[0] seconds.
|
||||
func (self *Jeth) Sleep(call otto.FunctionCall) (response otto.Value) {
|
||||
if len(call.ArgumentList) >= 1 {
|
||||
if call.Argument(0).IsNumber() {
|
||||
sleep, _ := call.Argument(0).ToInteger()
|
||||
time.Sleep(time.Duration(sleep) * time.Second)
|
||||
return otto.TrueValue()
|
||||
}
|
||||
}
|
||||
return throwJSExeception("usage: sleep(<sleep in seconds>)")
|
||||
}
|
||||
|
||||
// SleepBlocks will wait for a specified number of new blocks or max for a
|
||||
// given of seconds. sleepBlocks(nBlocks[, maxSleep]).
|
||||
func (self *Jeth) SleepBlocks(call otto.FunctionCall) (response otto.Value) {
|
||||
nBlocks := int64(0)
|
||||
maxSleep := int64(9999999999999999) // indefinitely
|
||||
|
||||
nArgs := len(call.ArgumentList)
|
||||
|
||||
if nArgs == 0 {
|
||||
throwJSExeception("usage: sleepBlocks(<n blocks>[, max sleep in seconds])")
|
||||
}
|
||||
|
||||
if nArgs >= 1 {
|
||||
if call.Argument(0).IsNumber() {
|
||||
nBlocks, _ = call.Argument(0).ToInteger()
|
||||
} else {
|
||||
throwJSExeception("expected number as first argument")
|
||||
}
|
||||
}
|
||||
|
||||
if nArgs >= 2 {
|
||||
if call.Argument(1).IsNumber() {
|
||||
maxSleep, _ = call.Argument(1).ToInteger()
|
||||
} else {
|
||||
throwJSExeception("expected number as second argument")
|
||||
}
|
||||
}
|
||||
|
||||
// go through the console, this will allow web3 to call the appropriate
|
||||
// callbacks if a delayed response or notification is received.
|
||||
currentBlockNr := func() int64 {
|
||||
result, err := call.Otto.Run("eth.blockNumber")
|
||||
if err != nil {
|
||||
throwJSExeception(err.Error())
|
||||
}
|
||||
blockNr, err := result.ToInteger()
|
||||
if err != nil {
|
||||
throwJSExeception(err.Error())
|
||||
}
|
||||
return blockNr
|
||||
}
|
||||
|
||||
targetBlockNr := currentBlockNr() + nBlocks
|
||||
deadline := time.Now().Add(time.Duration(maxSleep) * time.Second)
|
||||
|
||||
for time.Now().Before(deadline) {
|
||||
if currentBlockNr() >= targetBlockNr {
|
||||
return otto.TrueValue()
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
return otto.FalseValue()
|
||||
}
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package v2
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@ -37,7 +37,7 @@ const (
|
||||
)
|
||||
|
||||
// JSON-RPC request
|
||||
type jsonRequest struct {
|
||||
type JSONRequest struct {
|
||||
Method string `json:"method"`
|
||||
Version string `json:"jsonrpc"`
|
||||
Id *int64 `json:"id,omitempty"`
|
||||
@ -45,24 +45,24 @@ type jsonRequest struct {
|
||||
}
|
||||
|
||||
// JSON-RPC response
|
||||
type jsonSuccessResponse struct {
|
||||
type JSONSuccessResponse struct {
|
||||
Version string `json:"jsonrpc"`
|
||||
Id int64 `json:"id"`
|
||||
Result interface{} `json:"result,omitempty"`
|
||||
}
|
||||
|
||||
// JSON-RPC error object
|
||||
type jsonError struct {
|
||||
type JSONError struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// JSON-RPC error response
|
||||
type jsonErrResponse struct {
|
||||
type JSONErrResponse struct {
|
||||
Version string `json:"jsonrpc"`
|
||||
Id *int64 `json:"id,omitempty"`
|
||||
Error jsonError `json:"error"`
|
||||
Error JSONError `json:"error"`
|
||||
}
|
||||
|
||||
// JSON-RPC notification payload
|
||||
@ -85,7 +85,7 @@ type jsonCodec struct {
|
||||
isClosed int32
|
||||
d *json.Decoder
|
||||
e *json.Encoder
|
||||
req jsonRequest
|
||||
req JSONRequest
|
||||
rw io.ReadWriteCloser
|
||||
}
|
||||
|
||||
@ -126,7 +126,7 @@ func (c *jsonCodec) ReadRequestHeaders() ([]rpcRequest, bool, RPCError) {
|
||||
// parseRequest will parse a single request from the given RawMessage. It will return the parsed request, an indication
|
||||
// if the request was a batch or an error when the request could not be parsed.
|
||||
func parseRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, RPCError) {
|
||||
var in jsonRequest
|
||||
var in JSONRequest
|
||||
if err := json.Unmarshal(incomingMsg, &in); err != nil {
|
||||
return nil, false, &invalidMessageError{err.Error()}
|
||||
}
|
||||
@ -175,7 +175,7 @@ func parseRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, RPCError) {
|
||||
// parseBatchRequest will parse a batch request into a collection of requests from the given RawMessage, an indication
|
||||
// if the request was a batch or an error when the request could not be read.
|
||||
func parseBatchRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, RPCError) {
|
||||
var in []jsonRequest
|
||||
var in []JSONRequest
|
||||
if err := json.Unmarshal(incomingMsg, &in); err != nil {
|
||||
return nil, false, &invalidMessageError{err.Error()}
|
||||
}
|
||||
@ -296,21 +296,21 @@ func parsePositionalArguments(args json.RawMessage, argTypes []reflect.Type) ([]
|
||||
// CreateResponse will create a JSON-RPC success response with the given id and reply as result.
|
||||
func (c *jsonCodec) CreateResponse(id int64, reply interface{}) interface{} {
|
||||
if isHexNum(reflect.TypeOf(reply)) {
|
||||
return &jsonSuccessResponse{Version: jsonRPCVersion, Id: id, Result: fmt.Sprintf(`%#x`, reply)}
|
||||
return &JSONSuccessResponse{Version: jsonRPCVersion, Id: id, Result: fmt.Sprintf(`%#x`, reply)}
|
||||
}
|
||||
return &jsonSuccessResponse{Version: jsonRPCVersion, Id: id, Result: reply}
|
||||
return &JSONSuccessResponse{Version: jsonRPCVersion, Id: id, Result: reply}
|
||||
}
|
||||
|
||||
// CreateErrorResponse will create a JSON-RPC error response with the given id and error.
|
||||
func (c *jsonCodec) CreateErrorResponse(id *int64, err RPCError) interface{} {
|
||||
return &jsonErrResponse{Version: jsonRPCVersion, Id: id, Error: jsonError{Code: err.Code(), Message: err.Error()}}
|
||||
return &JSONErrResponse{Version: jsonRPCVersion, Id: id, Error: JSONError{Code: err.Code(), Message: err.Error()}}
|
||||
}
|
||||
|
||||
// CreateErrorResponseWithInfo will create a JSON-RPC error response with the given id and error.
|
||||
// info is optional and contains additional information about the error. When an empty string is passed it is ignored.
|
||||
func (c *jsonCodec) CreateErrorResponseWithInfo(id *int64, err RPCError, info interface{}) interface{} {
|
||||
return &jsonErrResponse{Version: jsonRPCVersion, Id: id,
|
||||
Error: jsonError{Code: err.Code(), Message: err.Error(), Data: info}}
|
||||
return &JSONErrResponse{Version: jsonRPCVersion, Id: id,
|
||||
Error: JSONError{Code: err.Code(), Message: err.Error(), Data: info}}
|
||||
}
|
||||
|
||||
// CreateNotification will create a JSON-RPC notification with the given subscription id and event as params.
|
@ -1,4 +1,4 @@
|
||||
package v2
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"bufio"
|
@ -14,23 +14,37 @@
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package v2
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"golang.org/x/net/context"
|
||||
"gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
const (
|
||||
stopPendingRequestTimeout = 3 * time.Second // give pending requests stopPendingRequestTimeout the time to finish when the server is stopped
|
||||
|
||||
DefaultIpcApis = "admin,eth,debug,miner,net,shh,txpool,personal,web3"
|
||||
DefaultHttpRpcApis = "eth,net,web3"
|
||||
)
|
||||
|
||||
// NewServer will create a new server instance with no registered handlers.
|
||||
func NewServer() *Server {
|
||||
server := &Server{services: make(serviceRegistry), subscriptions: make(subscriptionRegistry)}
|
||||
server := &Server{
|
||||
services: make(serviceRegistry),
|
||||
subscriptions: make(subscriptionRegistry),
|
||||
codecs: set.New(),
|
||||
run: 1,
|
||||
}
|
||||
|
||||
// register a default service which will provide meta information about the RPC service such as the services and
|
||||
// methods it offers.
|
||||
@ -124,14 +138,37 @@ func (s *Server) ServeCodec(codec ServerCodec) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
for {
|
||||
s.codecsMu.Lock()
|
||||
if atomic.LoadInt32(&s.run) != 1 { // server stopped
|
||||
s.codecsMu.Unlock()
|
||||
return
|
||||
}
|
||||
s.codecs.Add(codec)
|
||||
s.codecsMu.Unlock()
|
||||
|
||||
for atomic.LoadInt32(&s.run) == 1 {
|
||||
reqs, batch, err := s.readRequest(codec)
|
||||
|
||||
if err != nil {
|
||||
glog.V(logger.Debug).Infof("%v\n", err)
|
||||
codec.Write(codec.CreateErrorResponse(nil, err))
|
||||
break
|
||||
}
|
||||
|
||||
if atomic.LoadInt32(&s.run) != 1 {
|
||||
err = &shutdownError{}
|
||||
if batch {
|
||||
resps := make([]interface{}, len(reqs))
|
||||
for i, r := range reqs {
|
||||
resps[i] = codec.CreateErrorResponse(&r.id, err)
|
||||
}
|
||||
codec.Write(resps)
|
||||
} else {
|
||||
codec.Write(codec.CreateErrorResponse(&reqs[0].id, err))
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if batch {
|
||||
go s.execBatch(ctx, codec, reqs)
|
||||
} else {
|
||||
@ -140,6 +177,22 @@ func (s *Server) ServeCodec(codec ServerCodec) {
|
||||
}
|
||||
}
|
||||
|
||||
// Stop will stop reading new requests, wait for stopPendingRequestTimeout to allow pending requests to finish,
|
||||
// close all codecs which will cancels pending requests/subscriptions.
|
||||
func (s *Server) Stop() {
|
||||
if atomic.CompareAndSwapInt32(&s.run, 1, 0) {
|
||||
glog.V(logger.Debug).Infoln("RPC Server shutdown initiatied")
|
||||
time.AfterFunc(stopPendingRequestTimeout, func() {
|
||||
s.codecsMu.Lock()
|
||||
defer s.codecsMu.Unlock()
|
||||
s.codecs.Each(func(c interface{}) bool {
|
||||
c.(ServerCodec).Close()
|
||||
return true
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// sendNotification will create a notification from the given event by serializing member fields of the event.
|
||||
// It will then send the notification to the client, when it fails the codec is closed. When the event has multiple
|
||||
// fields an array of values is returned.
|
@ -1,4 +1,20 @@
|
||||
package v2
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@ -91,7 +107,7 @@ func (c *ServerTestCodec) ReadRequestHeaders() ([]rpcRequest, bool, RPCError) {
|
||||
c.counter += 1
|
||||
|
||||
if c.counter == 1 {
|
||||
var req jsonRequest
|
||||
var req JSONRequest
|
||||
json.Unmarshal(c.input, &req)
|
||||
return []rpcRequest{rpcRequest{id: *req.Id, isPubSub: false, service: "test", method: req.Method, params: req.Payload}}, false, nil
|
||||
}
|
||||
@ -157,16 +173,16 @@ func (c *ServerTestCodec) ParseRequestArguments(argTypes []reflect.Type, payload
|
||||
}
|
||||
|
||||
func (c *ServerTestCodec) CreateResponse(id int64, reply interface{}) interface{} {
|
||||
return &jsonSuccessResponse{Version: jsonRPCVersion, Id: id, Result: reply}
|
||||
return &JSONSuccessResponse{Version: jsonRPCVersion, Id: id, Result: reply}
|
||||
}
|
||||
|
||||
func (c *ServerTestCodec) CreateErrorResponse(id *int64, err RPCError) interface{} {
|
||||
return &jsonErrResponse{Version: jsonRPCVersion, Id: id, Error: jsonError{Code: err.Code(), Message: err.Error()}}
|
||||
return &JSONErrResponse{Version: jsonRPCVersion, Id: id, Error: JSONError{Code: err.Code(), Message: err.Error()}}
|
||||
}
|
||||
|
||||
func (c *ServerTestCodec) CreateErrorResponseWithInfo(id *int64, err RPCError, info interface{}) interface{} {
|
||||
return &jsonErrResponse{Version: jsonRPCVersion, Id: id,
|
||||
Error: jsonError{Code: err.Code(), Message: err.Error(), Data: info}}
|
||||
return &JSONErrResponse{Version: jsonRPCVersion, Id: id,
|
||||
Error: JSONError{Code: err.Code(), Message: err.Error(), Data: info}}
|
||||
}
|
||||
|
||||
func (c *ServerTestCodec) CreateNotification(subid string, event interface{}) interface{} {
|
||||
@ -203,7 +219,7 @@ func TestServerMethodExecution(t *testing.T) {
|
||||
}
|
||||
|
||||
id := int64(12345)
|
||||
req := jsonRequest{
|
||||
req := JSONRequest{
|
||||
Method: "echo",
|
||||
Version: "2.0",
|
||||
Id: &id,
|
||||
@ -233,7 +249,7 @@ func TestServerMethodWithCtx(t *testing.T) {
|
||||
}
|
||||
|
||||
id := int64(12345)
|
||||
req := jsonRequest{
|
||||
req := JSONRequest{
|
||||
Method: "echoWithCtx",
|
||||
Version: "2.0",
|
||||
Id: &id,
|
@ -1,126 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package shared
|
||||
|
||||
import "fmt"
|
||||
|
||||
type InvalidTypeError struct {
|
||||
method string
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e *InvalidTypeError) Error() string {
|
||||
return fmt.Sprintf("invalid type on field %s: %s", e.method, e.msg)
|
||||
}
|
||||
|
||||
func NewInvalidTypeError(method, msg string) *InvalidTypeError {
|
||||
return &InvalidTypeError{
|
||||
method: method,
|
||||
msg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
type InsufficientParamsError struct {
|
||||
have int
|
||||
want int
|
||||
}
|
||||
|
||||
func (e *InsufficientParamsError) Error() string {
|
||||
return fmt.Sprintf("insufficient params, want %d have %d", e.want, e.have)
|
||||
}
|
||||
|
||||
func NewInsufficientParamsError(have int, want int) *InsufficientParamsError {
|
||||
return &InsufficientParamsError{
|
||||
have: have,
|
||||
want: want,
|
||||
}
|
||||
}
|
||||
|
||||
type NotImplementedError struct {
|
||||
Method string
|
||||
}
|
||||
|
||||
func (e *NotImplementedError) Error() string {
|
||||
return fmt.Sprintf("%s method not implemented", e.Method)
|
||||
}
|
||||
|
||||
func NewNotImplementedError(method string) *NotImplementedError {
|
||||
return &NotImplementedError{
|
||||
Method: method,
|
||||
}
|
||||
}
|
||||
|
||||
type NotReadyError struct {
|
||||
Resource string
|
||||
}
|
||||
|
||||
func (e *NotReadyError) Error() string {
|
||||
return fmt.Sprintf("%s not ready", e.Resource)
|
||||
}
|
||||
|
||||
func NewNotReadyError(resource string) *NotReadyError {
|
||||
return &NotReadyError{
|
||||
Resource: resource,
|
||||
}
|
||||
}
|
||||
|
||||
type DecodeParamError struct {
|
||||
err string
|
||||
}
|
||||
|
||||
func (e *DecodeParamError) Error() string {
|
||||
return fmt.Sprintf("could not decode, %s", e.err)
|
||||
|
||||
}
|
||||
|
||||
func NewDecodeParamError(errstr string) error {
|
||||
return &DecodeParamError{
|
||||
err: errstr,
|
||||
}
|
||||
}
|
||||
|
||||
type ValidationError struct {
|
||||
ParamName string
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e *ValidationError) Error() string {
|
||||
return fmt.Sprintf("%s not valid, %s", e.ParamName, e.msg)
|
||||
}
|
||||
|
||||
func NewValidationError(param string, msg string) error {
|
||||
return &ValidationError{
|
||||
ParamName: param,
|
||||
msg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
type NotAvailableError struct {
|
||||
Method string
|
||||
Reason string
|
||||
}
|
||||
|
||||
func (e *NotAvailableError) Error() string {
|
||||
return fmt.Sprintf("%s method not available: %s", e.Method, e.Reason)
|
||||
}
|
||||
|
||||
func NewNotAvailableError(method string, reason string) *NotAvailableError {
|
||||
return &NotAvailableError{
|
||||
Method: method,
|
||||
Reason: reason,
|
||||
}
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package shared
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
)
|
||||
|
||||
// Ethereum RPC API interface
|
||||
type EthereumApi interface {
|
||||
// API identifier
|
||||
Name() string
|
||||
|
||||
// API version
|
||||
ApiVersion() string
|
||||
|
||||
// Execute the given request and returns the response or an error
|
||||
Execute(*Request) (interface{}, error)
|
||||
|
||||
// List of supported RCP methods this API provides
|
||||
Methods() []string
|
||||
}
|
||||
|
||||
// RPC request
|
||||
type Request struct {
|
||||
Id interface{} `json:"id"`
|
||||
Jsonrpc string `json:"jsonrpc"`
|
||||
Method string `json:"method"`
|
||||
Params json.RawMessage `json:"params"`
|
||||
}
|
||||
|
||||
// RPC response
|
||||
type Response struct {
|
||||
Id interface{} `json:"id"`
|
||||
Jsonrpc string `json:"jsonrpc"`
|
||||
}
|
||||
|
||||
// RPC success response
|
||||
type SuccessResponse struct {
|
||||
Id interface{} `json:"id"`
|
||||
Jsonrpc string `json:"jsonrpc"`
|
||||
Result interface{} `json:"result"`
|
||||
}
|
||||
|
||||
// RPC error response
|
||||
type ErrorResponse struct {
|
||||
Id interface{} `json:"id"`
|
||||
Jsonrpc string `json:"jsonrpc"`
|
||||
Error *ErrorObject `json:"error"`
|
||||
}
|
||||
|
||||
// RPC error response details
|
||||
type ErrorObject struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
// Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
// Create RPC error response, this allows for custom error codes
|
||||
func NewRpcErrorResponse(id interface{}, jsonrpcver string, errCode int, err error) *ErrorResponse {
|
||||
jsonerr := &ErrorObject{errCode, err.Error()}
|
||||
response := ErrorResponse{Jsonrpc: jsonrpcver, Id: id, Error: jsonerr}
|
||||
|
||||
glog.V(logger.Detail).Infof("Generated error response: %s", response)
|
||||
return &response
|
||||
}
|
||||
|
||||
// Create RPC response
|
||||
func NewRpcResponse(id interface{}, jsonrpcver string, reply interface{}, err error) *interface{} {
|
||||
var response interface{}
|
||||
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
response = &SuccessResponse{Jsonrpc: jsonrpcver, Id: id, Result: reply}
|
||||
case *NotImplementedError:
|
||||
jsonerr := &ErrorObject{-32601, err.Error()}
|
||||
response = &ErrorResponse{Jsonrpc: jsonrpcver, Id: id, Error: jsonerr}
|
||||
case *NotReadyError:
|
||||
jsonerr := &ErrorObject{-32000, err.Error()}
|
||||
response = &ErrorResponse{Jsonrpc: jsonrpcver, Id: id, Error: jsonerr}
|
||||
case *DecodeParamError, *InsufficientParamsError, *ValidationError, *InvalidTypeError:
|
||||
jsonerr := &ErrorObject{-32602, err.Error()}
|
||||
response = &ErrorResponse{Jsonrpc: jsonrpcver, Id: id, Error: jsonerr}
|
||||
default:
|
||||
jsonerr := &ErrorObject{-32603, err.Error()}
|
||||
response = &ErrorResponse{Jsonrpc: jsonrpcver, Id: id, Error: jsonerr}
|
||||
}
|
||||
|
||||
glog.V(logger.Detail).Infof("Generated response: %T %s", response, response)
|
||||
return &response
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package shared
|
||||
|
||||
import "strings"
|
||||
|
||||
const (
|
||||
AdminApiName = "admin"
|
||||
EthApiName = "eth"
|
||||
DbApiName = "db"
|
||||
DebugApiName = "debug"
|
||||
MergedApiName = "merged"
|
||||
MinerApiName = "miner"
|
||||
NetApiName = "net"
|
||||
ShhApiName = "shh"
|
||||
TxPoolApiName = "txpool"
|
||||
PersonalApiName = "personal"
|
||||
Web3ApiName = "web3"
|
||||
|
||||
JsonRpcVersion = "2.0"
|
||||
)
|
||||
|
||||
var (
|
||||
// All API's
|
||||
AllApis = strings.Join([]string{
|
||||
AdminApiName, DbApiName, EthApiName, DebugApiName, MinerApiName, NetApiName,
|
||||
ShhApiName, TxPoolApiName, PersonalApiName, Web3ApiName,
|
||||
}, ",")
|
||||
)
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package v2
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -25,6 +25,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
// API describes the set of methods offered over the RPC interface
|
||||
@ -75,6 +76,10 @@ type Server struct {
|
||||
services serviceRegistry
|
||||
muSubcriptions sync.Mutex // protects subscriptions
|
||||
subscriptions subscriptionRegistry
|
||||
|
||||
run int32
|
||||
codecsMu sync.Mutex
|
||||
codecs *set.Set
|
||||
}
|
||||
|
||||
// rpcRequest represents a raw incoming RPC request
|
||||
@ -350,3 +355,14 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
|
||||
func (bn *BlockNumber) Int64() int64 {
|
||||
return (int64)(*bn)
|
||||
}
|
||||
|
||||
// Client defines the interface for go client that wants to connect to a geth RPC endpoint
|
||||
type Client interface {
|
||||
// SupportedModules returns the collection of API's the server offers
|
||||
SupportedModules() (map[string]string, error)
|
||||
|
||||
Send(req interface{}) error
|
||||
Recv(msg interface{}) error
|
||||
|
||||
Close()
|
||||
}
|
@ -1,4 +1,20 @@
|
||||
package v2
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"bytes"
|
@ -1,24 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// package user agent provides frontends and agents which can interact with the user
|
||||
package useragent
|
||||
|
||||
var (
|
||||
AskPasswordMethod = "agent_askPassword"
|
||||
ConfirmTransactionMethod = "agent_confirmTransaction"
|
||||
EnableUserAgentMethod = "admin_enableUserAgent"
|
||||
)
|
@ -1,166 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package useragent
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
)
|
||||
|
||||
// remoteFrontend implements xeth.Frontend and will communicate with an external
|
||||
// user agent over a connection
|
||||
type RemoteFrontend struct {
|
||||
enabled bool
|
||||
mgr *accounts.Manager
|
||||
d *json.Decoder
|
||||
e *json.Encoder
|
||||
n int
|
||||
}
|
||||
|
||||
// NewRemoteFrontend creates a new frontend which will interact with an user agent
|
||||
// over the given connection
|
||||
func NewRemoteFrontend(conn net.Conn, mgr *accounts.Manager) *RemoteFrontend {
|
||||
return &RemoteFrontend{false, mgr, json.NewDecoder(conn), json.NewEncoder(conn), 0}
|
||||
}
|
||||
|
||||
// Enable will enable user interaction
|
||||
func (fe *RemoteFrontend) Enable() {
|
||||
fe.enabled = true
|
||||
}
|
||||
|
||||
func (fe *RemoteFrontend) AskPassword() (string, bool) {
|
||||
if !fe.enabled {
|
||||
return "", false
|
||||
}
|
||||
|
||||
err := fe.send(AskPasswordMethod)
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Unable to send password request to agent - %v\n", err)
|
||||
return "", false
|
||||
}
|
||||
|
||||
passwdRes, err := fe.recv()
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Unable to recv password response from agent - %v\n", err)
|
||||
return "", false
|
||||
}
|
||||
|
||||
if passwd, ok := passwdRes.Result.(string); ok {
|
||||
return passwd, true
|
||||
}
|
||||
|
||||
return "", false
|
||||
|
||||
}
|
||||
|
||||
// UnlockAccount asks the user agent for the user password and tries to unlock the account.
|
||||
// It will try 3 attempts before giving up.
|
||||
func (fe *RemoteFrontend) UnlockAccount(address []byte) bool {
|
||||
if !fe.enabled {
|
||||
return false
|
||||
}
|
||||
|
||||
err := fe.send(AskPasswordMethod, common.Bytes2Hex(address))
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Unable to send password request to agent - %v\n", err)
|
||||
return false
|
||||
}
|
||||
|
||||
passwdRes, err := fe.recv()
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Unable to recv password response from agent - %v\n", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if passwd, ok := passwdRes.Result.(string); ok {
|
||||
err = fe.mgr.Unlock(common.BytesToAddress(address), passwd)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
glog.V(logger.Debug).Infoln("3 invalid account unlock attempts")
|
||||
return false
|
||||
}
|
||||
|
||||
// ConfirmTransaction asks the user for approval
|
||||
func (fe *RemoteFrontend) ConfirmTransaction(tx string) bool {
|
||||
if !fe.enabled {
|
||||
return true // backwards compatibility
|
||||
}
|
||||
|
||||
err := fe.send(ConfirmTransactionMethod, tx)
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Unable to send tx confirmation request to agent - %v\n", err)
|
||||
return false
|
||||
}
|
||||
|
||||
confirmResponse, err := fe.recv()
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Unable to recv tx confirmation response from agent - %v\n", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if confirmed, ok := confirmResponse.Result.(bool); ok {
|
||||
return confirmed
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// send request to the agent
|
||||
func (fe *RemoteFrontend) send(method string, params ...interface{}) error {
|
||||
fe.n += 1
|
||||
|
||||
p, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
glog.V(logger.Info).Infof("Unable to send agent request %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
req := shared.Request{
|
||||
Method: method,
|
||||
Jsonrpc: shared.JsonRpcVersion,
|
||||
Id: fe.n,
|
||||
Params: p,
|
||||
}
|
||||
|
||||
return fe.e.Encode(&req)
|
||||
}
|
||||
|
||||
// recv user response from agent
|
||||
func (fe *RemoteFrontend) recv() (*shared.SuccessResponse, error) {
|
||||
var res json.RawMessage
|
||||
if err := fe.d.Decode(&res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response shared.SuccessResponse
|
||||
if err := json.Unmarshal(res, &response); err == nil {
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Invalid user agent response")
|
||||
}
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package v2
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
@ -25,6 +25,8 @@ import (
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
@ -212,3 +214,33 @@ func newSubscriptionId() (string, error) {
|
||||
}
|
||||
return "0x" + hex.EncodeToString(subid[:]), nil
|
||||
}
|
||||
|
||||
// SupportedModules returns the collection of API's that the RPC server offers
|
||||
// on which the given client connects.
|
||||
func SupportedModules(client Client) (map[string]string, error) {
|
||||
req := map[string]interface{}{
|
||||
"id": 1,
|
||||
"method": "rpc_modules",
|
||||
}
|
||||
|
||||
if err := client.Send(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response map[string]interface{}
|
||||
if err := client.Recv(&response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if payload, ok := response["result"]; ok {
|
||||
mods := make(map[string]string)
|
||||
if modules, ok := payload.(map[string]interface{}); ok {
|
||||
for m, v := range modules {
|
||||
mods[m] = fmt.Sprintf("%s", v)
|
||||
}
|
||||
return mods, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unable to retrieve modules")
|
||||
}
|
235
rpc/websocket.go
Normal file
235
rpc/websocket.go
Normal file
@ -0,0 +1,235 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"os"
|
||||
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"golang.org/x/net/websocket"
|
||||
"gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
var (
|
||||
wsServerMu sync.Mutex
|
||||
wsRPCServer *Server
|
||||
wsListener net.Listener
|
||||
)
|
||||
|
||||
// wsReaderWriterCloser reads and write payloads from and to a websocket connection.
|
||||
type wsReaderWriterCloser struct {
|
||||
c *websocket.Conn
|
||||
}
|
||||
|
||||
// Read will read incoming payload data into p.
|
||||
func (rw *wsReaderWriterCloser) Read(p []byte) (int, error) {
|
||||
return rw.c.Read(p)
|
||||
}
|
||||
|
||||
// Write writes p to the websocket.
|
||||
func (rw *wsReaderWriterCloser) Write(p []byte) (int, error) {
|
||||
return rw.c.Write(p)
|
||||
}
|
||||
|
||||
// Close closes the websocket connection.
|
||||
func (rw *wsReaderWriterCloser) Close() error {
|
||||
return rw.c.Close()
|
||||
}
|
||||
|
||||
// wsHandler accepts a websocket connection and handles incoming RPC requests.
|
||||
// Will return when the websocket connection is closed, either by the client or
|
||||
// server.
|
||||
func wsHandler(conn *websocket.Conn) {
|
||||
rwc := &wsReaderWriterCloser{conn}
|
||||
wsRPCServer.ServeCodec(NewJSONCodec(rwc))
|
||||
}
|
||||
|
||||
// wsHandshakeValidator returns a handler that verifies the origin during the
|
||||
// websocket upgrade process. When a '*' is specified as an allowed origins all
|
||||
// connections are accepted.
|
||||
func wsHandshakeValidator(allowedOrigins []string) func(*websocket.Config, *http.Request) error {
|
||||
origins := set.New()
|
||||
allowAllOrigins := false
|
||||
|
||||
for _, origin := range allowedOrigins {
|
||||
if origin == "*" {
|
||||
allowAllOrigins = true
|
||||
}
|
||||
if origin != "" {
|
||||
origins.Add(origin)
|
||||
}
|
||||
}
|
||||
|
||||
// allow localhost if no allowedOrigins are specified
|
||||
if len(origins.List()) == 0 {
|
||||
origins.Add("http://localhost")
|
||||
if hostname, err := os.Hostname(); err == nil {
|
||||
origins.Add("http://" + hostname)
|
||||
}
|
||||
}
|
||||
|
||||
glog.V(logger.Debug).Infof("Allowed origin(s) for WS RPC interface %v\n", origins.List())
|
||||
|
||||
f := func(cfg *websocket.Config, req *http.Request) error {
|
||||
origin := req.Header.Get("Origin")
|
||||
if allowAllOrigins || origins.Has(origin) {
|
||||
return nil
|
||||
}
|
||||
glog.V(logger.Debug).Infof("origin '%s' not allowed on WS-RPC interface\n", origin)
|
||||
return fmt.Errorf("origin %s not allowed", origin)
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// StartWS will start a websocket RPC server on the given address and port.
|
||||
func StartWS(address string, port int, corsdomains []string, apis []API) error {
|
||||
wsServerMu.Lock()
|
||||
defer wsServerMu.Unlock()
|
||||
|
||||
if wsRPCServer != nil {
|
||||
return fmt.Errorf("WS RPC interface already started on %s", wsListener.Addr())
|
||||
}
|
||||
|
||||
rpcServer := NewServer()
|
||||
for _, api := range apis {
|
||||
if err := rpcServer.RegisterName(api.Namespace, api.Service); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", address, port))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wsServer := websocket.Server{Handshake: wsHandshakeValidator(corsdomains), Handler: wsHandler}
|
||||
wsHTTPServer := http.Server{Handler: wsServer}
|
||||
|
||||
go wsHTTPServer.Serve(listener)
|
||||
|
||||
wsListener = listener
|
||||
wsRPCServer = rpcServer
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// StopWS stops the running websocket RPC server.
|
||||
func StopWS() error {
|
||||
wsServerMu.Lock()
|
||||
defer wsServerMu.Unlock()
|
||||
|
||||
if wsRPCServer == nil {
|
||||
return errors.New("HTTP RPC interface not started")
|
||||
}
|
||||
|
||||
wsListener.Close()
|
||||
wsRPCServer.Stop()
|
||||
|
||||
wsRPCServer = nil
|
||||
wsListener = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// wsClient represents a RPC client that communicates over websockets with a
|
||||
// RPC server.
|
||||
type wsClient struct {
|
||||
endpoint string
|
||||
connMu sync.Mutex
|
||||
conn *websocket.Conn
|
||||
}
|
||||
|
||||
// NewWSClientj creates a new RPC client that communicates with a RPC server
|
||||
// that is listening on the given endpoint using JSON encoding.
|
||||
func NewWSClient(endpoint string) (*wsClient, error) {
|
||||
return &wsClient{endpoint: endpoint}, nil
|
||||
}
|
||||
|
||||
// connection will return a websocket connection to the RPC server. It will
|
||||
// (re)connect when necessary.
|
||||
func (client *wsClient) connection() (*websocket.Conn, error) {
|
||||
if client.conn != nil {
|
||||
return client.conn, nil
|
||||
}
|
||||
|
||||
origin, err := os.Hostname()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
origin = "http://" + origin
|
||||
client.conn, err = websocket.Dial(client.endpoint, "", origin)
|
||||
|
||||
return client.conn, err
|
||||
}
|
||||
|
||||
// SupportedModules is the collection of modules the RPC server offers.
|
||||
func (client *wsClient) SupportedModules() (map[string]string, error) {
|
||||
return SupportedModules(client)
|
||||
}
|
||||
|
||||
// Send writes the JSON serialized msg to the websocket. It will create a new
|
||||
// websocket connection to the server if the client is currently not connected.
|
||||
func (client *wsClient) Send(msg interface{}) (err error) {
|
||||
client.connMu.Lock()
|
||||
defer client.connMu.Unlock()
|
||||
|
||||
var conn *websocket.Conn
|
||||
if conn, err = client.connection(); err == nil {
|
||||
if err = websocket.JSON.Send(conn, msg); err != nil {
|
||||
client.conn.Close()
|
||||
client.conn = nil
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Recv reads a JSON message from the websocket and unmarshals it into msg.
|
||||
func (client *wsClient) Recv(msg interface{}) (err error) {
|
||||
client.connMu.Lock()
|
||||
defer client.connMu.Unlock()
|
||||
|
||||
var conn *websocket.Conn
|
||||
if conn, err = client.connection(); err == nil {
|
||||
if err = websocket.JSON.Receive(conn, msg); err != nil {
|
||||
client.conn.Close()
|
||||
client.conn = nil
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Close closes the underlaying websocket connection.
|
||||
func (client *wsClient) Close() {
|
||||
client.connMu.Lock()
|
||||
defer client.connMu.Unlock()
|
||||
|
||||
if client.conn != nil {
|
||||
client.conn.Close()
|
||||
client.conn = nil
|
||||
}
|
||||
|
||||
}
|
77
rpc/xeth.go
77
rpc/xeth.go
@ -1,77 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package rpc implements the Ethereum JSON-RPC API.
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/ethereum/go-ethereum/rpc/comms"
|
||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
||||
)
|
||||
|
||||
// Xeth is a native API interface to a remote node.
|
||||
type Xeth struct {
|
||||
client comms.EthereumClient
|
||||
reqId uint32
|
||||
}
|
||||
|
||||
// NewXeth constructs a new native API interface to a remote node.
|
||||
func NewXeth(client comms.EthereumClient) *Xeth {
|
||||
return &Xeth{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
// Call invokes a method with the given parameters are the remote node.
|
||||
func (self *Xeth) Call(method string, params []interface{}) (map[string]interface{}, error) {
|
||||
// Assemble the json RPC request
|
||||
data, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req := &shared.Request{
|
||||
Id: atomic.AddUint32(&self.reqId, 1),
|
||||
Jsonrpc: "2.0",
|
||||
Method: method,
|
||||
Params: data,
|
||||
}
|
||||
// Send the request over and retrieve the response
|
||||
if err := self.client.Send(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := self.client.Recv()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Ensure the response is valid, and extract the results
|
||||
success, isSuccessResponse := res.(*shared.SuccessResponse)
|
||||
failure, isFailureResponse := res.(*shared.ErrorResponse)
|
||||
switch {
|
||||
case isFailureResponse:
|
||||
return nil, fmt.Errorf("Method invocation failed: %v", failure.Error)
|
||||
|
||||
case isSuccessResponse:
|
||||
return success.Result.(map[string]interface{}), nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("Invalid response type: %v", reflect.TypeOf(res))
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user