Browse Source

Registrar and contractInfo handling
* resolver -> common/registrar
* global registrar name registry interface
* add Call to resolver backend interface
* the hashReg and UrlHing contracts now initialised from global registry
* initialization of contracts uniform
* improve errors and more econsistent method names
* common/registrar/ethreg: versioned registrar
* integrate new naming and registrar in natspec
* js console api: setGlobalRegistrar, setHashReg, setUrlHint
* js test TestContract uses mining - tests fixed all pass
* eth/backend: allow PoW test mode (small ethash DAG)
* console jsre refers to resolver.abi/addr,
* cmd/geth/contracts.go moved to common/registrar

zelig 10 years ago
parent
commit
2739233719
6 changed files with 964 additions and 15 deletions
  1. 454 0
      cmd/console/js.go
  2. 1 1
      cmd/geth/js.go
  3. 1 1
      cmd/geth/js_test.go
  4. 174 11
      rpc/api/admin.go
  5. 270 2
      rpc/api/admin_args.go
  6. 64 0
      rpc/api/admin_js.go

+ 454 - 0
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)
+	}
+}

+ 1 - 1
cmd/geth/js.go

@@ -145,6 +145,7 @@ func newLightweightJSRE(libPath string, client comms.EthereumClient, interactive
 	js := &jsre{ps1: "> "}
 	js := &jsre{ps1: "> "}
 	js.wait = make(chan *big.Int)
 	js.wait = make(chan *big.Int)
 	js.client = client
 	js.client = client
+	js.ds = docserver.New("/")
 
 
 	if f == nil {
 	if f == nil {
 		f = js
 		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 + `");`)
 	js.re.Eval(`var GlobalRegistrar = eth.contract(` + registrar.GlobalRegistrarAbi + `);	 registrar = GlobalRegistrar.at("` + registrar.GlobalRegistrarAddr + `");`)
-
 	return nil
 	return nil
 }
 }
 
 

+ 1 - 1
cmd/geth/js_test.go

@@ -254,7 +254,7 @@ func TestSignature(t *testing.T) {
 }
 }
 
 
 func TestContract(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)
 	tmp, repl, ethereum := testJEthRE(t)
 	if err := ethereum.Start(); err != nil {
 	if err := ethereum.Start(); err != nil {
 		t.Errorf("error starting ethereum: %v", err)
 		t.Errorf("error starting ethereum: %v", err)

+ 174 - 11
rpc/api/admin.go

@@ -7,8 +7,14 @@ import (
 	"os"
 	"os"
 	"time"
 	"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"
 	"github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/core/types"
+	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/eth"
 	"github.com/ethereum/go-ethereum/eth"
 	"github.com/ethereum/go-ethereum/logger/glog"
 	"github.com/ethereum/go-ethereum/logger/glog"
 	"github.com/ethereum/go-ethereum/rlp"
 	"github.com/ethereum/go-ethereum/rlp"
@@ -26,17 +32,27 @@ const (
 var (
 var (
 	// mapping between methods and handlers
 	// mapping between methods and handlers
 	AdminMapping = map[string]adminhandler{
 	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
 	ethereum *eth.Ethereum
 	codec    codec.Codec
 	codec    codec.Codec
 	coder    codec.ApiCoder
 	coder    codec.ApiCoder
+	ds       *docserver.DocServer
 }
 }
 
 
 // create a new admin api instance
 // create a new admin api instance
@@ -58,6 +75,7 @@ func NewAdminApi(xeth *xeth.XEth, ethereum *eth.Ethereum, codec codec.Codec) *ad
 		ethereum: ethereum,
 		ethereum: ethereum,
 		codec:    codec,
 		codec:    codec,
 		coder:    codec.New(nil),
 		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)
 // 	time.Sleep(time.Duration(sec) * time.Second)
 // 	return otto.UndefinedValue()
 // 	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
+}

+ 270 - 2
rpc/api/admin_args.go

@@ -3,6 +3,7 @@ package api
 import (
 import (
 	"encoding/json"
 	"encoding/json"
 
 
+	"github.com/ethereum/go-ethereum/common/compiler"
 	"github.com/ethereum/go-ethereum/rpc/shared"
 	"github.com/ethereum/go-ethereum/rpc/shared"
 )
 )
 
 
@@ -154,6 +155,7 @@ type SleepBlocksArgs struct {
 }
 }
 
 
 func (args *SleepBlocksArgs) UnmarshalJSON(b []byte) (err error) {
 func (args *SleepBlocksArgs) UnmarshalJSON(b []byte) (err error) {
+
 	var obj []interface{}
 	var obj []interface{}
 	if err := json.Unmarshal(b, &obj); err != nil {
 	if err := json.Unmarshal(b, &obj); err != nil {
 		return shared.NewDecodeParamError(err.Error())
 		return shared.NewDecodeParamError(err.Error())
@@ -171,10 +173,276 @@ func (args *SleepBlocksArgs) UnmarshalJSON(b []byte) (err error) {
 
 
 	if len(obj) >= 2 {
 	if len(obj) >= 2 {
 		if n, ok := obj[1].(int64); ok {
 		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 {
 		} 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
 	return nil
 }
 }

+ 64 - 0
rpc/api/admin_js.go

@@ -53,7 +53,71 @@ web3._extend({
 			params: 0,
 			params: 0,
 			inputFormatter: [],
 			inputFormatter: [],
 			outputFormatter: web3._extend.formatters.formatOutputBool
 			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:
 	properties:
 	[
 	[