diff --git a/rpc/api/db.go b/rpc/api/db.go index 0eddc410ed..7ca049e801 100644 --- a/rpc/api/db.go +++ b/rpc/api/db.go @@ -97,7 +97,10 @@ func (self *dbApi) GetString(req *shared.Request) (interface{}, error) { } ret, err := self.xeth.DbGet([]byte(args.Database + args.Key)) - return string(ret), err + if err != nil { + return nil, err + } + return string(ret), nil } func (self *dbApi) PutString(req *shared.Request) (interface{}, error) { diff --git a/rpc/api/debug.go b/rpc/api/debug.go index d325b17209..b3e96fa26f 100644 --- a/rpc/api/debug.go +++ b/rpc/api/debug.go @@ -138,7 +138,10 @@ func (self *debugApi) GetBlockRlp(req *shared.Request) (interface{}, error) { return nil, fmt.Errorf("block #%d not found", args.BlockNumber) } encoded, err := rlp.EncodeToBytes(block) - return fmt.Sprintf("%x", encoded), err + if err != nil { + return nil, err + } + return fmt.Sprintf("%x", encoded), nil } func (self *debugApi) SetHead(req *shared.Request) (interface{}, error) { diff --git a/rpc/api/personal.go b/rpc/api/personal.go index 1b0dea330a..fd78a757d5 100644 --- a/rpc/api/personal.go +++ b/rpc/api/personal.go @@ -101,7 +101,10 @@ func (self *personalApi) NewAccount(req *shared.Request) (interface{}, error) { am := self.ethereum.AccountManager() acc, err := am.NewAccount(args.Passphrase) - return acc.Address.Hex(), err + if err != nil { + return nil, err + } + return acc.Address.Hex(), nil } func (self *personalApi) UnlockAccount(req *shared.Request) (interface{}, error) { diff --git a/rpc/genapi.go b/rpc/genapi.go index 7656e305ed..fa1366d1da 100644 --- a/rpc/genapi.go +++ b/rpc/genapi.go @@ -42,8 +42,13 @@ type Admin struct { xeth *Xeth } -func (self *Admin) AddPeer(url string) (interface{}, error) { - return self.xeth.Call("admin_addPeer", []interface{}{url}) +func (self *Admin) AddPeer(url string) (result bool, failure error) { + res, err := self.xeth.Call("admin_addPeer", []interface{}{url}) + if err != nil { + failure = err + return + } + return res.(bool), nil } func (self *Admin) ChainSyncStatus() (interface{}, error) { return self.xeth.Call("admin_chainSyncStatus", nil) @@ -51,20 +56,40 @@ func (self *Admin) ChainSyncStatus() (interface{}, error) { func (self *Admin) Datadir() (interface{}, error) { return self.xeth.Call("admin_datadir", nil) } -func (self *Admin) EnableUserAgent() (interface{}, error) { - return self.xeth.Call("admin_enableUserAgent", nil) +func (self *Admin) EnableUserAgent() (result bool, failure error) { + res, err := self.xeth.Call("admin_enableUserAgent", nil) + if err != nil { + failure = err + return + } + return res.(bool), nil } -func (self *Admin) ExportChain() (interface{}, error) { - return self.xeth.Call("admin_exportChain", nil) +func (self *Admin) ExportChain() (result bool, failure error) { + res, err := self.xeth.Call("admin_exportChain", nil) + if err != nil { + failure = err + return + } + return res.(bool), nil } func (self *Admin) GetContractInfo(contract string) (interface{}, error) { return self.xeth.Call("admin_getContractInfo", []interface{}{contract}) } -func (self *Admin) HttpGet(uri string, path string) (interface{}, error) { - return self.xeth.Call("admin_httpGet", []interface{}{uri, path}) +func (self *Admin) HttpGet(uri string, path string) (result string, failure error) { + res, err := self.xeth.Call("admin_httpGet", []interface{}{uri, path}) + if err != nil { + failure = err + return + } + return res.(string), nil } -func (self *Admin) ImportChain() (interface{}, error) { - return self.xeth.Call("admin_importChain", nil) +func (self *Admin) ImportChain() (result bool, failure error) { + res, err := self.xeth.Call("admin_importChain", nil) + if err != nil { + failure = err + return + } + return res.(bool), nil } func (self *Admin) NodeInfo() (interface{}, error) { return self.xeth.Call("admin_nodeInfo", nil) @@ -72,11 +97,21 @@ func (self *Admin) NodeInfo() (interface{}, error) { func (self *Admin) Peers() (interface{}, error) { return self.xeth.Call("admin_peers", nil) } -func (self *Admin) Register(sender string, address string, contentHashHex string) (interface{}, error) { - return self.xeth.Call("admin_register", []interface{}{sender, address, contentHashHex}) +func (self *Admin) Register(sender string, address string, contentHashHex string) (result bool, failure error) { + res, err := self.xeth.Call("admin_register", []interface{}{sender, address, contentHashHex}) + if err != nil { + failure = err + return + } + return res.(bool), nil } -func (self *Admin) RegisterUrl(sender string, contentHash string, url string) (interface{}, error) { - return self.xeth.Call("admin_registerUrl", []interface{}{sender, contentHash, url}) +func (self *Admin) RegisterUrl(sender string, contentHash string, url string) (result bool, failure error) { + res, err := self.xeth.Call("admin_registerUrl", []interface{}{sender, contentHash, url}) + if err != nil { + failure = err + return + } + return res.(bool), nil } func (self *Admin) SaveInfo(contractInfo compiler.ContractInfo, filename string) (interface{}, error) { return self.xeth.Call("admin_saveInfo", []interface{}{contractInfo, filename}) @@ -99,20 +134,45 @@ func (self *Admin) Sleep(s int) (interface{}, error) { func (self *Admin) SleepBlocks(n int64, timeout int64) (interface{}, error) { return self.xeth.Call("admin_sleepBlocks", []interface{}{n, timeout}) } -func (self *Admin) StartNatSpec() (interface{}, error) { - return self.xeth.Call("admin_startNatSpec", nil) +func (self *Admin) StartNatSpec() (result bool, failure error) { + res, err := self.xeth.Call("admin_startNatSpec", nil) + if err != nil { + failure = err + return + } + return res.(bool), nil } -func (self *Admin) StartRPC(listenAddress string, listenPort uint, corsDomain string, apis string) (interface{}, error) { - return self.xeth.Call("admin_startRPC", []interface{}{listenAddress, listenPort, corsDomain, apis}) +func (self *Admin) StartRPC(listenAddress string, listenPort uint, corsDomain string, apis string) (result bool, failure error) { + res, err := self.xeth.Call("admin_startRPC", []interface{}{listenAddress, listenPort, corsDomain, apis}) + if err != nil { + failure = err + return + } + return res.(bool), nil } -func (self *Admin) StopNatSpec() (interface{}, error) { - return self.xeth.Call("admin_stopNatSpec", nil) +func (self *Admin) StopNatSpec() (result bool, failure error) { + res, err := self.xeth.Call("admin_stopNatSpec", nil) + if err != nil { + failure = err + return + } + return res.(bool), nil } -func (self *Admin) StopRPC() (interface{}, error) { - return self.xeth.Call("admin_stopRPC", nil) +func (self *Admin) StopRPC() (result bool, failure error) { + res, err := self.xeth.Call("admin_stopRPC", nil) + if err != nil { + failure = err + return + } + return res.(bool), nil } -func (self *Admin) Verbosity(level int) (interface{}, error) { - return self.xeth.Call("admin_verbosity", []interface{}{level}) +func (self *Admin) Verbosity(level int) (result bool, failure error) { + res, err := self.xeth.Call("admin_verbosity", []interface{}{level}) + if err != nil { + failure = err + return + } + return res.(bool), nil } type Db struct { @@ -122,8 +182,13 @@ type Db struct { func (self *Db) GetHex() (interface{}, error) { return self.xeth.Call("db_getHex", nil) } -func (self *Db) GetString() (interface{}, error) { - return self.xeth.Call("db_getString", nil) +func (self *Db) GetString() (result string, failure error) { + res, err := self.xeth.Call("db_getString", nil) + if err != nil { + failure = err + return + } + return res.(string), nil } func (self *Db) PutHex() (interface{}, error) { return self.xeth.Call("db_putHex", nil) @@ -148,8 +213,13 @@ func (self *Debug) Metrics(raw bool) (interface{}, error) { func (self *Debug) PrintBlock() (interface{}, error) { return self.xeth.Call("debug_printBlock", nil) } -func (self *Debug) ProcessBlock() (interface{}, error) { - return self.xeth.Call("debug_processBlock", nil) +func (self *Debug) ProcessBlock() (result bool, failure error) { + res, err := self.xeth.Call("debug_processBlock", nil) + if err != nil { + failure = err + return + } + return res.(bool), nil } func (self *Debug) SeedHash() (interface{}, error) { return self.xeth.Call("debug_seedHash", nil) @@ -165,8 +235,13 @@ type Eth struct { func (self *Eth) Accounts() (interface{}, error) { return self.xeth.Call("eth_accounts", nil) } -func (self *Eth) BlockNumber() (interface{}, error) { - return self.xeth.Call("eth_blockNumber", nil) +func (self *Eth) BlockNumber() (result int64, failure error) { + res, err := self.xeth.Call("eth_blockNumber", nil) + if err != nil { + failure = err + return + } + return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil } func (self *Eth) Call(from string, to string, value *big.Int, gas *big.Int, gasPrice *big.Int, data string, blockNumber int64) (interface{}, error) { return self.xeth.Call("eth_call", []interface{}{from, to, value, gas, gasPrice, data, blockNumber}) @@ -177,14 +252,24 @@ func (self *Eth) Coinbase() (interface{}, error) { func (self *Eth) CompileSolidity() (interface{}, error) { return self.xeth.Call("eth_compileSolidity", nil) } -func (self *Eth) EstimateGas() (interface{}, error) { - return self.xeth.Call("eth_estimateGas", nil) +func (self *Eth) EstimateGas() (result int64, failure error) { + res, err := self.xeth.Call("eth_estimateGas", nil) + if err != nil { + failure = err + return + } + return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil } func (self *Eth) Flush() (interface{}, error) { return self.xeth.Call("eth_flush", nil) } -func (self *Eth) GasPrice(price string) (interface{}, error) { - return self.xeth.Call("eth_gasPrice", []interface{}{price}) +func (self *Eth) GasPrice(price string) (result int64, failure error) { + res, err := self.xeth.Call("eth_gasPrice", []interface{}{price}) + if err != nil { + failure = err + return + } + return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil } func (self *Eth) GetBalance(address string, blockNumber int64) (interface{}, error) { return self.xeth.Call("eth_getBalance", []interface{}{address, blockNumber}) @@ -234,8 +319,13 @@ func (self *Eth) GetTransactionByBlockNumberAndIndex() (interface{}, error) { func (self *Eth) GetTransactionByHash() (interface{}, error) { return self.xeth.Call("eth_getTransactionByHash", nil) } -func (self *Eth) GetTransactionCount() (interface{}, error) { - return self.xeth.Call("eth_getTransactionCount", nil) +func (self *Eth) GetTransactionCount() (result int64, failure error) { + res, err := self.xeth.Call("eth_getTransactionCount", nil) + if err != nil { + failure = err + return + } + return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil } func (self *Eth) GetTransactionReceipt() (interface{}, error) { return self.xeth.Call("eth_getTransactionReceipt", nil) @@ -246,29 +336,59 @@ func (self *Eth) GetUncleByBlockHashAndIndex() (interface{}, error) { func (self *Eth) GetUncleByBlockNumberAndIndex() (interface{}, error) { return self.xeth.Call("eth_getUncleByBlockNumberAndIndex", nil) } -func (self *Eth) GetUncleCountByBlockHash() (interface{}, error) { - return self.xeth.Call("eth_getUncleCountByBlockHash", nil) +func (self *Eth) GetUncleCountByBlockHash() (result int64, failure error) { + res, err := self.xeth.Call("eth_getUncleCountByBlockHash", nil) + if err != nil { + failure = err + return + } + return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil } -func (self *Eth) GetUncleCountByBlockNumber() (interface{}, error) { - return self.xeth.Call("eth_getUncleCountByBlockNumber", nil) +func (self *Eth) GetUncleCountByBlockNumber() (result int64, failure error) { + res, err := self.xeth.Call("eth_getUncleCountByBlockNumber", nil) + if err != nil { + failure = err + return + } + return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil } func (self *Eth) GetWork() (interface{}, error) { return self.xeth.Call("eth_getWork", nil) } -func (self *Eth) Hashrate() (interface{}, error) { - return self.xeth.Call("eth_hashrate", nil) +func (self *Eth) Hashrate() (result int64, failure error) { + res, err := self.xeth.Call("eth_hashrate", nil) + if err != nil { + failure = err + return + } + return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil } func (self *Eth) Mining() (interface{}, error) { return self.xeth.Call("eth_mining", nil) } -func (self *Eth) NewBlockFilter() (interface{}, error) { - return self.xeth.Call("eth_newBlockFilter", nil) +func (self *Eth) NewBlockFilter() (result int64, failure error) { + res, err := self.xeth.Call("eth_newBlockFilter", nil) + if err != nil { + failure = err + return + } + return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil } -func (self *Eth) NewFilter() (interface{}, error) { - return self.xeth.Call("eth_newFilter", nil) +func (self *Eth) NewFilter() (result int64, failure error) { + res, err := self.xeth.Call("eth_newFilter", nil) + if err != nil { + failure = err + return + } + return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil } -func (self *Eth) NewPendingTransactionFilter() (interface{}, error) { - return self.xeth.Call("eth_newPendingTransactionFilter", nil) +func (self *Eth) NewPendingTransactionFilter() (result int64, failure error) { + res, err := self.xeth.Call("eth_newPendingTransactionFilter", nil) + if err != nil { + failure = err + return + } + return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil } func (self *Eth) PendingTransactions() (interface{}, error) { return self.xeth.Call("eth_pendingTransactions", nil) @@ -288,8 +408,13 @@ func (self *Eth) Sign() (interface{}, error) { func (self *Eth) StorageAt(address string, blockNumber int64) (interface{}, error) { return self.xeth.Call("eth_storageAt", []interface{}{address, blockNumber}) } -func (self *Eth) SubmitHashrate() (interface{}, error) { - return self.xeth.Call("eth_submitHashrate", nil) +func (self *Eth) SubmitHashrate() (result bool, failure error) { + res, err := self.xeth.Call("eth_submitHashrate", nil) + if err != nil { + failure = err + return + } + return res.(bool), nil } func (self *Eth) SubmitWork(nonce uint64, header string, digest string) (interface{}, error) { return self.xeth.Call("eth_submitWork", []interface{}{nonce, header, digest}) @@ -308,29 +433,64 @@ type Miner struct { func (self *Miner) Hashrate() (interface{}, error) { return self.xeth.Call("miner_hashrate", nil) } -func (self *Miner) MakeDAG(blockNumber int64) (interface{}, error) { - return self.xeth.Call("miner_makeDAG", []interface{}{blockNumber}) +func (self *Miner) MakeDAG(blockNumber int64) (result bool, failure error) { + res, err := self.xeth.Call("miner_makeDAG", []interface{}{blockNumber}) + if err != nil { + failure = err + return + } + return res.(bool), nil } func (self *Miner) SetEtherbase(etherbase common.Address) (interface{}, error) { return self.xeth.Call("miner_setEtherbase", []interface{}{etherbase}) } -func (self *Miner) SetExtra(data string) (interface{}, error) { - return self.xeth.Call("miner_setExtra", []interface{}{data}) +func (self *Miner) SetExtra(data string) (result bool, failure error) { + res, err := self.xeth.Call("miner_setExtra", []interface{}{data}) + if err != nil { + failure = err + return + } + return res.(bool), nil } -func (self *Miner) SetGasPrice() (interface{}, error) { - return self.xeth.Call("miner_setGasPrice", nil) +func (self *Miner) SetGasPrice() (result bool, failure error) { + res, err := self.xeth.Call("miner_setGasPrice", nil) + if err != nil { + failure = err + return + } + return res.(bool), nil } -func (self *Miner) Start(threads int) (interface{}, error) { - return self.xeth.Call("miner_start", []interface{}{threads}) +func (self *Miner) Start(threads int) (result bool, failure error) { + res, err := self.xeth.Call("miner_start", []interface{}{threads}) + if err != nil { + failure = err + return + } + return res.(bool), nil } -func (self *Miner) StartAutoDAG() (interface{}, error) { - return self.xeth.Call("miner_startAutoDAG", nil) +func (self *Miner) StartAutoDAG() (result bool, failure error) { + res, err := self.xeth.Call("miner_startAutoDAG", nil) + if err != nil { + failure = err + return + } + return res.(bool), nil } -func (self *Miner) Stop() (interface{}, error) { - return self.xeth.Call("miner_stop", nil) +func (self *Miner) Stop() (result bool, failure error) { + res, err := self.xeth.Call("miner_stop", nil) + if err != nil { + failure = err + return + } + return res.(bool), nil } -func (self *Miner) StopAutoDAG() (interface{}, error) { - return self.xeth.Call("miner_stopAutoDAG", nil) +func (self *Miner) StopAutoDAG() (result bool, failure error) { + res, err := self.xeth.Call("miner_stopAutoDAG", nil) + if err != nil { + failure = err + return + } + return res.(bool), nil } type Net struct { @@ -340,8 +500,13 @@ type Net struct { func (self *Net) Listening() (interface{}, error) { return self.xeth.Call("net_listening", nil) } -func (self *Net) PeerCount() (interface{}, error) { - return self.xeth.Call("net_peerCount", nil) +func (self *Net) PeerCount() (result int64, failure error) { + res, err := self.xeth.Call("net_peerCount", nil) + if err != nil { + failure = err + return + } + return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil } func (self *Net) Version() (interface{}, error) { return self.xeth.Call("net_version", nil) @@ -374,14 +539,24 @@ func (self *Shh) GetMessages() (interface{}, error) { func (self *Shh) HasIdentity() (interface{}, error) { return self.xeth.Call("shh_hasIdentity", nil) } -func (self *Shh) NewFilter() (interface{}, error) { - return self.xeth.Call("shh_newFilter", nil) +func (self *Shh) NewFilter() (result int64, failure error) { + res, err := self.xeth.Call("shh_newFilter", nil) + if err != nil { + failure = err + return + } + return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil } func (self *Shh) NewIdentity() (interface{}, error) { return self.xeth.Call("shh_newIdentity", nil) } -func (self *Shh) Post() (interface{}, error) { - return self.xeth.Call("shh_post", nil) +func (self *Shh) Post() (result bool, failure error) { + res, err := self.xeth.Call("shh_post", nil) + if err != nil { + failure = err + return + } + return res.(bool), nil } func (self *Shh) UninstallFilter() (interface{}, error) { return self.xeth.Call("shh_uninstallFilter", nil) diff --git a/rpc/generator/generator.go b/rpc/generator/generator.go index aa43b6fdee..3e2831d384 100644 --- a/rpc/generator/generator.go +++ b/rpc/generator/generator.go @@ -30,6 +30,7 @@ import ( "os" "os/exec" "path/filepath" + "reflect" "sort" "strings" "unicode" @@ -47,8 +48,8 @@ type Package struct { type Endpoint struct { Method string Function string - Argument string Params []string + Return string } func main() { @@ -57,130 +58,24 @@ func main() { if err != nil { log.Fatalf("Failed to retrieve API package details: %v", err) } - types, values, err := declarations(api) + funs, types, values, err := declarations(api) if err != nil { log.Fatalf("Failed to collect API declarations: %v", err) } - // Create the collectors for the submodules - modules := []string{} - methods := make(map[string][]*Endpoint) - - // Iterate over all the API mappings, and locate the RPC function associations - for variable, val := range values { - if strings.HasSuffix(variable, "Mapping") { - // Iterate over all the exposed functionality and extract them - for _, mapping := range val.(*ast.CompositeLit).Elts { - // Fetch the name of the RPC function, and the associated argument definition - call := strings.Trim(mapping.(*ast.KeyValueExpr).Key.(*ast.BasicLit).Value, "\"") - args := mapping.(*ast.KeyValueExpr).Value.(*ast.SelectorExpr).Sel.Name + "Args" - - // Generate the submodule and function names - module := strings.Split(call, "_")[0] - module = string(unicode.ToUpper(rune(module[0]))) + module[1:] - function := strings.Split(call, "_")[1] - function = string(unicode.ToUpper(rune(function[0]))) + function[1:] - - // Generate the parameter list - params, paramList := []string{}, []string{} - if arg := types[args]; arg != nil { - for _, field := range arg.Type.(*ast.StructType).Fields.List { - variable := field.Names[0].String() - variable = string(unicode.ToLower(rune(variable[0]))) + variable[1:] - - kind := "" - switch t := field.Type.(type) { - case *ast.Ident: - kind = t.String() - case *ast.SelectorExpr: - kind = t.X.(*ast.Ident).String() + "." + t.Sel.String() - case *ast.StarExpr: - switch sub := t.X.(type) { - case *ast.Ident: - kind = "*" + sub.String() - case *ast.SelectorExpr: - kind = "*" + sub.X.(*ast.Ident).String() + "." + sub.Sel.String() - default: - log.Fatalf("Unknown pointer subtype: %v", sub) - } - default: - log.Fatalf("Unknown type: %v", t) - } - params = append(params, fmt.Sprintf("%s %s", variable, kind)) - paramList = append(paramList, variable) - } - } - // Insert the function to the submodule collection (alphabetically) - endpoint := &Endpoint{ - Method: call, - Function: fmt.Sprintf("%s(%s)", function, strings.Join(params, ",")), - Argument: args, - Params: paramList, - } - for i := 0; i < len(methods[module]); i++ { - if methods[module][i].Function > endpoint.Function { - methods[module] = append(methods[module], nil) - copy(methods[module][i+1:], methods[module][i:]) - methods[module][i] = endpoint - - endpoint = nil - break - } - } - if endpoint != nil { - methods[module] = append(methods[module], endpoint) - } - // Register the module if first occurance - if len(methods[module]) == 1 { - modules = append(modules, module) - } - } - } + // Gather all the deteced API endpoints + methods, err := endpoints(funs, types, values) + if err != nil { + log.Fatalf("Failed to gather API endpoints: %v", err) } - sort.Strings(modules) - - // Start generating the client API - client := "package rpc\n" - - // Generate the client struct with all its submodules - client += fmt.Sprintf("type GenApi struct {\n") - for _, module := range modules { - client += fmt.Sprintf("%s *%s\n", module, module) - } - client += fmt.Sprintf("}\n") - - // Generate the API constructor to create the individual submodules - client += fmt.Sprint("func NewGenApi(client comms.EthereumClient) *GenApi {\n") - client += fmt.Sprint("xeth := NewXeth(client)\n\n") - client += fmt.Sprint("return &GenApi{\n") - for _, module := range modules { - client += fmt.Sprintf("%s: &%s{xeth},\n", module, module) - } - client += fmt.Sprintf("}}\n") - - // Generate each of the client API calls - for _, module := range modules { - endpoints := methods[module] - - client += fmt.Sprintf("\ntype %s struct {\n xeth *Xeth\n}\n", module) - for _, endpoint := range endpoints { - client += fmt.Sprintf("func (self *%s) %s (interface{}, error) {\n", module, endpoint.Function) - if len(endpoint.Params) == 0 { - client += fmt.Sprintf("return self.xeth.Call(\"%s\", nil)\n", endpoint.Method) - } else { - client += fmt.Sprintf("return self.xeth.Call(\"%s\", []interface{}{%s})\n", endpoint.Method, strings.Join(endpoint.Params, ",")) - } - client += fmt.Sprintf("}\n") - } - } - // Format the final code and output - if blob, err := imports.Process("", []byte(client), nil); err != nil { + // Generate the client API and output if successfull + if code, err := generate(methods); err != nil { log.Fatalf("Failed to format output code: %v", err) - } else if err := ioutil.WriteFile("../genapi.go", blob, 0600); err != nil { + } else if err := ioutil.WriteFile("../genapi.go", code, 0600); err != nil { log.Fatalf("Failed to write output code: %v", err) } } -// Loads the details of the Go package. +// details loads the metadata of the Go package. func details(name string) (*Package, error) { // Create the command to retrieve the package infos cmd := exec.Command("go", "list", "-e", "-json", name) @@ -209,8 +104,9 @@ func details(name string) (*Package, error) { return info, nil } -// Iterates over a package contents and collects interesting declarations. -func declarations(pack *Package) (map[string]*ast.TypeSpec, map[string]ast.Expr, error) { +// declarations iterates over a package contents and collects type and value declarations. +func declarations(pack *Package) (map[string]*ast.BlockStmt, map[string]*ast.TypeSpec, map[string]ast.Expr, error) { + funs := make(map[string]*ast.BlockStmt) types := make(map[string]*ast.TypeSpec) values := make(map[string]ast.Expr) @@ -219,11 +115,17 @@ func declarations(pack *Package) (map[string]*ast.TypeSpec, map[string]ast.Expr, fileSet := token.NewFileSet() tree, err := parser.ParseFile(fileSet, filepath.Join(pack.Dir, path), nil, parser.ParseComments) if err != nil { - return nil, nil, err + return nil, nil, nil, err } // Collect all top level declarations for _, decl := range tree.Decls { switch decl := decl.(type) { + case *ast.FuncDecl: + if decl.Recv != nil { + recv := decl.Recv.List[0].Type.(*ast.StarExpr).X.(*ast.Ident).String() + call := decl.Name.String() + funs[recv+"."+call] = decl.Body + } case *ast.GenDecl: for _, spec := range decl.Specs { switch spec := spec.(type) { @@ -238,5 +140,196 @@ func declarations(pack *Package) (map[string]*ast.TypeSpec, map[string]ast.Expr, } } } - return types, values, nil + return funs, types, values, nil +} + +// returns recursively iterates over a block statement and extracts all the +// return statements that contain nil errors. +func returns(block *ast.BlockStmt) []*ast.ReturnStmt { + results := []*ast.ReturnStmt{} + + for _, stmt := range block.List { + switch stmt := stmt.(type) { + case *ast.ReturnStmt: + if ident, ok := stmt.Results[1].(*ast.Ident); ok { + if ident.String() == "nil" { + results = append(results, stmt) + } + } + case *ast.IfStmt: + results = append(results, returns(stmt.Body)...) + } + } + return results +} + +// endpoints collects the detected RPC API method endpoints. +func endpoints(funs map[string]*ast.BlockStmt, types map[string]*ast.TypeSpec, values map[string]ast.Expr) (map[string][]*Endpoint, error) { + methods := make(map[string][]*Endpoint) + + // Iterate over all the API mappings, and locate the RPC function associations + for variable, val := range values { + if strings.HasSuffix(variable, "Mapping") { + // Iterate over all the exposed functionality and extract them + for _, mapping := range val.(*ast.CompositeLit).Elts { + // Fetch the name of the RPC function, and the associated argument definition + call := strings.Trim(mapping.(*ast.KeyValueExpr).Key.(*ast.BasicLit).Value, "\"") + impl := mapping.(*ast.KeyValueExpr).Value.(*ast.SelectorExpr) + args := mapping.(*ast.KeyValueExpr).Value.(*ast.SelectorExpr).Sel.Name + "Args" + + // Generate the submodule and function names + module := strings.Split(call, "_")[0] + module = string(unicode.ToUpper(rune(module[0]))) + module[1:] + function := strings.Split(call, "_")[1] + function = string(unicode.ToUpper(rune(function[0]))) + function[1:] + + // Generate the parameter list + params, paramList := []string{}, []string{} + if arg := types[args]; arg != nil { + for _, field := range arg.Type.(*ast.StructType).Fields.List { + variable := field.Names[0].String() + variable = string(unicode.ToLower(rune(variable[0]))) + variable[1:] + + kind := "" + switch t := field.Type.(type) { + case *ast.Ident: + kind = t.String() + case *ast.SelectorExpr: + kind = t.X.(*ast.Ident).String() + "." + t.Sel.String() + case *ast.StarExpr: + switch sub := t.X.(type) { + case *ast.Ident: + kind = "*" + sub.String() + case *ast.SelectorExpr: + kind = "*" + sub.X.(*ast.Ident).String() + "." + sub.Sel.String() + default: + return nil, fmt.Errorf("Unknown pointer subtype: %v", sub) + } + default: + return nil, fmt.Errorf("Unknown type: %v", t) + } + params = append(params, fmt.Sprintf("%s %s", variable, kind)) + paramList = append(paramList, variable) + } + } + // Try to detect and generate the return type + owner := impl.X.(*ast.ParenExpr).X.(*ast.StarExpr).X.(*ast.Ident).String() + method := mapping.(*ast.KeyValueExpr).Value.(*ast.SelectorExpr).Sel.String() + rets := returns(funs[owner+"."+method]) + + result := "interface{}" + for _, ret := range rets { + res := ret.Results[0] + switch res := res.(type) { + case *ast.Ident: + if res.String() == "nil" { + break + } else if res.String() == "true" || res.String() == "false" { + result = "bool" + } else { + fmt.Println(owner, method, res, "unknown ident") + } + case *ast.CallExpr: + if ident, ok := res.Fun.(*ast.Ident); ok { + if ident.String() == "string" { + result = "string" + } else if ident.String() == "newHexNum" { + result = "int64" + } else { + fmt.Println(owner, method, res, "unknown ident funcion") + } + } else { + fmt.Println(owner, method, res, "call", res.Fun, reflect.TypeOf(res.Fun)) + } + default: + fmt.Println(owner, method, res, reflect.TypeOf(res)) + } + } + // Insert the function to the submodule collection (alphabetically) + methods[module] = append(methods[module], &Endpoint{ + Method: call, + Function: fmt.Sprintf("%s(%s)", function, strings.Join(params, ",")), + Params: paramList, + Return: result, + }) + } + } + } + return methods, nil +} + +// generate creates the client code belonging to a set of API method endpoints. +func generate(methods map[string][]*Endpoint) ([]byte, error) { + // Create a sorted list of API modules and endpoints to generate + modules := make([]string, 0, len(methods)) + for module, _ := range methods { + modules = append(modules, module) + } + sort.Strings(modules) + + for _, module := range modules { + for i := 0; i < len(methods[module]); i++ { + for j := i + 1; j < len(methods[module]); j++ { + if methods[module][i].Function > methods[module][j].Function { + methods[module][i], methods[module][j] = methods[module][j], methods[module][i] + } + } + } + } + // Start generating the client API + client := "package rpc\n" + + // Generate the client struct with all its submodules + client += fmt.Sprintf("type GenApi struct {\n") + for _, module := range modules { + client += fmt.Sprintf("%s *%s\n", module, module) + } + client += fmt.Sprintf("}\n") + + // Generate the API constructor to create the individual submodules + client += fmt.Sprint("func NewGenApi(client comms.EthereumClient) *GenApi {\n") + client += fmt.Sprint("xeth := NewXeth(client)\n\n") + client += fmt.Sprint("return &GenApi{\n") + for _, module := range modules { + client += fmt.Sprintf("%s: &%s{xeth},\n", module, module) + } + client += fmt.Sprintf("}}\n") + + // Generate each of the client API calls + for _, module := range modules { + endpoints := methods[module] + + client += fmt.Sprintf("\ntype %s struct {\n xeth *Xeth\n}\n", module) + for _, endpoint := range endpoints { + // Generate the header (only add variables if conversions are required) + if endpoint.Return == "interface{}" { + client += fmt.Sprintf("func (self *%s) %s (interface{}, error) {\n", module, endpoint.Function) + } else { + client += fmt.Sprintf("func (self *%s) %s (result %s, failure error) {\n", module, endpoint.Function, endpoint.Return) + } + // Generate the actual function invocation (straight return if no return type is known) + invocation := fmt.Sprintf("self.xeth.Call(\"%s\", nil)", endpoint.Method) + if len(endpoint.Params) > 0 { + invocation = fmt.Sprintf("self.xeth.Call(\"%s\", []interface{}{%s})", endpoint.Method, strings.Join(endpoint.Params, ",")) + } + if endpoint.Return == "interface{}" { + client += fmt.Sprintf("return %s\n", invocation) + } else { + client += fmt.Sprintf("res, err := %s\n", invocation) + } + // If conversions are needed, check for errors and post process + if endpoint.Return != "interface{}" { + client += fmt.Sprintf("if err != nil { failure = err; return; }\n") + + if endpoint.Return == "int64" { + client += fmt.Sprintf("return new(big.Int).SetBytes(common.FromHex(res.(string))).Int64(), nil\n") + } else { + client += fmt.Sprintf("return res.(%s), nil\n", endpoint.Return) + } + } + client += fmt.Sprintf("}\n") + } + } + // Format the final code and return + return imports.Process("", []byte(client), nil) }