github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/cmd/geth/apicmd.go (about)

     1  // Copyright 2017 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // go-ethereum is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  package main
    17  
    18  import (
    19  	"encoding/json"
    20  	"errors"
    21  	"fmt"
    22  	"math/rand"
    23  	"strconv"
    24  	"strings"
    25  
    26  	"gopkg.in/urfave/cli.v1"
    27  
    28  	"os"
    29  
    30  	"github.com/ethereumproject/go-ethereum/node"
    31  	"github.com/ethereumproject/go-ethereum/rpc"
    32  )
    33  
    34  var (
    35  	apiCommand = cli.Command{
    36  		Action: execAPI,
    37  		Name:   "api",
    38  		Usage:  "Run any API command",
    39  		Description: `
    40  	The api command allows you to communicate (via IPC) with a running geth instance
    41  	and run any RPC API method.
    42  
    43  	Each parameter should be passed as JSON representation:
    44  	- no quotations for numbers or booleans,
    45  	- strings must be correctly quoted, like '"some value"' (quotes must be
    46  	  included in string passed to application),
    47  	- complex objects could be passed as JSON string.
    48  
    49  	Examples:
    50  
    51  		$ geth api eth getBlockByNumber 123 true
    52  		$ geth eth getBlockByNumber '"latest"' true
    53  		$ geth --chain morden api eth sendTransaction '{"from": "0x396599f365093186742c17aab158bf515e978bc7", "gas": "0x5208", "gasPrice": "0x02540be400", "to": "0xa02cee0fc1d3fb4dde86b79fe93e4140671fd949"}'
    54  
    55  	Output will be written to stderr in JSON format.
    56  		`,
    57  	}
    58  )
    59  
    60  func execAPI(ctx *cli.Context) error {
    61  	client, err := getClient(ctx)
    62  	if err != nil {
    63  		return err
    64  	}
    65  
    66  	if err := validateArguments(ctx, client); err != nil {
    67  		return err
    68  	}
    69  
    70  	result, err := callRPC(ctx, client)
    71  	if err != nil {
    72  		return err
    73  	}
    74  	return prettyPrint(result)
    75  }
    76  
    77  func getClient(ctx *cli.Context) (rpc.Client, error) {
    78  	chainDir := MustMakeChainDataDir(ctx)
    79  	var uri = "ipc:" + node.DefaultIPCEndpoint(chainDir)
    80  	return rpc.NewClient(uri)
    81  }
    82  
    83  func validateArguments(ctx *cli.Context, client rpc.Client) error {
    84  	if len(ctx.Args()) < 2 {
    85  		return fmt.Errorf("api command requires at least 2 arguments (module and method), %d provided",
    86  			len(ctx.Args()))
    87  	}
    88  	modules, err := client.SupportedModules()
    89  	if err != nil {
    90  		return err
    91  	}
    92  
    93  	module := ctx.Args()[0]
    94  	if _, ok := modules[module]; !ok {
    95  		return fmt.Errorf("unknown API module: %s", module)
    96  	}
    97  
    98  	return nil
    99  }
   100  
   101  func callRPC(ctx *cli.Context, client rpc.Client) (interface{}, error) {
   102  	var (
   103  		module = ctx.Args()[0]
   104  		method = ctx.Args()[1]
   105  		args   = ctx.Args()[2:]
   106  	)
   107  	req := rpc.JSONRequest{
   108  		Id:      json.RawMessage(strconv.Itoa(rand.Int())),
   109  		Method:  module + "_" + method,
   110  		Version: "2.0",
   111  		Payload: json.RawMessage("[" + strings.Join(args, ",") + "]"),
   112  	}
   113  
   114  	if err := client.Send(req); err != nil {
   115  		return nil, err
   116  	}
   117  
   118  	var res rpc.JSONResponse
   119  	if err := client.Recv(&res); err != nil {
   120  		return nil, err
   121  	}
   122  	if res.Error != nil {
   123  		return nil, fmt.Errorf("error in %s_%s: %s (code: %d)",
   124  			module, method, res.Error.Message, res.Error.Code)
   125  	}
   126  	if res.Result != nil {
   127  		return res.Result, nil
   128  	}
   129  
   130  	return nil, errors.New("no API response")
   131  }
   132  
   133  func prettyPrint(result interface{}) error {
   134  	jsonBytes, err := json.MarshalIndent(result, "", "  ")
   135  	if err != nil {
   136  		return err
   137  	}
   138  	os.Stderr.Write(jsonBytes)
   139  	return nil
   140  }