admin.go 6.04 KB
Newer Older
Bas van Kervel's avatar
Bas van Kervel committed
1 2 3 4 5 6 7 8 9 10 11 12 13
package api

import (
	"fmt"
	"io"
	"os"

	"github.com/ethereum/go-ethereum/core"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/eth"
	"github.com/ethereum/go-ethereum/logger/glog"
	"github.com/ethereum/go-ethereum/rlp"
	"github.com/ethereum/go-ethereum/rpc/codec"
14
	"github.com/ethereum/go-ethereum/rpc/comms"
Bas van Kervel's avatar
Bas van Kervel committed
15 16 17 18 19
	"github.com/ethereum/go-ethereum/rpc/shared"
	"github.com/ethereum/go-ethereum/xeth"
)

const (
20
	AdminApiversion = "1.0"
Bas van Kervel's avatar
Bas van Kervel committed
21 22 23 24 25 26
	importBatchSize = 2500
)

var (
	// mapping between methods and handlers
	AdminMapping = map[string]adminhandler{
27 28 29 30 31 32 33 34 35
		"admin_addPeer":         (*adminApi).AddPeer,
		"admin_peers":           (*adminApi).Peers,
		"admin_nodeInfo":        (*adminApi).NodeInfo,
		"admin_exportChain":     (*adminApi).ExportChain,
		"admin_importChain":     (*adminApi).ImportChain,
		"admin_verbosity":       (*adminApi).Verbosity,
		"admin_chainSyncStatus": (*adminApi).ChainSyncStatus,
		"admin_setSolc":         (*adminApi).SetSolc,
		"admin_datadir":         (*adminApi).DataDir,
36 37
		"admin_startRPC":        (*adminApi).StartRPC,
		"admin_stopRPC":         (*adminApi).StopRPC,
Bas van Kervel's avatar
Bas van Kervel committed
38 39 40 41 42 43 44 45 46 47
	}
)

// admin callback handler
type adminhandler func(*adminApi, *shared.Request) (interface{}, error)

// admin api provider
type adminApi struct {
	xeth     *xeth.XEth
	ethereum *eth.Ethereum
48 49
	codec    codec.Codec
	coder    codec.ApiCoder
Bas van Kervel's avatar
Bas van Kervel committed
50 51 52
}

// create a new admin api instance
53
func NewAdminApi(xeth *xeth.XEth, ethereum *eth.Ethereum, codec codec.Codec) *adminApi {
Bas van Kervel's avatar
Bas van Kervel committed
54 55 56
	return &adminApi{
		xeth:     xeth,
		ethereum: ethereum,
57 58
		codec:    codec,
		coder:    codec.New(nil),
Bas van Kervel's avatar
Bas van Kervel committed
59 60 61 62 63
	}
}

// collection with supported methods
func (self *adminApi) Methods() []string {
64
	methods := make([]string, len(AdminMapping))
Bas van Kervel's avatar
Bas van Kervel committed
65
	i := 0
66
	for k := range AdminMapping {
Bas van Kervel's avatar
Bas van Kervel committed
67 68 69 70 71 72 73 74
		methods[i] = k
		i++
	}
	return methods
}

// Execute given request
func (self *adminApi) Execute(req *shared.Request) (interface{}, error) {
75
	if callback, ok := AdminMapping[req.Method]; ok {
Bas van Kervel's avatar
Bas van Kervel committed
76 77 78 79 80 81 82
		return callback(self, req)
	}

	return nil, &shared.NotImplementedError{req.Method}
}

func (self *adminApi) Name() string {
83
	return shared.AdminApiName
Bas van Kervel's avatar
Bas van Kervel committed
84 85
}

86 87 88 89
func (self *adminApi) ApiVersion() string {
	return AdminApiversion
}

Bas van Kervel's avatar
Bas van Kervel committed
90 91
func (self *adminApi) AddPeer(req *shared.Request) (interface{}, error) {
	args := new(AddPeerArgs)
92
	if err := self.coder.Decode(req.Params, &args); err != nil {
Bas van Kervel's avatar
Bas van Kervel committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
		return nil, shared.NewDecodeParamError(err.Error())
	}

	err := self.ethereum.AddPeer(args.Url)
	if err == nil {
		return true, nil
	}
	return false, err
}

func (self *adminApi) Peers(req *shared.Request) (interface{}, error) {
	return self.ethereum.PeersInfo(), nil
}

func (self *adminApi) NodeInfo(req *shared.Request) (interface{}, error) {
	return self.ethereum.NodeInfo(), nil
}

111 112 113 114
func (self *adminApi) DataDir(req *shared.Request) (interface{}, error) {
	return self.ethereum.DataDir, nil
}

Bas van Kervel's avatar
Bas van Kervel committed
115 116 117 118 119 120 121 122 123 124 125
func hasAllBlocks(chain *core.ChainManager, bs []*types.Block) bool {
	for _, b := range bs {
		if !chain.HasBlock(b.Hash()) {
			return false
		}
	}
	return true
}

func (self *adminApi) ImportChain(req *shared.Request) (interface{}, error) {
	args := new(ImportExportChainArgs)
126
	if err := self.coder.Decode(req.Params, &args); err != nil {
Bas van Kervel's avatar
Bas van Kervel committed
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
		return nil, shared.NewDecodeParamError(err.Error())
	}

	fh, err := os.Open(args.Filename)
	if err != nil {
		return false, err
	}
	defer fh.Close()
	stream := rlp.NewStream(fh, 0)

	// Run actual the import.
	blocks := make(types.Blocks, importBatchSize)
	n := 0
	for batch := 0; ; batch++ {

		i := 0
		for ; i < importBatchSize; i++ {
			var b types.Block
			if err := stream.Decode(&b); err == io.EOF {
				break
			} else if err != nil {
				return false, fmt.Errorf("at block %d: %v", n, err)
			}
			blocks[i] = &b
			n++
		}
		if i == 0 {
			break
		}
		// Import the batch.
		if hasAllBlocks(self.ethereum.ChainManager(), blocks[:i]) {
			continue
		}
		if _, err := self.ethereum.ChainManager().InsertChain(blocks[:i]); err != nil {
			return false, fmt.Errorf("invalid block %d: %v", n, err)
		}
	}
	return true, nil
}

func (self *adminApi) ExportChain(req *shared.Request) (interface{}, error) {
	args := new(ImportExportChainArgs)
169
	if err := self.coder.Decode(req.Params, &args); err != nil {
Bas van Kervel's avatar
Bas van Kervel committed
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
		return nil, shared.NewDecodeParamError(err.Error())
	}

	fh, err := os.OpenFile(args.Filename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
	if err != nil {
		return false, err
	}
	defer fh.Close()
	if err := self.ethereum.ChainManager().Export(fh); err != nil {
		return false, err
	}

	return true, nil
}

func (self *adminApi) Verbosity(req *shared.Request) (interface{}, error) {
	args := new(VerbosityArgs)
187
	if err := self.coder.Decode(req.Params, &args); err != nil {
Bas van Kervel's avatar
Bas van Kervel committed
188 189 190 191 192 193 194
		return nil, shared.NewDecodeParamError(err.Error())
	}

	glog.SetV(args.Level)
	return true, nil
}

195
func (self *adminApi) ChainSyncStatus(req *shared.Request) (interface{}, error) {
196 197 198
	pending, cached, importing, estimate := self.ethereum.Downloader().Stats()

	return map[string]interface{}{
Bas van Kervel's avatar
Bas van Kervel committed
199
		"blocksAvailable":        pending,
200
		"blocksWaitingForImport": cached,
Bas van Kervel's avatar
Bas van Kervel committed
201 202
		"importing":              importing,
		"estimate":               estimate.String(),
203
	}, nil
Bas van Kervel's avatar
Bas van Kervel committed
204 205 206 207
}

func (self *adminApi) SetSolc(req *shared.Request) (interface{}, error) {
	args := new(SetSolcArgs)
208
	if err := self.coder.Decode(req.Params, &args); err != nil {
Bas van Kervel's avatar
Bas van Kervel committed
209 210 211 212 213 214 215 216 217
		return nil, shared.NewDecodeParamError(err.Error())
	}

	solc, err := self.xeth.SetSolc(args.Path)
	if err != nil {
		return nil, err
	}
	return solc.Info(), nil
}
218 219 220 221 222 223 224 225 226 227 228 229 230

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,
	}

231 232 233
	apis, err := ParseApiString(args.Apis, self.codec, self.xeth, self.ethereum)
	if err != nil {
		return false, err
234 235
	}

236
	err = comms.StartHttp(cfg, self.codec, Merge(apis...))
237 238 239 240 241 242 243 244 245 246
	if err == nil {
		return true, nil
	}
	return false, err
}

func (self *adminApi) StopRPC(req *shared.Request) (interface{}, error) {
	comms.StopHttp()
	return true, nil
}