diff --git a/cmd/console/js.go b/cmd/console/js.go new file mode 100644 index 0000000000..20052ed2df --- /dev/null +++ b/cmd/console/js.go @@ -0,0 +1,454 @@ +// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301 USA + +package main + +import ( + "bufio" + "fmt" + "math/big" + "os" + "os/signal" + "path/filepath" + "strings" + + "encoding/json" + + "sort" + + "github.com/codegangsta/cli" + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common/docserver" + re "github.com/ethereum/go-ethereum/jsre" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/rpc/api" + "github.com/ethereum/go-ethereum/rpc/codec" + "github.com/ethereum/go-ethereum/rpc/comms" + "github.com/ethereum/go-ethereum/rpc/shared" + "github.com/peterh/liner" + "github.com/robertkrimen/otto" +) + +type prompter interface { + AppendHistory(string) + Prompt(p string) (string, error) + PasswordPrompt(p string) (string, error) +} + +type dumbterm struct{ r *bufio.Reader } + +func (r dumbterm) Prompt(p string) (string, error) { + fmt.Print(p) + line, err := r.r.ReadString('\n') + return strings.TrimSuffix(line, "\n"), err +} + +func (r dumbterm) PasswordPrompt(p string) (string, error) { + fmt.Println("!! Unsupported terminal, password will echo.") + fmt.Print(p) + input, err := bufio.NewReader(os.Stdin).ReadString('\n') + fmt.Println() + return input, err +} + +func (r dumbterm) AppendHistory(string) {} + +type jsre struct { + re *re.JSRE + wait chan *big.Int + ps1 string + atexit func() + datadir string + prompter +} + +var ( + loadedModulesMethods map[string][]string +) + +func loadAutoCompletion(js *jsre, ipcpath string) { + modules, err := js.suportedApis(ipcpath) + if err != nil { + utils.Fatalf("Unable to determine supported modules - %v", err) + } + + loadedModulesMethods = make(map[string][]string) + for module, _ := range modules { + loadedModulesMethods[module] = api.AutoCompletion[module] + } +} + +func keywordCompleter(line string) []string { + results := make([]string, 0) + + if strings.Contains(line, ".") { + elements := strings.Split(line, ".") + if len(elements) == 2 { + module := elements[0] + partialMethod := elements[1] + if methods, found := loadedModulesMethods[module]; found { + for _, method := range methods { + if strings.HasPrefix(method, partialMethod) { // e.g. debug.se + results = append(results, module+"."+method) + } + } + } + } + } else { + for module, methods := range loadedModulesMethods { + if line == module { // user typed in full module name, show all methods + for _, method := range methods { + results = append(results, module+"."+method) + } + } else if strings.HasPrefix(module, line) { // partial method name, e.g. admi + results = append(results, module) + } + } + } + return results +} + +func apiWordCompleter(line string, pos int) (head string, completions []string, tail string) { + if len(line) == 0 { + return "", nil, "" + } + + i := 0 + for i = pos - 1; i > 0; i-- { + if line[i] == '.' || (line[i] >= 'a' && line[i] <= 'z') || (line[i] >= 'A' && line[i] <= 'Z') { + continue + } + if i >= 3 && line[i] == '3' && line[i-3] == 'w' && line[i-2] == 'e' && line[i-1] == 'b' { + continue + } + i += 1 + break + } + + begin := line[:i] + keyword := line[i:pos] + end := line[pos:] + + completionWords := keywordCompleter(keyword) + return begin, completionWords, end +} + +func newJSRE(libPath, ipcpath string) *jsre { + js := &jsre{ps1: "> "} + js.wait = make(chan *big.Int) + + // update state in separare forever blocks + js.re = re.New(libPath) + js.apiBindings(ipcpath) + + if !liner.TerminalSupported() { + js.prompter = dumbterm{bufio.NewReader(os.Stdin)} + } else { + lr := liner.NewLiner() + js.withHistory(func(hist *os.File) { lr.ReadHistory(hist) }) + lr.SetCtrlCAborts(true) + loadAutoCompletion(js, ipcpath) + lr.SetWordCompleter(apiWordCompleter) + lr.SetTabCompletionStyle(liner.TabPrints) + js.prompter = lr + js.atexit = func() { + js.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) }) + lr.Close() + close(js.wait) + } + } + return js +} + +func (js *jsre) apiBindings(ipcpath string) { + ethApi := rpc.NewEthereumApi(nil) + jeth := rpc.NewJeth(ethApi, js.re, ipcpath) + + js.re.Set("jeth", struct{}{}) + t, _ := js.re.Get("jeth") + jethObj := t.Object() + jethObj.Set("send", jeth.SendIpc) + jethObj.Set("sendAsync", jeth.SendIpc) + + err := js.re.Compile("bignumber.js", re.BigNumber_JS) + if err != nil { + utils.Fatalf("Error loading bignumber.js: %v", err) + } + + err = js.re.Compile("ethereum.js", re.Web3_JS) + if err != nil { + utils.Fatalf("Error loading web3.js: %v", err) + } + + _, err = js.re.Eval("var web3 = require('web3');") + if err != nil { + utils.Fatalf("Error requiring web3: %v", err) + } + + _, err = js.re.Eval("web3.setProvider(jeth)") + if err != nil { + utils.Fatalf("Error setting web3 provider: %v", err) + } + + apis, err := js.suportedApis(ipcpath) + if err != nil { + utils.Fatalf("Unable to determine supported api's: %v", err) + } + + // load only supported API's in javascript runtime + shortcuts := "var eth = web3.eth; " + for apiName, _ := range apis { + if apiName == api.Web3ApiName || apiName == api.EthApiName { + continue // manually mapped + } + + if err = js.re.Compile(fmt.Sprintf("%s.js", apiName), api.Javascript(apiName)); err == nil { + shortcuts += fmt.Sprintf("var %s = web3.%s; ", apiName, apiName) + } else { + utils.Fatalf("Error loading %s.js: %v", apiName, err) + } + } + + _, err = js.re.Eval(shortcuts) + + if err != nil { + utils.Fatalf("Error setting namespaces: %v", err) + } + + js.re.Eval(globalRegistrar + "registrar = GlobalRegistrar.at(\"" + globalRegistrarAddr + "\");") +} + +var ds = docserver.New("/") + +/* +func (self *jsre) ConfirmTransaction(tx string) bool { + if self.ethereum.NatSpec { + notice := natspec.GetNotice(self.xeth, tx, ds) + fmt.Println(notice) + answer, _ := self.Prompt("Confirm Transaction [y/n]") + return strings.HasPrefix(strings.Trim(answer, " "), "y") + } else { + return true + } +} + +func (self *jsre) UnlockAccount(addr []byte) bool { + fmt.Printf("Please unlock account %x.\n", addr) + pass, err := self.PasswordPrompt("Passphrase: ") + if err != nil { + return false + } + // TODO: allow retry + if err := self.ethereum.AccountManager().Unlock(common.BytesToAddress(addr), pass); err != nil { + return false + } else { + fmt.Println("Account is now unlocked for this session.") + return true + } +} +*/ + +func (self *jsre) exec(filename string) error { + if err := self.re.Exec(filename); err != nil { + self.re.Stop(false) + return fmt.Errorf("Javascript Error: %v", err) + } + self.re.Stop(true) + return nil +} + +func (self *jsre) suportedApis(ipcpath string) (map[string]string, error) { + config := comms.IpcConfig{ + Endpoint: ipcpath, + } + + client, err := comms.NewIpcClient(config, codec.JSON) + if err != nil { + return nil, err + } + + req := shared.Request{ + Id: 1, + Jsonrpc: "2.0", + Method: "modules", + } + + err = client.Send(req) + if err != nil { + return nil, err + } + + res, err := client.Recv() + if err != nil { + return nil, err + } + + if sucRes, ok := res.(shared.SuccessResponse); ok { + data, _ := json.Marshal(sucRes.Result) + apis := make(map[string]string) + err = json.Unmarshal(data, &apis) + if err == nil { + return apis, nil + } + } + + return nil, fmt.Errorf("Unable to determine supported API's") +} + +// show summary of current geth instance +func (self *jsre) welcome(ipcpath string) { + self.re.Eval(`console.log('instance: ' + web3.version.client);`) + self.re.Eval(`console.log(' datadir: ' + admin.datadir);`) + self.re.Eval(`console.log("coinbase: " + eth.coinbase);`) + self.re.Eval(`var lastBlockTimestamp = 1000 * eth.getBlock(eth.blockNumber).timestamp`) + self.re.Eval(`console.log("at block: " + eth.blockNumber + " (" + new Date(lastBlockTimestamp).toLocaleDateString() + + " " + new Date(lastBlockTimestamp).toLocaleTimeString() + ")");`) + + if modules, err := self.suportedApis(ipcpath); err == nil { + loadedModules := make([]string, 0) + for api, version := range modules { + loadedModules = append(loadedModules, fmt.Sprintf("%s:%s", api, version)) + } + sort.Strings(loadedModules) + + self.re.Eval(fmt.Sprintf("var modules = '%s';", strings.Join(loadedModules, " "))) + self.re.Eval(`console.log(" modules: " + modules);`) + } +} + +func (self *jsre) batch(args cli.Args) { + statement := strings.Join(args, " ") + val, err := self.re.Run(statement) + + if err != nil { + fmt.Printf("error: %v", err) + } else if val.IsDefined() && val.IsObject() { + obj, _ := self.re.Get("ret_result") + fmt.Printf("%v", obj) + } else if val.IsDefined() { + fmt.Printf("%v", val) + } + + if self.atexit != nil { + self.atexit() + } + + self.re.Stop(false) +} + +func (self *jsre) interactive(ipcpath string) { + self.welcome(ipcpath) + + // Read input lines. + prompt := make(chan string) + inputln := make(chan string) + go func() { + defer close(inputln) + for { + line, err := self.Prompt(<-prompt) + if err != nil { + return + } + inputln <- line + } + }() + // Wait for Ctrl-C, too. + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt) + + defer func() { + if self.atexit != nil { + self.atexit() + } + self.re.Stop(false) + }() + for { + prompt <- self.ps1 + select { + case <-sig: + fmt.Println("caught interrupt, exiting") + return + case input, ok := <-inputln: + if !ok || indentCount <= 0 && input == "exit" { + return + } + if input == "" { + continue + } + str += input + "\n" + self.setIndent() + if indentCount <= 0 { + hist := str[:len(str)-1] + self.AppendHistory(hist) + self.parseInput(str) + str = "" + } + } + } +} + +func (self *jsre) withHistory(op func(*os.File)) { + hist, err := os.OpenFile(filepath.Join(self.datadir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm) + if err != nil { + fmt.Printf("unable to open history file: %v\n", err) + return + } + op(hist) + hist.Close() +} + +func (self *jsre) parseInput(code string) { + defer func() { + if r := recover(); r != nil { + fmt.Println("[native] error", r) + } + }() + value, err := self.re.Run(code) + if err != nil { + if ottoErr, ok := err.(*otto.Error); ok { + fmt.Println(ottoErr.String()) + } else { + fmt.Println(err) + } + return + } + self.printValue(value) +} + +var indentCount = 0 +var str = "" + +func (self *jsre) setIndent() { + open := strings.Count(str, "{") + open += strings.Count(str, "(") + closed := strings.Count(str, "}") + closed += strings.Count(str, ")") + indentCount = open - closed + if indentCount <= 0 { + self.ps1 = "> " + } else { + self.ps1 = strings.Join(make([]string, indentCount*2), "..") + self.ps1 += " " + } +} + +func (self *jsre) printValue(v interface{}) { + val, err := self.re.PrettyPrint(v) + if err == nil { + fmt.Printf("%v", val) + } +} diff --git a/cmd/geth/js.go b/cmd/geth/js.go index 5a5dd75f3d..06c9239130 100644 --- a/cmd/geth/js.go +++ b/cmd/geth/js.go @@ -145,6 +145,7 @@ func newLightweightJSRE(libPath string, client comms.EthereumClient, interactive js := &jsre{ps1: "> "} js.wait = make(chan *big.Int) js.client = client + js.ds = docserver.New("/") if f == nil { f = js @@ -335,7 +336,6 @@ func (js *jsre) apiBindings(f xeth.Frontend) error { } js.re.Eval(`var GlobalRegistrar = eth.contract(` + registrar.GlobalRegistrarAbi + `); registrar = GlobalRegistrar.at("` + registrar.GlobalRegistrarAddr + `");`) - return nil } diff --git a/cmd/geth/js_test.go b/cmd/geth/js_test.go index 0b7045ff61..91b927dd3a 100644 --- a/cmd/geth/js_test.go +++ b/cmd/geth/js_test.go @@ -254,7 +254,7 @@ func TestSignature(t *testing.T) { } func TestContract(t *testing.T) { - // t.Skip("contract testing is implemented with mining in ethash test mode. This takes about 7seconds to run. Unskip and run on demand") + t.Skip("contract testing is implemented with mining in ethash test mode. This takes about 7seconds to run. Unskip and run on demand") tmp, repl, ethereum := testJEthRE(t) if err := ethereum.Start(); err != nil { t.Errorf("error starting ethereum: %v", err) diff --git a/rpc/api/admin.go b/rpc/api/admin.go index 78c75a60b8..40199caa9d 100644 --- a/rpc/api/admin.go +++ b/rpc/api/admin.go @@ -7,8 +7,14 @@ import ( "os" "time" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/compiler" + "github.com/ethereum/go-ethereum/common/docserver" + "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/logger/glog" "github.com/ethereum/go-ethereum/rlp" @@ -26,17 +32,27 @@ const ( 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_verbosity": (*adminApi).Verbosity, - "admin_chainSyncStatus": (*adminApi).ChainSyncStatus, - "admin_setSolc": (*adminApi).SetSolc, - "admin_datadir": (*adminApi).DataDir, - "admin_startRPC": (*adminApi).StartRPC, - "admin_stopRPC": (*adminApi).StopRPC, + "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, + "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, } ) @@ -49,6 +65,7 @@ type adminApi struct { ethereum *eth.Ethereum codec codec.Codec coder codec.ApiCoder + ds *docserver.DocServer } // create a new admin api instance @@ -58,6 +75,7 @@ func NewAdminApi(xeth *xeth.XEth, ethereum *eth.Ethereum, codec codec.Codec) *ad ethereum: ethereum, codec: codec, coder: codec.New(nil), + ds: docserver.New("/"), } } @@ -292,3 +310,148 @@ func sleepBlocks(wait chan *big.Int, height *big.Int, timer <-chan time.Time) (n // time.Sleep(time.Duration(sec) * time.Second) // return otto.UndefinedValue() // } +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) + err := reg.SetGlobalRegistrar(args.NameReg, sender) + if err != nil { + return false, err + } + + return registrar.GlobalRegistrarAddr, 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) + err := reg.SetHashReg(args.HashReg, sender) + if err != nil { + return false, err + } + + return registrar.HashRegAddr, 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) + err := reg.SetUrlHint(urlHint, sender) + if err != nil { + return nil, err + } + + return registrar.UrlHintAddr, 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.ds) + 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.ds.Get(args.Uri, args.Path) + if err != nil { + return nil, err + } + + return string(resp), nil +} diff --git a/rpc/api/admin_args.go b/rpc/api/admin_args.go index 7aee5d678e..a4d692c0a2 100644 --- a/rpc/api/admin_args.go +++ b/rpc/api/admin_args.go @@ -3,6 +3,7 @@ package api import ( "encoding/json" + "github.com/ethereum/go-ethereum/common/compiler" "github.com/ethereum/go-ethereum/rpc/shared" ) @@ -154,6 +155,7 @@ type SleepBlocksArgs struct { } func (args *SleepBlocksArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} if err := json.Unmarshal(b, &obj); err != nil { return shared.NewDecodeParamError(err.Error()) @@ -171,10 +173,276 @@ func (args *SleepBlocksArgs) UnmarshalJSON(b []byte) (err error) { if len(obj) >= 2 { if n, ok := obj[1].(int64); ok { - args.N = n + args.Timeout = n + } else { + return shared.NewInvalidTypeError("N", "not an integer") + } + } + + 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 { + 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 { + if hashreg, ok := obj[0].(string); ok { + args.HashReg = hashreg + } else { + return shared.NewInvalidTypeError("HashReg", "not a string") + } + } + + if len(obj) >= 2 { + 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 { + if urlhint, ok := obj[0].(string); ok { + args.UrlHint = urlhint + } else { + return shared.NewInvalidTypeError("UrlHint", "not a string") + } + } + + if len(obj) >= 2 { + 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[1].(string); ok { + args.Sender = sender + } else { + return shared.NewInvalidTypeError("Sender", "not a string") + } + } + + 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("Timeout", "not an integer") + 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 { + if path, ok := obj[1].(string); ok { + args.Path = path + } else { + return shared.NewInvalidTypeError("Path", "not a string") + } + } + return nil } diff --git a/rpc/api/admin_js.go b/rpc/api/admin_js.go index 97642ade71..1f822f2c78 100644 --- a/rpc/api/admin_js.go +++ b/rpc/api/admin_js.go @@ -53,7 +53,71 @@ web3._extend({ params: 0, inputFormatter: [], outputFormatter: web3._extend.formatters.formatOutputBool + }), + new web3._extend.Method({ + name: 'setGlobalRegistrar', + call: 'admin_setGlobalRegistrar', + params: 2, + inputFormatter: [web3._extend.utils.formatInputString,web3._extend.utils.formatInputString], + outputFormatter: web3._extend.formatters.formatOutputString + }), + new web3._extend.Method({ + name: 'setHashReg', + call: 'admin_setHashReg', + params: 2, + inputFormatter: [web3._extend.utils.formatInputString,web3._extend.utils.formatInputString], + outputFormatter: web3._extend.formatters.formatOutputString + }), + new web3._extend.Method({ + name: 'saveInfo', + call: 'admin_saveInfo', + params: 2, + inputFormatter: [function(obj) { return obj; },web3._extend.utils.formatInputString], + outputFormatter: web3._extend.formatters.formatOutputString + }), + new web3._extend.Method({ + name: 'register', + call: 'admin_register', + params: 2, + inputFormatter: [web3._extend.utils.formatInputString,web3._extend.utils.formatInputString], + outputFormatter: web3._extend.formatters.formatOutputBool + }), + new web3._extend.Method({ + name: 'registerUrl', + call: 'admin_registerUrl', + params: 3, + inputFormatter: [web3._extend.utils.formatInputString,web3._extend.utils.formatInputString,web3._extend.utils.formatInputString], + outputFormatter: web3._extend.formatters.formatOutputBool + }), + new web3._extend.Method({ + name: 'StartNatSpec', + call: 'admin_startNatSpec', + params: 0, + inputFormatter: [], + outputFormatter: web3._extend.formatters.formatOutputBool + }), + new web3._extend.Method({ + name: 'StopNatSpec', + call: 'admin_stopNatSpec', + params: 0, + inputFormatter: [], + outputFormatter: web3._extend.formatters.formatOutputBool + }), + new web3._extend.Method({ + name: 'getContractInfo', + call: 'admin_getContractInfo', + params: 1, + inputFormatter: [web3._extend.utils.formatInputString], + outputFormatter: function(obj) { return json.parse(obj); } + }), + new web3._extend.Method({ + name: 'httpGet', + call: 'admin_httpGet', + params: 2, + inputFormatter: [web3._extend.utils.formatInputString,web3._extend.utils.formatInputString], + outputFormatter: web3._extend.formatters.formatOutputString }) + ], properties: [