diff --git a/cmd/geth/blocktestcmd.go b/cmd/geth/blocktestcmd.go index 116eec2b37..ffea4400ee 100644 --- a/cmd/geth/blocktestcmd.go +++ b/cmd/geth/blocktestcmd.go @@ -86,7 +86,6 @@ func runBlockTest(ctx *cli.Context) { } func runOneBlockTest(ctx *cli.Context, test *tests.BlockTest) (*eth.Ethereum, error) { - // TODO remove in favor of logic contained in tests package cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx) cfg.NewDB = func(path string) (common.Database, error) { return ethdb.NewMemDatabase() } cfg.MaxPeers = 0 // disable network diff --git a/cmd/geth/contracts.go b/cmd/geth/contracts.go deleted file mode 100644 index 1f27838d15..0000000000 --- a/cmd/geth/contracts.go +++ /dev/null @@ -1,6 +0,0 @@ -package main - -var ( - globalRegistrar = `var GlobalRegistrar = web3.eth.contract([{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"name","outputs":[{"name":"o_name","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"content","outputs":[{"name":"","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"addr","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserve","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"subRegistrar","outputs":[{"name":"o_subRegistrar","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_newOwner","type":"address"}],"name":"transfer","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_registrar","type":"address"}],"name":"setSubRegistrar","outputs":[],"type":"function"},{"constant":false,"inputs":[],"name":"Registrar","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_a","type":"address"},{"name":"_primary","type":"bool"}],"name":"setAddress","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_content","type":"bytes32"}],"name":"setContent","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"disown","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"register","outputs":[{"name":"","type":"address"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"}],"name":"Changed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"addr","type":"address"}],"name":"PrimaryChanged","type":"event"}]);` - globalRegistrarAddr = "0xc6d9d2cd449a754c494264e1809c50e34d64562b" -) diff --git a/cmd/geth/js.go b/cmd/geth/js.go index 01840ebd9b..06c9239130 100644 --- a/cmd/geth/js.go +++ b/cmd/geth/js.go @@ -32,16 +32,17 @@ import ( "github.com/ethereum/go-ethereum/common" "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/eth" 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/ethereum/go-ethereum/xeth" "github.com/peterh/liner" "github.com/robertkrimen/otto" - "github.com/ethereum/go-ethereum/rpc/shared" ) type prompter interface { @@ -69,6 +70,7 @@ func (r dumbterm) PasswordPrompt(p string) (string, error) { func (r dumbterm) AppendHistory(string) {} type jsre struct { + ds *docserver.DocServer re *re.JSRE ethereum *eth.Ethereum xeth *xeth.XEth @@ -143,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 @@ -180,6 +183,7 @@ func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain string, client comms.Et if f == nil { f = js } + js.ds = docserver.New("/") js.xeth = xeth.New(ethereum, f) js.wait = js.xeth.UpdateState() js.client = client @@ -331,15 +335,13 @@ func (js *jsre) apiBindings(f xeth.Frontend) error { utils.Fatalf("Error setting namespaces: %v", err) } - js.re.Eval(globalRegistrar + "registrar = GlobalRegistrar.at(\"" + globalRegistrarAddr + "\");") + js.re.Eval(`var GlobalRegistrar = eth.contract(` + registrar.GlobalRegistrarAbi + `); registrar = GlobalRegistrar.at("` + registrar.GlobalRegistrarAddr + `");`) return nil } -var ds, _ = docserver.New("/") - func (self *jsre) ConfirmTransaction(tx string) bool { if self.ethereum.NatSpec { - notice := natspec.GetNotice(self.xeth, tx, ds) + notice := natspec.GetNotice(self.xeth, tx, self.ds) fmt.Println(notice) answer, _ := self.Prompt("Confirm Transaction [y/n]") return strings.HasPrefix(strings.Trim(answer, " "), "y") diff --git a/cmd/geth/js_test.go b/cmd/geth/js_test.go index 480f77c917..646ca404ad 100644 --- a/cmd/geth/js_test.go +++ b/cmd/geth/js_test.go @@ -3,21 +3,22 @@ package main import ( "fmt" "io/ioutil" + "math/big" "os" "path/filepath" "regexp" "runtime" "strconv" "testing" + "time" "github.com/ethereum/go-ethereum/accounts" "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/resolver" + "github.com/ethereum/go-ethereum/common/registrar" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/rpc/codec" @@ -43,7 +44,6 @@ var ( type testjethre struct { *jsre - stateDb *state.StateDB lastConfirm string ds *docserver.DocServer } @@ -64,6 +64,10 @@ func (self *testjethre) ConfirmTransaction(tx string) bool { } func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) { + return testREPL(t, nil) +} + +func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *eth.Ethereum) { tmp, err := ioutil.TempDir("", "geth-test") if err != nil { t.Fatal(err) @@ -74,14 +78,19 @@ func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) { ks := crypto.NewKeyStorePlain(filepath.Join(tmp, "keystore")) am := accounts.NewManager(ks) - ethereum, err := eth.New(ð.Config{ + conf := ð.Config{ NodeKey: testNodeKey, DataDir: tmp, AccountManager: am, MaxPeers: 0, Name: "test", SolcPath: testSolcPath, - }) + PowTest: true, + } + if config != nil { + config(conf) + } + ethereum, err := eth.New(conf) if err != nil { t.Fatal("%v", err) } @@ -102,25 +111,16 @@ func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) { } assetPath := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext") - ds, err := docserver.New("/") - if err != nil { - t.Errorf("Error creating DocServer: %v", err) - } - tf := &testjethre{ds: ds, stateDb: ethereum.ChainManager().State().Copy()} client := comms.NewInProcClient(codec.JSON) + ds := docserver.New("/") + tf := &testjethre{ds: ds} repl := newJSRE(ethereum, assetPath, "", client, false, tf) tf.jsre = repl return tmp, tf, ethereum } -// this line below is needed for transaction to be applied to the state in testing -// the heavy lifing is done in XEth.ApplyTestTxs -// this is fragile, overwriting xeth will result in -// process leaking since xeth loops cannot quit safely -// should be replaced by proper mining with testDAG for easy full integration tests -// txc, self.xeth = self.xeth.ApplyTestTxs(self.xeth.repl.stateDb, coinbase, txc) - func TestNodeInfo(t *testing.T) { + t.Skip("broken after p2p update") tmp, repl, ethereum := testJEthRE(t) if err := ethereum.Start(); err != nil { t.Fatalf("error starting ethereum: %v", err) @@ -254,8 +254,12 @@ func TestSignature(t *testing.T) { } func TestContract(t *testing.T) { - t.Skip() - tmp, repl, ethereum := testJEthRE(t) + t.Skip("contract testing is implemented with mining in ethash test mode. This takes about 7seconds to run. Unskip and run on demand") + coinbase := common.HexToAddress(testAddress) + tmp, repl, ethereum := testREPL(t, func(conf *eth.Config) { + conf.Etherbase = testAddress + conf.PowTest = true + }) if err := ethereum.Start(); err != nil { t.Errorf("error starting ethereum: %v", err) return @@ -263,12 +267,26 @@ func TestContract(t *testing.T) { defer ethereum.Stop() defer os.RemoveAll(tmp) - var txc uint64 - coinbase := common.HexToAddress(testAddress) - resolver.New(repl.xeth).CreateContracts(coinbase) - // time.Sleep(1000 * time.Millisecond) + reg := registrar.New(repl.xeth) + _, err := reg.SetGlobalRegistrar("", coinbase) + if err != nil { + t.Errorf("error setting HashReg: %v", err) + } + _, err = reg.SetHashReg("", coinbase) + if err != nil { + t.Errorf("error setting HashReg: %v", err) + } + _, err = reg.SetUrlHint("", coinbase) + if err != nil { + t.Errorf("error setting HashReg: %v", err) + } + /* TODO: + * lookup receipt and contract addresses by tx hash + * name registration for HashReg and UrlHint addresses + * mine those transactions + * then set once more SetHashReg SetUrlHint + */ - // checkEvalJSON(t, repl, `eth.getBlock("pending", true).transactions.length`, `2`) source := `contract test {\n` + " /// @notice Will multiply `a` by 7." + `\n` + ` function multiply(uint a) returns(uint d) {\n` + @@ -276,14 +294,20 @@ func TestContract(t *testing.T) { ` }\n` + `}\n` - checkEvalJSON(t, repl, `admin.contractInfo.stop()`, `true`) + if checkEvalJSON(t, repl, `admin.stopNatSpec()`, `true`) != nil { + return + } contractInfo, err := ioutil.ReadFile("info_test.json") if err != nil { t.Fatalf("%v", err) } - checkEvalJSON(t, repl, `primary = eth.accounts[0]`, `"`+testAddress+`"`) - checkEvalJSON(t, repl, `source = "`+source+`"`, `"`+source+`"`) + if checkEvalJSON(t, repl, `primary = eth.accounts[0]`, `"`+testAddress+`"`) != nil { + return + } + if checkEvalJSON(t, repl, `source = "`+source+`"`, `"`+source+`"`) != nil { + return + } // if solc is found with right version, test it, otherwise read from file sol, err := compiler.New("") @@ -303,69 +327,160 @@ func TestContract(t *testing.T) { t.Errorf("%v", err) } } else { - checkEvalJSON(t, repl, `contract = eth.compile.solidity(source).test`, string(contractInfo)) + if checkEvalJSON(t, repl, `contract = eth.compile.solidity(source).test`, string(contractInfo)) != nil { + return + } } - checkEvalJSON(t, repl, `contract.code`, `"0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056"`) + if checkEvalJSON(t, repl, `contract.code`, `"0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056"`) != nil { + return + } - checkEvalJSON( + if checkEvalJSON( t, repl, - `contractaddress = eth.sendTransaction({from: primary, data: contract.code })`, - `"0x5dcaace5982778b409c524873b319667eba5d074"`, - ) + `contractaddress = eth.sendTransaction({from: primary, data: contract.code})`, + `"0x46d69d55c3c4b86a924a92c9fc4720bb7bce1d74"`, + ) != nil { + return + } + + if !processTxs(repl, t, 8) { + return + } callSetup := `abiDef = JSON.parse('[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]'); Multiply7 = eth.contract(abiDef); multiply7 = Multiply7.at(contractaddress); ` - // time.Sleep(1500 * time.Millisecond) _, err = repl.re.Run(callSetup) if err != nil { t.Errorf("unexpected error setting up contract, got %v", err) + return } - // checkEvalJSON(t, repl, `eth.getBlock("pending", true).transactions.length`, `3`) - - // why is this sometimes failing? - // checkEvalJSON(t, repl, `multiply7.multiply.call(6)`, `42`) expNotice := "" if repl.lastConfirm != expNotice { t.Errorf("incorrect confirmation message: expected %v, got %v", expNotice, repl.lastConfirm) + return } - txc, repl.xeth = repl.xeth.ApplyTestTxs(repl.stateDb, coinbase, txc) + if checkEvalJSON(t, repl, `admin.startNatSpec()`, `true`) != nil { + return + } + if checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary })`, `"0x4ef9088431a8033e4580d00e4eb2487275e031ff4163c7529df0ef45af17857b"`) != nil { + return + } - checkEvalJSON(t, repl, `admin.contractInfo.start()`, `true`) - checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary, gas: "1000000", gasPrice: "100000" })`, `undefined`) - expNotice = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x87e2802265838c7f14bb69eecd2112911af6767907a702eeaa445239fb20711b'): {"params":[{"to":"0x5dcaace5982778b409c524873b319667eba5d074","data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000006"}]}` + if !processTxs(repl, t, 1) { + return + } + + expNotice = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x87e2802265838c7f14bb69eecd2112911af6767907a702eeaa445239fb20711b'): {"params":[{"to":"0x46d69d55c3c4b86a924a92c9fc4720bb7bce1d74","data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000006"}]}` if repl.lastConfirm != expNotice { - t.Errorf("incorrect confirmation message: expected %v, got %v", expNotice, repl.lastConfirm) + t.Errorf("incorrect confirmation message: expected\n%v, got\n%v", expNotice, repl.lastConfirm) + return } - var contenthash = `"0x86d2b7cf1e72e9a7a3f8d96601f0151742a2f780f1526414304fbe413dc7f9bd"` - if sol != nil { + var contentHash = `"0x86d2b7cf1e72e9a7a3f8d96601f0151742a2f780f1526414304fbe413dc7f9bd"` + if sol != nil && solcVersion != sol.Version() { modContractInfo := versionRE.ReplaceAll(contractInfo, []byte(`"compilerVersion":"`+sol.Version()+`"`)) - _ = modContractInfo - // contenthash = crypto.Sha3(modContractInfo) + fmt.Printf("modified contractinfo:\n%s\n", modContractInfo) + contentHash = `"` + common.ToHex(crypto.Sha3([]byte(modContractInfo))) + `"` } - checkEvalJSON(t, repl, `filename = "/tmp/info.json"`, `"/tmp/info.json"`) - checkEvalJSON(t, repl, `contenthash = admin.contractInfo.register(primary, contractaddress, contract, filename)`, contenthash) - checkEvalJSON(t, repl, `admin.contractInfo.registerUrl(primary, contenthash, "file://"+filename)`, `true`) - if err != nil { - t.Errorf("unexpected error registering, got %v", err) + if checkEvalJSON(t, repl, `filename = "/tmp/info.json"`, `"/tmp/info.json"`) != nil { + return + } + if checkEvalJSON(t, repl, `contentHash = admin.saveInfo(contract.info, filename)`, contentHash) != nil { + return + } + if checkEvalJSON(t, repl, `admin.register(primary, contractaddress, contentHash)`, `true`) != nil { + return + } + if checkEvalJSON(t, repl, `admin.registerUrl(primary, contentHash, "file://"+filename)`, `true`) != nil { + return } - checkEvalJSON(t, repl, `admin.contractInfo.start()`, `true`) + if checkEvalJSON(t, repl, `admin.startNatSpec()`, `true`) != nil { + return + } - // update state - txc, repl.xeth = repl.xeth.ApplyTestTxs(repl.stateDb, coinbase, txc) + if !processTxs(repl, t, 3) { + return + } + + if checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary })`, `"0x66d7635c12ad0b231e66da2f987ca3dfdca58ffe49c6442aa55960858103fd0c"`) != nil { + return + } + + if !processTxs(repl, t, 1) { + return + } - checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary, gas: "1000000", gasPrice: "100000" })`, `undefined`) expNotice = "Will multiply 6 by 7." if repl.lastConfirm != expNotice { - t.Errorf("incorrect confirmation message: expected %v, got %v", expNotice, repl.lastConfirm) + t.Errorf("incorrect confirmation message: expected\n%v, got\n%v", expNotice, repl.lastConfirm) + return + } +} + +func pendingTransactions(repl *testjethre, t *testing.T) (txc int64, err error) { + txs := repl.ethereum.TxPool().GetTransactions() + return int64(len(txs)), nil +} + +func processTxs(repl *testjethre, t *testing.T, expTxc int) bool { + var txc int64 + var err error + for i := 0; i < 50; i++ { + txc, err = pendingTransactions(repl, t) + if err != nil { + t.Errorf("unexpected error checking pending transactions: %v", err) + return false + } + if expTxc < int(txc) { + t.Errorf("too many pending transactions: expected %v, got %v", expTxc, txc) + return false + } else if expTxc == int(txc) { + break + } + time.Sleep(100 * time.Millisecond) + } + if int(txc) != expTxc { + t.Errorf("incorrect number of pending transactions, expected %v, got %v", expTxc, txc) + return false } + err = repl.ethereum.StartMining(runtime.NumCPU()) + if err != nil { + t.Errorf("unexpected error mining: %v", err) + return false + } + defer repl.ethereum.StopMining() + + timer := time.NewTimer(100 * time.Second) + height := new(big.Int).Add(repl.xeth.CurrentBlock().Number(), big.NewInt(1)) + repl.wait <- height + select { + case <-timer.C: + // if times out make sure the xeth loop does not block + go func() { + select { + case repl.wait <- nil: + case <-repl.wait: + } + }() + case <-repl.wait: + } + txc, err = pendingTransactions(repl, t) + if err != nil { + t.Errorf("unexpected error checking pending transactions: %v", err) + return false + } + if txc != 0 { + t.Errorf("%d trasactions were not mined", txc) + return false + } + return true } func checkEvalJSON(t *testing.T, re *testjethre, expr, want string) error { diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go index caf86974e5..4d5ffb473e 100644 --- a/common/compiler/solidity.go +++ b/common/compiler/solidity.go @@ -185,12 +185,12 @@ func (sol *Solidity) Compile(source string) (contracts map[string]*Contract, err return } -func ExtractInfo(contract *Contract, filename string) (contenthash common.Hash, err error) { - contractInfo, err := json.Marshal(contract.Info) +func SaveInfo(info *ContractInfo, filename string) (contenthash common.Hash, err error) { + infojson, err := json.Marshal(info) if err != nil { return } - contenthash = common.BytesToHash(crypto.Sha3(contractInfo)) - err = ioutil.WriteFile(filename, contractInfo, 0600) + contenthash = common.BytesToHash(crypto.Sha3(infojson)) + err = ioutil.WriteFile(filename, infojson, 0600) return } diff --git a/common/compiler/solidity_test.go b/common/compiler/solidity_test.go index 59b6b6cd66..b493d6be35 100644 --- a/common/compiler/solidity_test.go +++ b/common/compiler/solidity_test.go @@ -72,19 +72,15 @@ func TestNoCompiler(t *testing.T) { } } -func TestExtractInfo(t *testing.T) { +func TestSaveInfo(t *testing.T) { var cinfo ContractInfo err := json.Unmarshal([]byte(info), &cinfo) if err != nil { t.Errorf("%v", err) } - contract := &Contract{ - Code: "", - Info: cinfo, - } filename := "/tmp/solctest.info.json" os.Remove(filename) - cinfohash, err := ExtractInfo(contract, filename) + cinfohash, err := SaveInfo(&cinfo, filename) if err != nil { t.Errorf("error extracting info: %v", err) } diff --git a/common/docserver/docserver.go b/common/docserver/docserver.go index 5e076aa7ee..6b0cd31308 100644 --- a/common/docserver/docserver.go +++ b/common/docserver/docserver.go @@ -4,34 +4,26 @@ import ( "fmt" "io/ioutil" "net/http" + "path/filepath" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" ) -// http://golang.org/pkg/net/http/#RoundTripper -var ( - schemes = map[string]func(*DocServer) http.RoundTripper{ - // Simple File server from local disk file:///etc/passwd :) - "file": fileServerOnDocRoot, - } -) - -func fileServerOnDocRoot(ds *DocServer) http.RoundTripper { - return http.NewFileTransport(http.Dir(ds.DocRoot)) -} - type DocServer struct { *http.Transport DocRoot string + schemes []string } -func New(docRoot string) (self *DocServer, err error) { +func New(docRoot string) (self *DocServer) { self = &DocServer{ Transport: &http.Transport{}, DocRoot: docRoot, + schemes: []string{"file"}, } - err = self.RegisterProtocols(schemes) + self.DocRoot = "/tmp/" + self.RegisterProtocol("file", http.NewFileTransport(http.Dir(self.DocRoot))) return } @@ -45,16 +37,44 @@ func (self *DocServer) Client() *http.Client { } } -func (self *DocServer) RegisterProtocols(schemes map[string]func(*DocServer) http.RoundTripper) (err error) { - for scheme, rtf := range schemes { - self.RegisterProtocol(scheme, rtf(self)) +func (self *DocServer) RegisterScheme(scheme string, rt http.RoundTripper) { + self.schemes = append(self.schemes, scheme) + self.RegisterProtocol(scheme, rt) +} + +func (self *DocServer) HasScheme(scheme string) bool { + for _, s := range self.schemes { + if s == scheme { + return true + } } - return + return false } func (self *DocServer) GetAuthContent(uri string, hash common.Hash) (content []byte, err error) { + // retrieve content + content, err = self.Get(uri, "") + if err != nil { + return + } + + // check hash to authenticate content + chash := crypto.Sha3Hash(content) + if chash != hash { + content = nil + err = fmt.Errorf("content hash mismatch %x != %x (exp)", hash[:], chash[:]) + } + + return + +} + +// Get(uri, path) downloads the document at uri, if path is non-empty it +// is interpreted as a filepath to which the contents are saved +func (self *DocServer) Get(uri, path string) (content []byte, err error) { // retrieve content resp, err := self.Client().Get(uri) + defer func() { if resp != nil { resp.Body.Close() @@ -68,13 +88,10 @@ func (self *DocServer) GetAuthContent(uri string, hash common.Hash) (content []b return } - // check hash to authenticate content - hashbytes := crypto.Sha3(content) - var chash common.Hash - copy(chash[:], hashbytes) - if chash != hash { - content = nil - err = fmt.Errorf("content hash mismatch") + if path != "" { + var abspath string + abspath, err = filepath.Abs(path) + ioutil.WriteFile(abspath, content, 0700) } return diff --git a/common/docserver/docserver_test.go b/common/docserver/docserver_test.go index 400d7447ac..ca126071c7 100644 --- a/common/docserver/docserver_test.go +++ b/common/docserver/docserver_test.go @@ -2,6 +2,7 @@ package docserver import ( "io/ioutil" + "net/http" "os" "testing" @@ -15,7 +16,7 @@ func TestGetAuthContent(t *testing.T) { copy(hash[:], crypto.Sha3([]byte(text))) ioutil.WriteFile("/tmp/test.content", []byte(text), os.ModePerm) - ds, err := New("/tmp/") + ds := New("/tmp/") content, err := ds.GetAuthContent("file:///test.content", hash) if err != nil { t.Errorf("no error expected, got %v", err) @@ -26,7 +27,7 @@ func TestGetAuthContent(t *testing.T) { hash = common.Hash{} content, err = ds.GetAuthContent("file:///test.content", hash) - expected := "content hash mismatch" + expected := "content hash mismatch 0000000000000000000000000000000000000000000000000000000000000000 != 9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658 (exp)" if err == nil { t.Errorf("expected error, got nothing") } else { @@ -36,3 +37,18 @@ func TestGetAuthContent(t *testing.T) { } } + +type rt struct{} + +func (rt) RoundTrip(req *http.Request) (resp *http.Response, err error) { return } + +func TestRegisterScheme(t *testing.T) { + ds := New("/tmp/") + if ds.HasScheme("scheme") { + t.Errorf("expected scheme not to be registered") + } + ds.RegisterScheme("scheme", rt{}) + if !ds.HasScheme("scheme") { + t.Errorf("expected scheme to be registered") + } +} diff --git a/common/natspec/natspec.go b/common/natspec/natspec.go index 7e5f053c70..9965a2227a 100644 --- a/common/natspec/natspec.go +++ b/common/natspec/natspec.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/docserver" - "github.com/ethereum/go-ethereum/common/resolver" + "github.com/ethereum/go-ethereum/common/registrar" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/xeth" ) @@ -88,7 +88,7 @@ func New(xeth *xeth.XEth, jsontx string, http *docserver.DocServer) (self *NatSp } // also called by admin.contractInfo.get -func FetchDocsForContract(contractAddress string, xeth *xeth.XEth, http *docserver.DocServer) (content []byte, err error) { +func FetchDocsForContract(contractAddress string, xeth *xeth.XEth, ds *docserver.DocServer) (content []byte, err error) { // retrieve contract hash from state codehex := xeth.CodeAt(contractAddress) codeb := xeth.CodeAtBytes(contractAddress) @@ -99,20 +99,32 @@ func FetchDocsForContract(contractAddress string, xeth *xeth.XEth, http *docserv } codehash := common.BytesToHash(crypto.Sha3(codeb)) // set up nameresolver with natspecreg + urlhint contract addresses - res := resolver.New(xeth) + reg := registrar.New(xeth) // resolve host via HashReg/UrlHint Resolver - uri, hash, err := res.KeyToUrl(codehash) + hash, err := reg.HashToHash(codehash) + if err != nil { + return + } + if ds.HasScheme("bzz") { + content, err = ds.Get("bzz://"+hash.Hex()[2:], "") + if err == nil { // non-fatal + return + } + err = nil + //falling back to urlhint + } + + uri, err := reg.HashToUrl(hash) if err != nil { return } // get content via http client and authenticate content using hash - content, err = http.GetAuthContent(uri, hash) + content, err = ds.GetAuthContent(uri, hash) if err != nil { return } - return } diff --git a/common/natspec/natspec_e2e_test.go b/common/natspec/natspec_e2e_test.go index 7e91726496..db5ef2527d 100644 --- a/common/natspec/natspec_e2e_test.go +++ b/common/natspec/natspec_e2e_test.go @@ -3,16 +3,18 @@ package natspec import ( "fmt" "io/ioutil" + "math/big" "os" + "runtime" "strings" "testing" + "time" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/docserver" - "github.com/ethereum/go-ethereum/common/resolver" + "github.com/ethereum/go-ethereum/common/registrar" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" xe "github.com/ethereum/go-ethereum/xeth" @@ -73,13 +75,10 @@ const ( ) type testFrontend struct { - t *testing.T - // resolver *resolver.Resolver + t *testing.T ethereum *eth.Ethereum xeth *xe.XEth - coinbase common.Address - stateDb *state.StateDB - txc uint64 + wait chan *big.Int lastConfirm string wantNatSpec bool } @@ -91,10 +90,7 @@ func (self *testFrontend) UnlockAccount(acc []byte) bool { func (self *testFrontend) ConfirmTransaction(tx string) bool { if self.wantNatSpec { - ds, err := docserver.New("/tmp/") - if err != nil { - self.t.Errorf("Error creating DocServer: %v", err) - } + ds := docserver.New("/tmp/") self.lastConfirm = GetNotice(self.xeth, tx, ds) } return true @@ -128,6 +124,8 @@ func testEth(t *testing.T) (ethereum *eth.Ethereum, err error) { DataDir: "/tmp/eth-natspec", AccountManager: am, MaxPeers: 0, + PowTest: true, + Etherbase: testAddress, }) if err != nil { @@ -153,86 +151,106 @@ func testInit(t *testing.T) (self *testFrontend) { // mock frontend self = &testFrontend{t: t, ethereum: ethereum} self.xeth = xe.New(ethereum, self) - - addr, _ := ethereum.Etherbase() - self.coinbase = addr - self.stateDb = self.ethereum.ChainManager().State().Copy() + self.wait = self.xeth.UpdateState() + addr, _ := self.ethereum.Etherbase() // initialise the registry contracts - // self.resolver.CreateContracts(addr) - resolver.New(self.xeth).CreateContracts(addr) - self.applyTxs() - // t.Logf("HashReg contract registered at %v", resolver.HashRegContractAddress) - // t.Logf("URLHint contract registered at %v", resolver.UrlHintContractAddress) + reg := registrar.New(self.xeth) + var registrarTxhash, hashRegTxhash, urlHintTxhash string + registrarTxhash, err = reg.SetGlobalRegistrar("", addr) + if err != nil { + t.Errorf("error creating GlobalRegistrar: %v", err) + } + + hashRegTxhash, err = reg.SetHashReg("", addr) + if err != nil { + t.Errorf("error creating HashReg: %v", err) + } + urlHintTxhash, err = reg.SetUrlHint("", addr) + if err != nil { + t.Errorf("error creating UrlHint: %v", err) + } + if !processTxs(self, t, 3) { + t.Errorf("error mining txs") + } + _ = registrarTxhash + _ = hashRegTxhash + _ = urlHintTxhash + + /* TODO: + * lookup receipt and contract addresses by tx hash + * name registration for HashReg and UrlHint addresses + * mine those transactions + * then set once more SetHashReg SetUrlHint + */ return } -// this is needed for transaction to be applied to the state in testing -// the heavy lifing is done in XEth.ApplyTestTxs -// this is fragile, -// and does process leaking since xeth loops cannot quit safely -// should be replaced by proper mining with testDAG for easy full integration tests -func (self *testFrontend) applyTxs() { - self.txc, self.xeth = self.xeth.ApplyTestTxs(self.stateDb, self.coinbase, self.txc) - return -} - // end to end test func TestNatspecE2E(t *testing.T) { t.Skip() tf := testInit(t) defer tf.ethereum.Stop() + addr, _ := tf.ethereum.Etherbase() // create a contractInfo file (mock cloud-deployed contract metadocs) // incidentally this is the info for the registry contract itself ioutil.WriteFile("/tmp/"+testFileName, []byte(testContractInfo), os.ModePerm) - dochash := common.BytesToHash(crypto.Sha3([]byte(testContractInfo))) + dochash := crypto.Sha3Hash([]byte(testContractInfo)) // take the codehash for the contract we wanna test - // codehex := tf.xeth.CodeAt(resolver.HashRegContractAddress) - codeb := tf.xeth.CodeAtBytes(resolver.HashRegContractAddress) - codehash := common.BytesToHash(crypto.Sha3(codeb)) + codeb := tf.xeth.CodeAtBytes(registrar.HashRegAddr) + codehash := crypto.Sha3Hash(codeb) // use resolver to register codehash->dochash->url - registry := resolver.New(tf.xeth) - _, err := registry.Register(tf.coinbase, codehash, dochash, "file:///"+testFileName) + // test if globalregistry works + // registrar.HashRefAddr = "0x0" + // registrar.UrlHintAddr = "0x0" + reg := registrar.New(tf.xeth) + _, err := reg.SetHashToHash(addr, codehash, dochash) if err != nil { t.Errorf("error registering: %v", err) } - // apply txs to the state - tf.applyTxs() + _, err = reg.SetUrlToHash(addr, dochash, "file:///"+testFileName) + if err != nil { + t.Errorf("error registering: %v", err) + } + if !processTxs(tf, t, 5) { + return + } // NatSpec info for register method of HashReg contract installed // now using the same transactions to check confirm messages tf.wantNatSpec = true // this is set so now the backend uses natspec confirmation - _, err = registry.RegisterContentHash(tf.coinbase, codehash, dochash) + _, err = reg.SetHashToHash(addr, codehash, dochash) if err != nil { t.Errorf("error calling contract registry: %v", err) } + fmt.Printf("GlobalRegistrar: %v, HashReg: %v, UrlHint: %v\n", registrar.GlobalRegistrarAddr, registrar.HashRegAddr, registrar.UrlHintAddr) if tf.lastConfirm != testExpNotice { - t.Errorf("Wrong confirm message. expected '%v', got '%v'", testExpNotice, tf.lastConfirm) + t.Errorf("Wrong confirm message. expected\n'%v', got\n'%v'", testExpNotice, tf.lastConfirm) } // test unknown method - exp := fmt.Sprintf(testExpNotice2, resolver.HashRegContractAddress) - _, err = registry.SetOwner(tf.coinbase) + exp := fmt.Sprintf(testExpNotice2, registrar.HashRegAddr) + _, err = reg.SetOwner(addr) if err != nil { t.Errorf("error setting owner: %v", err) } if tf.lastConfirm != exp { - t.Errorf("Wrong confirm message, expected '%v', got '%v'", exp, tf.lastConfirm) + t.Errorf("Wrong confirm message, expected\n'%v', got\n'%v'", exp, tf.lastConfirm) } // test unknown contract - exp = fmt.Sprintf(testExpNotice3, resolver.UrlHintContractAddress) + exp = fmt.Sprintf(testExpNotice3, registrar.UrlHintAddr) - _, err = registry.RegisterUrl(tf.coinbase, dochash, "file:///test.content") + _, err = reg.SetUrlToHash(addr, dochash, "file:///test.content") if err != nil { t.Errorf("error registering: %v", err) } @@ -242,3 +260,63 @@ func TestNatspecE2E(t *testing.T) { } } + +func pendingTransactions(repl *testFrontend, t *testing.T) (txc int64, err error) { + txs := repl.ethereum.TxPool().GetTransactions() + return int64(len(txs)), nil +} + +func processTxs(repl *testFrontend, t *testing.T, expTxc int) bool { + var txc int64 + var err error + for i := 0; i < 50; i++ { + txc, err = pendingTransactions(repl, t) + if err != nil { + t.Errorf("unexpected error checking pending transactions: %v", err) + return false + } + if expTxc < int(txc) { + t.Errorf("too many pending transactions: expected %v, got %v", expTxc, txc) + return false + } else if expTxc == int(txc) { + break + } + time.Sleep(100 * time.Millisecond) + } + if int(txc) != expTxc { + t.Errorf("incorrect number of pending transactions, expected %v, got %v", expTxc, txc) + return false + } + + err = repl.ethereum.StartMining(runtime.NumCPU()) + if err != nil { + t.Errorf("unexpected error mining: %v", err) + return false + } + defer repl.ethereum.StopMining() + + timer := time.NewTimer(100 * time.Second) + height := new(big.Int).Add(repl.xeth.CurrentBlock().Number(), big.NewInt(1)) + repl.wait <- height + select { + case <-timer.C: + // if times out make sure the xeth loop does not block + go func() { + select { + case repl.wait <- nil: + case <-repl.wait: + } + }() + case <-repl.wait: + } + txc, err = pendingTransactions(repl, t) + if err != nil { + t.Errorf("unexpected error checking pending transactions: %v", err) + return false + } + if txc != 0 { + t.Errorf("%d trasactions were not mined", txc) + return false + } + return true +} diff --git a/common/natspec/natspec_e2e_test.go.orig b/common/natspec/natspec_e2e_test.go.orig new file mode 100644 index 0000000000..ae8e17ad9a --- /dev/null +++ b/common/natspec/natspec_e2e_test.go.orig @@ -0,0 +1,253 @@ +package natspec + +import ( + "fmt" + "io/ioutil" + "os" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/docserver" + "github.com/ethereum/go-ethereum/common/registrar" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth" + xe "github.com/ethereum/go-ethereum/xeth" +) + +const ( + testBalance = "10000000000000000000" + + testFileName = "long_file_name_for_testing_registration_of_URLs_longer_than_32_bytes.content" + + testNotice = "Register key `utils.toHex(_key)` <- content `utils.toHex(_content)`" + + testExpNotice = "Register key 0xadd1a7d961cff0242089674ec2ef6fca671ab15e1fe80e38859fc815b98d88ab <- content 0xb3a2dea218de5d8bbe6c4645aadbf67b5ab00ecb1a9ec95dbdad6a0eed3e41a7" + + testExpNotice2 = `About to submit transaction (NatSpec notice error: abi key does not match any method): {"params":[{"to":"%s","data": "0x31e12c20"}]}` + + testExpNotice3 = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x1392c62d05b2d149e22a339c531157ae06b44d39a674cce500064b12b9aeb019'): {"params":[{"to":"%s","data": "0x300a3bbfb3a2dea218de5d8bbe6c4645aadbf67b5ab00ecb1a9ec95dbdad6a0eed3e41a7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066696c653a2f2f2f746573742e636f6e74656e74"}]}` +) + +const ( + testUserDoc = ` +{ + "methods": { + "register(uint256,uint256)": { + "notice": "` + testNotice + `" + } + }, + "invariants": [ + { "notice": "" } + ], + "construction": [ + { "notice": "" } + ] +} +` + testAbiDefinition = ` +[{ + "name": "register", + "constant": false, + "type": "function", + "inputs": [{ + "name": "_key", + "type": "uint256" + }, { + "name": "_content", + "type": "uint256" + }], + "outputs": [] +}] +` + + testContractInfo = ` +{ + "userDoc": ` + testUserDoc + `, + "abiDefinition": ` + testAbiDefinition + ` +} +` +) + +type testFrontend struct { + t *testing.T + ethereum *eth.Ethereum + xeth *xe.XEth + coinbase common.Address + stateDb *state.StateDB + txc uint64 + lastConfirm string + wantNatSpec bool +} + +func (self *testFrontend) UnlockAccount(acc []byte) bool { + self.ethereum.AccountManager().Unlock(common.BytesToAddress(acc), "password") + return true +} + +func (self *testFrontend) ConfirmTransaction(tx string) bool { + if self.wantNatSpec { + ds := docserver.New("/tmp/") + self.lastConfirm = GetNotice(self.xeth, tx, ds) + } + return true +} + +func testEth(t *testing.T) (ethereum *eth.Ethereum, err error) { + + os.RemoveAll("/tmp/eth-natspec/") + + err = os.MkdirAll("/tmp/eth-natspec/keystore", os.ModePerm) + if err != nil { + panic(err) + } + + // create a testAddress + ks := crypto.NewKeyStorePassphrase("/tmp/eth-natspec/keystore") + am := accounts.NewManager(ks) + testAccount, err := am.NewAccount("password") + if err != nil { + panic(err) + } + testAddress := strings.TrimPrefix(testAccount.Address.Hex(), "0x") + + // set up mock genesis with balance on the testAddress + core.GenesisAccounts = []byte(`{ + "` + testAddress + `": {"balance": "` + testBalance + `"} + }`) + + // only use minimalistic stack with no networking + ethereum, err = eth.New(ð.Config{ + DataDir: "/tmp/eth-natspec", + AccountManager: am, + MaxPeers: 0, + }) + + if err != nil { + panic(err) + } + + return +} + +func testInit(t *testing.T) (self *testFrontend) { + // initialise and start minimal ethereum stack + ethereum, err := testEth(t) + if err != nil { + t.Errorf("error creating ethereum: %v", err) + return + } + err = ethereum.Start() + if err != nil { + t.Errorf("error starting ethereum: %v", err) + return + } + + // mock frontend + self = &testFrontend{t: t, ethereum: ethereum} + self.xeth = xe.New(ethereum, self) + + addr, _ := ethereum.Etherbase() + self.coinbase = addr + self.stateDb = self.ethereum.ChainManager().State().Copy() + + // initialise the registry contracts + reg := registrar.New(self.xeth) + err = reg.SetHashReg("", addr) + if err != nil { + t.Errorf("error creating HashReg: %v", err) + } + err = reg.SetUrlHint("", addr) + if err != nil { + t.Errorf("error creating UrlHint: %v", err) + } + self.applyTxs() + + return + +} + +// this is needed for transaction to be applied to the state in testing +// the heavy lifing is done in XEth.ApplyTestTxs +// this is fragile, +// and does process leaking since xeth loops cannot quit safely +// should be replaced by proper mining with testDAG for easy full integration tests +func (self *testFrontend) applyTxs() { + self.txc, self.xeth = self.xeth.ApplyTestTxs(self.stateDb, self.coinbase, self.txc) + return +} + +// end to end test +func TestNatspecE2E(t *testing.T) { + t.Skip() + + tf := testInit(t) + defer tf.ethereum.Stop() + + // create a contractInfo file (mock cloud-deployed contract metadocs) + // incidentally this is the info for the registry contract itself + ioutil.WriteFile("/tmp/"+testFileName, []byte(testContractInfo), os.ModePerm) + dochash := common.BytesToHash(crypto.Sha3([]byte(testContractInfo))) + + // take the codehash for the contract we wanna test + // codehex := tf.xeth.CodeAt(registar.HashRegAddr) + codeb := tf.xeth.CodeAtBytes(registrar.HashRegAddr) + codehash := common.BytesToHash(crypto.Sha3(codeb)) + + // use resolver to register codehash->dochash->url + // test if globalregistry works + // registrar.HashRefAddr = "0x0" + // registrar.UrlHintAddr = "0x0" + reg := registrar.New(tf.xeth) + _, err := reg.SetHashToHash(tf.coinbase, codehash, dochash) + if err != nil { + t.Errorf("error registering: %v", err) + } + _, err = reg.SetUrlToHash(tf.coinbase, dochash, "file:///"+testFileName) + if err != nil { + t.Errorf("error registering: %v", err) + } + // apply txs to the state + tf.applyTxs() + + // NatSpec info for register method of HashReg contract installed + // now using the same transactions to check confirm messages + + tf.wantNatSpec = true // this is set so now the backend uses natspec confirmation + _, err = reg.SetHashToHash(tf.coinbase, codehash, dochash) + if err != nil { + t.Errorf("error calling contract registry: %v", err) + } + + fmt.Printf("GlobalRegistrar: %v, HashReg: %v, UrlHint: %v\n", registrar.GlobalRegistrarAddr, registrar.HashRegAddr, registrar.UrlHintAddr) + if tf.lastConfirm != testExpNotice { + t.Errorf("Wrong confirm message. expected '%v', got '%v'", testExpNotice, tf.lastConfirm) + } + + // test unknown method + exp := fmt.Sprintf(testExpNotice2, registrar.HashRegAddr) + _, err = reg.SetOwner(tf.coinbase) + if err != nil { + t.Errorf("error setting owner: %v", err) + } + + if tf.lastConfirm != exp { + t.Errorf("Wrong confirm message, expected '%v', got '%v'", exp, tf.lastConfirm) + } + + // test unknown contract + exp = fmt.Sprintf(testExpNotice3, registrar.UrlHintAddr) + + _, err = reg.SetUrlToHash(tf.coinbase, dochash, "file:///test.content") + if err != nil { + t.Errorf("error registering: %v", err) + } + + if tf.lastConfirm != exp { + t.Errorf("Wrong confirm message, expected '%v', got '%v'", exp, tf.lastConfirm) + } + +} diff --git a/common/registrar/contracts.go b/common/registrar/contracts.go new file mode 100644 index 0000000000..6c624030ef --- /dev/null +++ b/common/registrar/contracts.go @@ -0,0 +1,147 @@ +package registrar + +const ( // built-in contracts address source code and evm code + UrlHintCode = "0x60c180600c6000396000f30060003560e060020a90048063300a3bbf14601557005b6024600435602435604435602a565b60006000f35b6000600084815260200190815260200160002054600160a060020a0316600014806078575033600160a060020a03166000600085815260200190815260200160002054600160a060020a0316145b607f5760bc565b336000600085815260200190815260200160002081905550806001600085815260200190815260200160002083610100811060b657005b01819055505b50505056" + + UrlHintSrc = ` +contract URLhint { + function register(uint256 _hash, uint8 idx, uint256 _url) { + if (owner[_hash] == 0 || owner[_hash] == msg.sender) { + owner[_hash] = msg.sender; + url[_hash][idx] = _url; + } + } + mapping (uint256 => address) owner; + (uint256 => uint256[256]) url; +} + ` + + HashRegCode = "0x609880600c6000396000f30060003560e060020a9004806331e12c2014601f578063d66d6c1014602b57005b6025603d565b60006000f35b6037600435602435605d565b60006000f35b600054600160a060020a0316600014605357605b565b336000819055505b565b600054600160a060020a031633600160a060020a031614607b576094565b8060016000848152602001908152602001600020819055505b505056" + + HashRegSrc = ` +contract HashReg { + function setowner() { + if (owner == 0) { + owner = msg.sender; + } + } + function register(uint256 _key, uint256 _content) { + if (msg.sender == owner) { + content[_key] = _content; + } + } + address owner; + mapping (uint256 => uint256) content; +} +` + + GlobalRegistrarSrc = ` +//sol + +import "owned"; + +contract NameRegister { + function addr(bytes32 _name) constant returns (address o_owner) {} + function name(address _owner) constant returns (bytes32 o_name) {} +} + +contract Registrar is NameRegister { + event Changed(bytes32 indexed name); + event PrimaryChanged(bytes32 indexed name, address indexed addr); + + function owner(bytes32 _name) constant returns (address o_owner) {} + function addr(bytes32 _name) constant returns (address o_address) {} + function subRegistrar(bytes32 _name) constant returns (address o_subRegistrar) {} + function content(bytes32 _name) constant returns (bytes32 o_content) {} + + function name(address _owner) constant returns (bytes32 o_name) {} +} + +contract GlobalRegistrar is Registrar { + struct Record { + address owner; + address primary; + address subRegistrar; + bytes32 content; + uint value; + uint renewalDate; + } + + function Registrar() { + // TODO: Populate with hall-of-fame. + } + + function reserve(bytes32 _name) { + // Don't allow the same name to be overwritten. + // TODO: bidding mechanism + if (m_toRecord[_name].owner == 0) { + m_toRecord[_name].owner = msg.sender; + Changed(_name); + } + } + + /* + TODO + > 12 chars: free + <= 12 chars: auction: + 1. new names are auctioned + - 7 day period to collect all bid bytes32es + deposits + - 1 day period to collect all bids to be considered (validity requires associated deposit to be >10% of bid) + - all valid bids are burnt except highest - difference between that and second highest is returned to winner + 2. remember when last auctioned/renewed + 3. anyone can force renewal process: + - 7 day period to collect all bid bytes32es + deposits + - 1 day period to collect all bids & full amounts - bids only uncovered if sufficiently high. + - 1% of winner burnt; original owner paid rest. + */ + + modifier onlyrecordowner(bytes32 _name) { if (m_toRecord[_name].owner == msg.sender) _ } + + function transfer(bytes32 _name, address _newOwner) onlyrecordowner(_name) { + m_toRecord[_name].owner = _newOwner; + Changed(_name); + } + + function disown(bytes32 _name) onlyrecordowner(_name) { + if (m_toName[m_toRecord[_name].primary] == _name) + { + PrimaryChanged(_name, m_toRecord[_name].primary); + m_toName[m_toRecord[_name].primary] = ""; + } + delete m_toRecord[_name]; + Changed(_name); + } + + function setAddress(bytes32 _name, address _a, bool _primary) onlyrecordowner(_name) { + m_toRecord[_name].primary = _a; + if (_primary) + { + PrimaryChanged(_name, _a); + m_toName[_a] = _name; + } + Changed(_name); + } + function setSubRegistrar(bytes32 _name, address _registrar) onlyrecordowner(_name) { + m_toRecord[_name].subRegistrar = _registrar; + Changed(_name); + } + function setContent(bytes32 _name, bytes32 _content) onlyrecordowner(_name) { + m_toRecord[_name].content = _content; + Changed(_name); + } + + function owner(bytes32 _name) constant returns (address) { return m_toRecord[_name].owner; } + function addr(bytes32 _name) constant returns (address) { return m_toRecord[_name].primary; } +// function subRegistrar(bytes32 _name) constant returns (address) { return m_toRecord[_name].subRegistrar; } // TODO: bring in on next iteration. + function register(bytes32 _name) constant returns (address) { return m_toRecord[_name].subRegistrar; } // only possible for now + function content(bytes32 _name) constant returns (bytes32) { return m_toRecord[_name].content; } + function name(address _owner) constant returns (bytes32 o_name) { return m_toName[_owner]; } + + mapping (address => bytes32) m_toName; + mapping (bytes32 => Record) m_toRecord; +} +` + GlobalRegistrarAbi = `[{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"name","outputs":[{"name":"o_name","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"content","outputs":[{"name":"","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"addr","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserve","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"subRegistrar","outputs":[{"name":"o_subRegistrar","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_newOwner","type":"address"}],"name":"transfer","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_registrar","type":"address"}],"name":"setSubRegistrar","outputs":[],"type":"function"},{"constant":false,"inputs":[],"name":"Registrar","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_a","type":"address"},{"name":"_primary","type":"bool"}],"name":"setAddress","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_content","type":"bytes32"}],"name":"setContent","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"disown","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"register","outputs":[{"name":"","type":"address"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"}],"name":"Changed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"addr","type":"address"}],"name":"PrimaryChanged","type":"event"}]` + + GlobalRegistrarCode = `0x610b408061000e6000396000f3006000357c01000000000000000000000000000000000000000000000000000000009004806301984892146100b357806302571be3146100ce5780632dff6941146100ff5780633b3b57de1461011a578063432ced041461014b5780635a3a05bd1461016257806379ce9fac1461019357806389a69c0e146101b0578063b9f37c86146101cd578063be99a980146101de578063c3d014d614610201578063d93e75731461021e578063e1fa8e841461023557005b6100c4600480359060200150610b02565b8060005260206000f35b6100df6004803590602001506109f3565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b610110600480359060200150610ad4565b8060005260206000f35b61012b600480359060200150610a3e565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b61015c600480359060200150610271565b60006000f35b610173600480359060200150610266565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b6101aa600480359060200180359060200150610341565b60006000f35b6101c7600480359060200180359060200150610844565b60006000f35b6101d860045061026e565b60006000f35b6101fb6004803590602001803590602001803590602001506106de565b60006000f35b61021860048035906020018035906020015061092c565b60006000f35b61022f600480359060200150610429565b60006000f35b610246600480359060200150610a89565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b60005b919050565b5b565b60006001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561033d57336001600050600083815260200190815260200160002060005060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550807fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b5b50565b813373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561042357816001600050600085815260200190815260200160002060005060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b5050565b803373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156106d95781600060005060006001600050600086815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000505414156105fd576001600050600083815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16827ff63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a85456040604090036040a36000600060005060006001600050600086815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050819055505b6001600050600083815260200190815260200160002060006000820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556002820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556003820160005060009055600482016000506000905560058201600050600090555050817fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b50565b823373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561083d57826001600050600086815260200190815260200160002060005060010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908302179055508115610811578273ffffffffffffffffffffffffffffffffffffffff16847ff63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a85456040604090036040a383600060005060008573ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050819055505b837fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b505050565b813373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561092657816001600050600085815260200190815260200160002060005060020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b5050565b813373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156109ed57816001600050600085815260200190815260200160002060005060030160005081905550827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b5050565b60006001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050610a39565b919050565b60006001600050600083815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050610a84565b919050565b60006001600050600083815260200190815260200160002060005060020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050610acf565b919050565b600060016000506000838152602001908152602001600020600050600301600050549050610afd565b919050565b6000600060005060008373ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050549050610b3b565b91905056` +) diff --git a/common/registrar/ethreg/ethreg.go b/common/registrar/ethreg/ethreg.go new file mode 100644 index 0000000000..f5ad3345bf --- /dev/null +++ b/common/registrar/ethreg/ethreg.go @@ -0,0 +1,32 @@ +package ethreg + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common/registrar" + "github.com/ethereum/go-ethereum/xeth" +) + +// implements a versioned Registrar on an archiving full node +type EthReg struct { + backend *xeth.XEth + registry *registrar.Registrar +} + +func New(xe *xeth.XEth) (self *EthReg) { + self = &EthReg{backend: xe} + self.registry = registrar.New(xe) + return +} + +func (self *EthReg) Registry() *registrar.Registrar { + return self.registry +} + +func (self *EthReg) Resolver(n *big.Int) *registrar.Registrar { + xe := self.backend + if n != nil { + xe = self.backend.AtStateNum(n.Int64()) + } + return registrar.New(xe) +} diff --git a/common/registrar/registrar.go b/common/registrar/registrar.go new file mode 100644 index 0000000000..b70c7227bf --- /dev/null +++ b/common/registrar/registrar.go @@ -0,0 +1,392 @@ +package registrar + +import ( + "encoding/binary" + "fmt" + "math/big" + "regexp" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +/* +Registrar implements the Ethereum name registrar services mapping +- arbitrary strings to ethereum addresses +- hashes to hashes +- hashes to arbitrary strings +(likely will provide lookup service for all three) + +The Registrar is used by +* the roundtripper transport implementation of +url schemes to resolve domain names and services that register these names +* contract info retrieval (NatSpec). + +The Registrar uses 3 contracts on the blockchain: +* GlobalRegistrar: Name (string) -> Address (Owner) +* HashReg : Key Hash (hash of domain name or contract code) -> Content Hash +* UrlHint : Content Hash -> Url Hint + +These contracts are (currently) not included in the genesis block. +Each Set needs to be called once on each blockchain/network once. + +Contract addresses need to be set (HashReg and UrlHint retrieved from the global +registrar the first time any Registrar method is called in a client session + +So the caller needs to make sure the relevant environment initialised the desired +contracts +*/ +var ( + UrlHintAddr = "0x0" + HashRegAddr = "0x0" + GlobalRegistrarAddr = "0x0" + // GlobalRegistrarAddr = "0xc6d9d2cd449a754c494264e1809c50e34d64562b" + + zero = regexp.MustCompile("^(0x)?0*$") +) + +const ( + trueHex = "0000000000000000000000000000000000000000000000000000000000000001" + falseHex = "0000000000000000000000000000000000000000000000000000000000000000" +) + +func abiSignature(s string) string { + return common.ToHex(crypto.Sha3([]byte(s))[:4]) +} + +var ( + HashRegName = "HashReg" + UrlHintName = "UrlHint" + + registerContentHashAbi = abiSignature("register(uint256,uint256)") + registerUrlAbi = abiSignature("register(uint256,uint8,uint256)") + setOwnerAbi = abiSignature("setowner()") + reserveAbi = abiSignature("reserve(bytes32)") + resolveAbi = abiSignature("addr(bytes32)") + registerAbi = abiSignature("setAddress(bytes32,address,bool)") + addressAbiPrefix = falseHex[:24] +) + +// Registrar's backend is defined as an interface (implemented by xeth, but could be remote) +type Backend interface { + StorageAt(string, string) string + Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) + Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error) +} + +// TODO Registrar should also just implement The Resolver and Registry interfaces +// Simplify for now. +type VersionedRegistrar interface { + Resolver(*big.Int) *Registrar + Registry() *Registrar +} + +type Registrar struct { + backend Backend +} + +func New(b Backend) (res *Registrar) { + res = &Registrar{b} + return +} + +func (self *Registrar) SetGlobalRegistrar(namereg string, addr common.Address) (txhash string, err error) { + if namereg != "" { + GlobalRegistrarAddr = namereg + return + } + if GlobalRegistrarAddr == "0x0" || GlobalRegistrarAddr == "0x" { + if (addr == common.Address{}) { + err = fmt.Errorf("GlobalRegistrar address not found and sender for creation not given") + return + } else { + txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "800000", "", GlobalRegistrarCode) + if err != nil { + err = fmt.Errorf("GlobalRegistrar address not found and sender for creation failed: %v", err) + return + } + } + } + return +} + +func (self *Registrar) SetHashReg(hashreg string, addr common.Address) (txhash string, err error) { + if hashreg != "" { + HashRegAddr = hashreg + } else { + if !zero.MatchString(HashRegAddr) { + return + } + nameHex, extra := encodeName(HashRegName, 2) + hashRegAbi := resolveAbi + nameHex + extra + glog.V(logger.Detail).Infof("\ncall HashRegAddr %v with %v\n", GlobalRegistrarAddr, hashRegAbi) + var res string + res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", hashRegAbi) + if len(res) >= 40 { + HashRegAddr = "0x" + res[len(res)-40:len(res)] + } + if err != nil || zero.MatchString(HashRegAddr) { + if (addr == common.Address{}) { + err = fmt.Errorf("HashReg address not found and sender for creation not given") + return + } + + txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "", "", HashRegCode) + if err != nil { + err = fmt.Errorf("HashReg address not found and sender for creation failed: %v", err) + } + glog.V(logger.Detail).Infof("created HashRegAddr @ txhash %v\n", txhash) + } else { + glog.V(logger.Detail).Infof("HashRegAddr found at @ %v\n", HashRegAddr) + return + } + } + + return +} + +func (self *Registrar) SetUrlHint(urlhint string, addr common.Address) (txhash string, err error) { + if urlhint != "" { + UrlHintAddr = urlhint + } else { + if !zero.MatchString(UrlHintAddr) { + return + } + nameHex, extra := encodeName(UrlHintName, 2) + urlHintAbi := resolveAbi + nameHex + extra + glog.V(logger.Detail).Infof("UrlHint address query data: %s to %s", urlHintAbi, GlobalRegistrarAddr) + var res string + res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", urlHintAbi) + if len(res) >= 40 { + UrlHintAddr = "0x" + res[len(res)-40:len(res)] + } + if err != nil || zero.MatchString(UrlHintAddr) { + if (addr == common.Address{}) { + err = fmt.Errorf("UrlHint address not found and sender for creation not given") + return + } + txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "210000", "", UrlHintCode) + if err != nil { + err = fmt.Errorf("UrlHint address not found and sender for creation failed: %v", err) + } + glog.V(logger.Detail).Infof("created UrlHint @ txhash %v\n", txhash) + } else { + glog.V(logger.Detail).Infof("UrlHint found @ %v\n", HashRegAddr) + return + } + } + + return +} + +// ReserveName(from, name) reserves name for the sender address in the globalRegistrar +// the tx needs to be mined to take effect +func (self *Registrar) ReserveName(address common.Address, name string) (txh string, err error) { + nameHex, extra := encodeName(name, 2) + abi := reserveAbi + nameHex + extra + glog.V(logger.Detail).Infof("Reserve data: %s", abi) + return self.backend.Transact( + address.Hex(), + GlobalRegistrarAddr, + "", "", "", "", + abi, + ) +} + +// SetAddressToName(from, name, addr) will set the Address to address for name +// in the globalRegistrar using from as the sender of the transaction +// the tx needs to be mined to take effect +func (self *Registrar) SetAddressToName(from common.Address, name string, address common.Address) (txh string, err error) { + nameHex, extra := encodeName(name, 6) + addrHex := encodeAddress(address) + + abi := registerAbi + nameHex + addrHex + trueHex + extra + glog.V(logger.Detail).Infof("SetAddressToName data: %s to %s ", abi, GlobalRegistrarAddr) + + return self.backend.Transact( + from.Hex(), + GlobalRegistrarAddr, + "", "", "", "", + abi, + ) +} + +// NameToAddr(from, name) queries the registrar for the address on name +func (self *Registrar) NameToAddr(from common.Address, name string) (address common.Address, err error) { + nameHex, extra := encodeName(name, 2) + abi := resolveAbi + nameHex + extra + glog.V(logger.Detail).Infof("NameToAddr data: %s", abi) + res, _, err := self.backend.Call( + from.Hex(), + GlobalRegistrarAddr, + "", "", "", + abi, + ) + if err != nil { + return + } + address = common.HexToAddress(res) + return +} + +// called as first step in the registration process on HashReg +func (self *Registrar) SetOwner(address common.Address) (txh string, err error) { + return self.backend.Transact( + address.Hex(), + HashRegAddr, + "", "", "", "", + setOwnerAbi, + ) +} + +// registers some content hash to a key/code hash +// e.g., the contract Info combined Json Doc's ContentHash +// to CodeHash of a contract or hash of a domain +func (self *Registrar) SetHashToHash(address common.Address, codehash, dochash common.Hash) (txh string, err error) { + _, err = self.SetOwner(address) + if err != nil { + return + } + codehex := common.Bytes2Hex(codehash[:]) + dochex := common.Bytes2Hex(dochash[:]) + + data := registerContentHashAbi + codehex + dochex + glog.V(logger.Detail).Infof("SetHashToHash data: %s sent to %v\n", data, HashRegAddr) + return self.backend.Transact( + address.Hex(), + HashRegAddr, + "", "", "", "", + data, + ) +} + +// SetUrlToHash(from, hash, url) registers a url to a content hash so that the content can be fetched +// address is used as sender for the transaction and will be the owner of a new +// registry entry on first time use +// FIXME: silently doing nothing if sender is not the owner +// note that with content addressed storage, this step is no longer necessary +func (self *Registrar) SetUrlToHash(address common.Address, hash common.Hash, url string) (txh string, err error) { + hashHex := common.Bytes2Hex(hash[:]) + var urlHex string + urlb := []byte(url) + var cnt byte + n := len(urlb) + + for n > 0 { + if n > 32 { + n = 32 + } + urlHex = common.Bytes2Hex(urlb[:n]) + urlb = urlb[n:] + n = len(urlb) + bcnt := make([]byte, 32) + bcnt[31] = cnt + data := registerUrlAbi + + hashHex + + common.Bytes2Hex(bcnt) + + common.Bytes2Hex(common.Hex2BytesFixed(urlHex, 32)) + txh, err = self.backend.Transact( + address.Hex(), + UrlHintAddr, + "", "", "", "", + data, + ) + if err != nil { + return + } + cnt++ + } + return +} + +// HashToHash(key) resolves contenthash for key (a hash) using HashReg +// resolution is costless non-transactional +// implemented as direct retrieval from db +func (self *Registrar) HashToHash(khash common.Hash) (chash common.Hash, err error) { + // look up in hashReg + at := HashRegAddr[2:] + key := storageAddress(storageMapping(storageIdx2Addr(1), khash[:])) + hash := self.backend.StorageAt(at, key) + + if hash == "0x0" || len(hash) < 3 || (hash == common.Hash{}.Hex()) { + err = fmt.Errorf("content hash not found for '%v'", khash.Hex()) + return + } + copy(chash[:], common.Hex2BytesFixed(hash[2:], 32)) + return +} + +// HashToUrl(contenthash) resolves the url for contenthash using UrlHint +// resolution is costless non-transactional +// implemented as direct retrieval from db +// if we use content addressed storage, this step is no longer necessary +func (self *Registrar) HashToUrl(chash common.Hash) (uri string, err error) { + // look up in URL reg + var str string = " " + var idx uint32 + for len(str) > 0 { + mapaddr := storageMapping(storageIdx2Addr(1), chash[:]) + key := storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(idx))) + hex := self.backend.StorageAt(UrlHintAddr[2:], key) + str = string(common.Hex2Bytes(hex[2:])) + l := 0 + for (l < len(str)) && (str[l] == 0) { + l++ + } + + str = str[l:] + uri = uri + str + idx++ + } + + if len(uri) == 0 { + err = fmt.Errorf("GetURLhint: URL hint not found for '%v'", chash.Hex()) + } + return +} + +func storageIdx2Addr(varidx uint32) []byte { + data := make([]byte, 32) + binary.BigEndian.PutUint32(data[28:32], varidx) + return data +} + +func storageMapping(addr, key []byte) []byte { + data := make([]byte, 64) + copy(data[0:32], key[0:32]) + copy(data[32:64], addr[0:32]) + sha := crypto.Sha3(data) + return sha +} + +func storageFixedArray(addr, idx []byte) []byte { + var carry byte + for i := 31; i >= 0; i-- { + var b byte = addr[i] + idx[i] + carry + if b < addr[i] { + carry = 1 + } else { + carry = 0 + } + addr[i] = b + } + return addr +} + +func storageAddress(addr []byte) string { + return common.ToHex(addr) +} + +func encodeAddress(address common.Address) string { + return addressAbiPrefix + address.Hex()[2:] +} + +func encodeName(name string, index uint8) (string, string) { + extra := common.Bytes2Hex([]byte(name)) + if len(name) > 32 { + return fmt.Sprintf("%064x", index), extra + } + return extra + falseHex[len(extra):], "" +} diff --git a/common/resolver/resolver_test.go b/common/registrar/registrar_test.go similarity index 61% rename from common/resolver/resolver_test.go rename to common/registrar/registrar_test.go index 02d12592e5..7561e424e2 100644 --- a/common/resolver/resolver_test.go +++ b/common/registrar/registrar_test.go @@ -1,4 +1,4 @@ -package resolver +package registrar import ( "testing" @@ -20,22 +20,22 @@ var ( ) func NewTestBackend() *testBackend { - HashRegContractAddress = common.BigToAddress(common.Big0).Hex()[2:] - UrlHintContractAddress = common.BigToAddress(common.Big1).Hex()[2:] + HashRegAddr = common.BigToAddress(common.Big0).Hex() //[2:] + UrlHintAddr = common.BigToAddress(common.Big1).Hex() //[2:] self := &testBackend{} self.contracts = make(map[string](map[string]string)) - self.contracts[HashRegContractAddress] = make(map[string]string) + self.contracts[HashRegAddr[2:]] = make(map[string]string) key := storageAddress(storageMapping(storageIdx2Addr(1), codehash[:])) - self.contracts[HashRegContractAddress][key] = hash.Hex() + self.contracts[HashRegAddr[2:]][key] = hash.Hex() - self.contracts[UrlHintContractAddress] = make(map[string]string) + self.contracts[UrlHintAddr[2:]] = make(map[string]string) mapaddr := storageMapping(storageIdx2Addr(1), hash[:]) key = storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(0))) - self.contracts[UrlHintContractAddress][key] = common.ToHex([]byte(url)) + self.contracts[UrlHintAddr[2:]][key] = common.ToHex([]byte(url)) key = storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(1))) - self.contracts[UrlHintContractAddress][key] = "0x00" + self.contracts[UrlHintAddr[2:]][key] = "0x0" return self } @@ -52,11 +52,25 @@ func (self *testBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, ga return "", nil } -func TestKeyToContentHash(t *testing.T) { +func (self *testBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error) { + return "", "", nil +} + +func TestSetGlobalRegistrar(t *testing.T) { b := NewTestBackend() res := New(b) + _, err := res.SetGlobalRegistrar("addresshex", common.BigToAddress(common.Big1)) + if err != nil { + t.Errorf("unexpected error: %v'", err) + } +} - got, err := res.KeyToContentHash(codehash) +func TestHashToHash(t *testing.T) { + b := NewTestBackend() + res := New(b) + // res.SetHashReg() + + got, err := res.HashToHash(codehash) if err != nil { t.Errorf("expected no error, got %v", err) } else { @@ -66,28 +80,17 @@ func TestKeyToContentHash(t *testing.T) { } } -func TestContentHashToUrl(t *testing.T) { +func TestHashToUrl(t *testing.T) { b := NewTestBackend() res := New(b) - got, err := res.ContentHashToUrl(hash) + // res.SetUrlHint() + + got, err := res.HashToUrl(hash) if err != nil { - t.Errorf("expected no error, got %v", err) + t.Errorf("expected error, got %v", err) } else { if got != url { t.Errorf("incorrect result, expected '%v', got '%s'", url, got) } } } - -func TestKeyToUrl(t *testing.T) { - b := NewTestBackend() - res := New(b) - got, _, err := res.KeyToUrl(codehash) - if err != nil { - t.Errorf("expected no error, got %v", err) - } else { - if got != url { - t.Errorf("incorrect result, expected \n'%s', got \n'%s'", url, got) - } - } -} diff --git a/common/resolver/contracts.go b/common/resolver/contracts.go deleted file mode 100644 index 4aad95e436..0000000000 --- a/common/resolver/contracts.go +++ /dev/null @@ -1,36 +0,0 @@ -package resolver - -const ( // built-in contracts address and code - ContractCodeURLhint = "0x60c180600c6000396000f30060003560e060020a90048063300a3bbf14601557005b6024600435602435604435602a565b60006000f35b6000600084815260200190815260200160002054600160a060020a0316600014806078575033600160a060020a03166000600085815260200190815260200160002054600160a060020a0316145b607f5760bc565b336000600085815260200190815260200160002081905550806001600085815260200190815260200160002083610100811060b657005b01819055505b50505056" - /* - contract URLhint { - function register(uint256 _hash, uint8 idx, uint256 _url) { - if (owner[_hash] == 0 || owner[_hash] == msg.sender) { - owner[_hash] = msg.sender; - url[_hash][idx] = _url; - } - } - mapping (uint256 => address) owner; - mapping (uint256 => uint256[256]) url; - } - */ - - ContractCodeHashReg = "0x609880600c6000396000f30060003560e060020a9004806331e12c2014601f578063d66d6c1014602b57005b6025603d565b60006000f35b6037600435602435605d565b60006000f35b600054600160a060020a0316600014605357605b565b336000819055505b565b600054600160a060020a031633600160a060020a031614607b576094565b8060016000848152602001908152602001600020819055505b505056" - /* - contract HashReg { - function setowner() { - if (owner == 0) { - owner = msg.sender; - } - } - function register(uint256 _key, uint256 _content) { - if (msg.sender == owner) { - content[_key] = _content; - } - } - address owner; - mapping (uint256 => uint256) content; - } - */ - -) diff --git a/common/resolver/resolver.go b/common/resolver/resolver.go deleted file mode 100644 index 9016547e10..0000000000 --- a/common/resolver/resolver.go +++ /dev/null @@ -1,232 +0,0 @@ -package resolver - -import ( - "encoding/binary" - "fmt" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" -) - -/* -Resolver implements the Ethereum DNS mapping -HashReg : Key Hash (hash of domain name or contract code) -> Content Hash -UrlHint : Content Hash -> Url Hint - -The resolver is meant to be called by the roundtripper transport implementation -of a url scheme -*/ - -// // contract addresses will be hardcoded after they're created -var UrlHintContractAddress, HashRegContractAddress string - -const ( - txValue = "0" - txGas = "100000" - txGasPrice = "1000000000000" -) - -func abi(s string) string { - return common.ToHex(crypto.Sha3([]byte(s))[:4]) -} - -var ( - registerContentHashAbi = abi("register(uint256,uint256)") - registerUrlAbi = abi("register(uint256,uint8,uint256)") - setOwnerAbi = abi("setowner()") -) - -type Backend interface { - StorageAt(string, string) string - Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) -} - -type Resolver struct { - backend Backend -} - -func New(eth Backend) *Resolver { - return &Resolver{eth} -} - -// for testing and play temporarily -// ideally the HashReg and UrlHint contracts should be in the genesis block -// if we got build-in support for natspec/contract info -// there should be only one of these officially endorsed -// addresses as constants -// TODO: could get around this with namereg, check -func (self *Resolver) CreateContracts(addr common.Address) (err error) { - HashRegContractAddress, err = self.backend.Transact(addr.Hex(), "", "", txValue, txGas, txGasPrice, ContractCodeHashReg) - if err != nil { - return - } - UrlHintContractAddress, err = self.backend.Transact(addr.Hex(), "", "", txValue, txGas, txGasPrice, ContractCodeURLhint) - glog.V(logger.Detail).Infof("HashReg @ %v\nUrlHint @ %v\n", HashRegContractAddress, UrlHintContractAddress) - return -} - -// called as first step in the registration process on HashReg -func (self *Resolver) SetOwner(address common.Address) (txh string, err error) { - return self.backend.Transact( - address.Hex(), - HashRegContractAddress, - "", txValue, txGas, txGasPrice, - setOwnerAbi, - ) -} - -// registers some content hash to a key/code hash -// e.g., the contract Info combined Json Doc's ContentHash -// to CodeHash of a contract or hash of a domain -// kept -func (self *Resolver) RegisterContentHash(address common.Address, codehash, dochash common.Hash) (txh string, err error) { - _, err = self.SetOwner(address) - if err != nil { - return - } - codehex := common.Bytes2Hex(codehash[:]) - dochex := common.Bytes2Hex(dochash[:]) - - data := registerContentHashAbi + codehex + dochex - return self.backend.Transact( - address.Hex(), - HashRegContractAddress, - "", txValue, txGas, txGasPrice, - data, - ) -} - -// registers a url to a content hash so that the content can be fetched -// address is used as sender for the transaction and will be the owner of a new -// registry entry on first time use -// FIXME: silently doing nothing if sender is not the owner -// note that with content addressed storage, this step is no longer necessary -// it could be purely -func (self *Resolver) RegisterUrl(address common.Address, hash common.Hash, url string) (txh string, err error) { - hashHex := common.Bytes2Hex(hash[:]) - var urlHex string - urlb := []byte(url) - var cnt byte - n := len(urlb) - - for n > 0 { - if n > 32 { - n = 32 - } - urlHex = common.Bytes2Hex(urlb[:n]) - urlb = urlb[n:] - n = len(urlb) - bcnt := make([]byte, 32) - bcnt[31] = cnt - data := registerUrlAbi + - hashHex + - common.Bytes2Hex(bcnt) + - common.Bytes2Hex(common.Hex2BytesFixed(urlHex, 32)) - txh, err = self.backend.Transact( - address.Hex(), - UrlHintContractAddress, - "", txValue, txGas, txGasPrice, - data, - ) - if err != nil { - return - } - cnt++ - } - return -} - -func (self *Resolver) Register(address common.Address, codehash, dochash common.Hash, url string) (txh string, err error) { - - _, err = self.RegisterContentHash(address, codehash, dochash) - if err != nil { - return - } - return self.RegisterUrl(address, dochash, url) -} - -// resolution is costless non-transactional -// implemented as direct retrieval from db -func (self *Resolver) KeyToContentHash(khash common.Hash) (chash common.Hash, err error) { - // look up in hashReg - at := common.Bytes2Hex(common.FromHex(HashRegContractAddress)) - key := storageAddress(storageMapping(storageIdx2Addr(1), khash[:])) - hash := self.backend.StorageAt(at, key) - - if hash == "0x0" || len(hash) < 3 { - err = fmt.Errorf("content hash not found for '%v'", khash.Hex()) - return - } - copy(chash[:], common.Hex2BytesFixed(hash[2:], 32)) - return -} - -// retrieves the url-hint for the content hash - -// if we use content addressed storage, this step is no longer necessary -func (self *Resolver) ContentHashToUrl(chash common.Hash) (uri string, err error) { - // look up in URL reg - var str string = " " - var idx uint32 - for len(str) > 0 { - mapaddr := storageMapping(storageIdx2Addr(1), chash[:]) - key := storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(idx))) - hex := self.backend.StorageAt(UrlHintContractAddress, key) - str = string(common.Hex2Bytes(hex[2:])) - l := len(str) - for (l > 0) && (str[l-1] == 0) { - l-- - } - str = str[:l] - uri = uri + str - idx++ - } - - if len(uri) == 0 { - err = fmt.Errorf("GetURLhint: URL hint not found for '%v'", chash.Hex()) - } - return -} - -func (self *Resolver) KeyToUrl(key common.Hash) (uri string, hash common.Hash, err error) { - // look up in urlHint - hash, err = self.KeyToContentHash(key) - if err != nil { - return - } - uri, err = self.ContentHashToUrl(hash) - return -} - -func storageIdx2Addr(varidx uint32) []byte { - data := make([]byte, 32) - binary.BigEndian.PutUint32(data[28:32], varidx) - return data -} - -func storageMapping(addr, key []byte) []byte { - data := make([]byte, 64) - copy(data[0:32], key[0:32]) - copy(data[32:64], addr[0:32]) - sha := crypto.Sha3(data) - return sha -} - -func storageFixedArray(addr, idx []byte) []byte { - var carry byte - for i := 31; i >= 0; i-- { - var b byte = addr[i] + idx[i] + carry - if b < addr[i] { - carry = 1 - } else { - carry = 0 - } - addr[i] = b - } - return addr -} - -func storageAddress(addr []byte) string { - return common.ToHex(addr) -} diff --git a/eth/backend.go b/eth/backend.go index e62252b6cd..38e06bcf81 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -70,6 +70,7 @@ type Config struct { VmDebug bool NatSpec bool AutoDAG bool + PowTest bool MaxPeers int MaxPendingPeers int @@ -221,6 +222,7 @@ type Ethereum struct { NatSpec bool DataDir string AutoDAG bool + PowTest bool autodagquit chan bool etherbase common.Address clientVersion string @@ -329,6 +331,7 @@ func New(config *Config) (*Ethereum, error) { MinerThreads: config.MinerThreads, SolcPath: config.SolcPath, AutoDAG: config.AutoDAG, + PowTest: config.PowTest, GpoMinGasPrice: config.GpoMinGasPrice, GpoMaxGasPrice: config.GpoMaxGasPrice, GpoFullBlockRatio: config.GpoFullBlockRatio, @@ -337,7 +340,15 @@ func New(config *Config) (*Ethereum, error) { GpobaseCorrectionFactor: config.GpobaseCorrectionFactor, } - eth.pow = ethash.New() + if config.PowTest { + glog.V(logger.Info).Infof("ethash used in test mode") + eth.pow, err = ethash.NewForTesting() + if err != nil { + return nil, err + } + } else { + eth.pow = ethash.New() + } genesis := core.GenesisBlock(uint64(config.GenesisNonce), stateDb) eth.chainManager, err = core.NewChainManager(genesis, blockDb, stateDb, extraDb, eth.pow, eth.EventMux()) if err != nil { diff --git a/jsre/pp_js.go b/jsre/pp_js.go index 20821e4a1b..735132bb74 100644 --- a/jsre/pp_js.go +++ b/jsre/pp_js.go @@ -97,7 +97,15 @@ var isMemberFunction = function(object, member) { } var isBigNumber = function (object) { - return typeof BigNumber !== 'undefined' && object instanceof BigNumber; + var result = typeof BigNumber !== 'undefined' && object instanceof BigNumber; + + if (!result) { + if(typeof(object) === "object") { + result = object.constructor.toString().indexOf("function BigNumber(") == 0; + } + } + + return result }; function prettyPrint(/* */) { diff --git a/rpc/api/admin.go b/rpc/api/admin.go index b27482cfea..c5f0260906 100644 --- a/rpc/api/admin.go +++ b/rpc/api/admin.go @@ -3,10 +3,18 @@ package api import ( "fmt" "io" + "math/big" "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" @@ -24,17 +32,29 @@ 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, + "admin_sleepBlocks": (*adminApi).SleepBlocks, + "admin_sleep": (*adminApi).Sleep, } ) @@ -47,6 +67,7 @@ type adminApi struct { ethereum *eth.Ethereum codec codec.Codec coder codec.ApiCoder + ds *docserver.DocServer } // create a new admin api instance @@ -56,6 +77,7 @@ func NewAdminApi(xeth *xeth.XEth, ethereum *eth.Ethereum, codec codec.Codec) *ad ethereum: ethereum, codec: codec, coder: codec.New(nil), + ds: docserver.New("/"), } } @@ -244,3 +266,195 @@ func (self *adminApi) StopRPC(req *shared.Request) (interface{}, error) { comms.StopHttp() return true, nil } + +func (self *adminApi) SleepBlocks(req *shared.Request) (interface{}, error) { + args := new(SleepBlocksArgs) + if err := self.coder.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + var timer <-chan time.Time + var height *big.Int + var err error + if args.Timeout > 0 { + timer = time.NewTimer(time.Duration(args.Timeout) * time.Second).C + } + + height = new(big.Int).Add(self.xeth.CurrentBlock().Number(), big.NewInt(args.N)) + height, err = sleepBlocks(self.xeth.UpdateState(), height, timer) + if err != nil { + return nil, err + } + return height.Uint64(), nil +} + +func sleepBlocks(wait chan *big.Int, height *big.Int, timer <-chan time.Time) (newHeight *big.Int, err error) { + wait <- height + select { + case <-timer: + // if times out make sure the xeth loop does not block + go func() { + select { + case wait <- nil: + case <-wait: + } + }() + return nil, fmt.Errorf("timeout") + case newHeight = <-wait: + } + return +} + +func (self *adminApi) Sleep(req *shared.Request) (interface{}, error) { + args := new(SleepArgs) + if err := self.coder.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + time.Sleep(time.Duration(args.S) * time.Second) + return nil, nil +} + +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) + txhash, err := reg.SetGlobalRegistrar(args.NameReg, sender) + if err != nil { + return false, err + } + + return txhash, 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) + txhash, err := reg.SetHashReg(args.HashReg, sender) + if err != nil { + return false, err + } + + return txhash, 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) + txhash, err := reg.SetUrlHint(urlHint, sender) + if err != nil { + return nil, err + } + + return txhash, 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 5437971caa..5329079056 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" ) @@ -113,7 +114,7 @@ func (args *StartRPCArgs) UnmarshalJSON(b []byte) (err error) { args.ListenPort = 8545 args.Apis = "net,eth,web3" - if len(obj) >= 1 { + if len(obj) >= 1 && obj[0] != nil { if addr, ok := obj[0].(string); ok { args.ListenAddress = addr } else { @@ -121,7 +122,7 @@ func (args *StartRPCArgs) UnmarshalJSON(b []byte) (err error) { } } - if len(obj) >= 2 { + if len(obj) >= 2 && obj[1] != nil { if port, ok := obj[1].(float64); ok && port >= 0 && port <= 64*1024 { args.ListenPort = uint(port) } else { @@ -129,7 +130,7 @@ func (args *StartRPCArgs) UnmarshalJSON(b []byte) (err error) { } } - if len(obj) >= 3 { + if len(obj) >= 3 && obj[2] != nil { if corsDomain, ok := obj[2].(string); ok { args.CorsDomain = corsDomain } else { @@ -137,7 +138,7 @@ func (args *StartRPCArgs) UnmarshalJSON(b []byte) (err error) { } } - if len(obj) >= 4 { + if len(obj) >= 4 && obj[3] != nil { if apis, ok := obj[3].(string); ok { args.Apis = apis } else { @@ -147,3 +148,327 @@ func (args *StartRPCArgs) UnmarshalJSON(b []byte) (err error) { return nil } + +type SleepArgs struct { + S int +} + +func (args *SleepArgs) 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 obj[0] != nil { + if n, err := numString(obj[0]); err == nil { + args.S = int(n.Int64()) + } else { + return shared.NewInvalidTypeError("N", "not an integer: "+err.Error()) + } + } else { + return shared.NewInsufficientParamsError(0, 1) + } + } + return nil +} + +type SleepBlocksArgs struct { + N int64 + Timeout int64 +} + +func (args *SleepBlocksArgs) UnmarshalJSON(b []byte) (err error) { + + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + + args.N = 1 + args.Timeout = 0 + if len(obj) >= 1 && obj[0] != nil { + if n, err := numString(obj[0]); err == nil { + args.N = n.Int64() + } else { + return shared.NewInvalidTypeError("N", "not an integer: "+err.Error()) + } + } + + if len(obj) >= 2 && obj[1] != nil { + if n, err := numString(obj[1]); err == nil { + args.Timeout = n.Int64() + } else { + return shared.NewInvalidTypeError("Timeout", "not an integer: "+err.Error()) + } + } + + 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 && obj[1] != nil { + 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 && obj[0] != nil { + if hashreg, ok := obj[0].(string); ok { + args.HashReg = hashreg + } else { + return shared.NewInvalidTypeError("HashReg", "not a string") + } + } + + if len(obj) >= 2 && obj[1] != nil { + 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 && obj[0] != nil { + if urlhint, ok := obj[0].(string); ok { + args.UrlHint = urlhint + } else { + return shared.NewInvalidTypeError("UrlHint", "not a string") + } + } + + if len(obj) >= 2 && obj[1] != nil { + 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[0].(string); ok { + args.Sender = sender + } else { + return shared.NewInvalidTypeError("Sender", "not a string") + } + } + + if len(obj) >= 2 { + 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("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 && obj[1] != nil { + 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..ddfa2ea04f 100644 --- a/rpc/api/admin_js.go +++ b/rpc/api/admin_js.go @@ -9,73 +9,128 @@ web3._extend({ name: 'addPeer', call: 'admin_addPeer', params: 1, - inputFormatter: [web3._extend.utils.formatInputString], - outputFormatter: web3._extend.formatters.formatOutputBool + inputFormatter: [null] }), new web3._extend.Method({ name: 'exportChain', call: 'admin_exportChain', params: 1, - inputFormatter: [web3._extend.utils.formatInputString], - outputFormatter: function(obj) { return obj; } + inputFormatter: [null] }), new web3._extend.Method({ name: 'importChain', call: 'admin_importChain', params: 1, - inputFormatter: [web3._extend.utils.formatInputString], - outputFormatter: function(obj) { return obj; } + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'sleepBlocks', + call: 'admin_sleepBlocks', + params: 2, + inputFormatter: [null, null] }), new web3._extend.Method({ name: 'verbosity', call: 'admin_verbosity', params: 1, - inputFormatter: [web3._extend.utils.formatInputInt], - outputFormatter: web3._extend.formatters.formatOutputBool + inputFormatter: [web3._extend.utils.fromDecimal] }), new web3._extend.Method({ name: 'setSolc', call: 'admin_setSolc', params: 1, - inputFormatter: [web3._extend.utils.formatInputString], - outputFormatter: web3._extend.formatters.formatOutputString + inputFormatter: [null] }), new web3._extend.Method({ name: 'startRPC', call: 'admin_startRPC', params: 4, - inputFormatter: [web3._extend.utils.formatInputString,web3._extend.utils.formatInputInteger,web3._extend.utils.formatInputString,web3._extend.utils.formatInputString], - outputFormatter: web3._extend.formatters.formatOutputBool + inputFormatter: [null, null, null, null] }), new web3._extend.Method({ name: 'stopRPC', call: 'admin_stopRPC', params: 0, - inputFormatter: [], - outputFormatter: web3._extend.formatters.formatOutputBool + inputFormatter: [] + }), + new web3._extend.Method({ + name: 'setGlobalRegistrar', + call: 'admin_setGlobalRegistrar', + params: 2, + inputFormatter: [null,null] + }), + new web3._extend.Method({ + name: 'setHashReg', + call: 'admin_setHashReg', + params: 2, + inputFormatter: [null,null] + }), + new web3._extend.Method({ + name: 'setUrlHint', + call: 'admin_setUrlHint', + params: 2, + inputFormatter: [null,null] + }), + new web3._extend.Method({ + name: 'saveInfo', + call: 'admin_saveInfo', + params: 2, + inputFormatter: [null,null] + }), + new web3._extend.Method({ + name: 'register', + call: 'admin_register', + params: 3, + inputFormatter: [null,null,null] + }), + new web3._extend.Method({ + name: 'registerUrl', + call: 'admin_registerUrl', + params: 3, + inputFormatter: [null,null,null] + }), + new web3._extend.Method({ + name: 'startNatSpec', + call: 'admin_startNatSpec', + params: 0, + inputFormatter: [] + }), + new web3._extend.Method({ + name: 'stopNatSpec', + call: 'admin_stopNatSpec', + params: 0, + inputFormatter: [] + }), + new web3._extend.Method({ + name: 'getContractInfo', + call: 'admin_getContractInfo', + params: 1, + inputFormatter: [null], + }), + new web3._extend.Method({ + name: 'httpGet', + call: 'admin_httpGet', + params: 2, + inputFormatter: [null, null] }) ], properties: [ new web3._extend.Property({ name: 'nodeInfo', - getter: 'admin_nodeInfo', - outputFormatter: web3._extend.formatters.formatOutputString + getter: 'admin_nodeInfo' }), new web3._extend.Property({ name: 'peers', - getter: 'admin_peers', - outputFormatter: function(obj) { return obj; } + getter: 'admin_peers' }), new web3._extend.Property({ name: 'datadir', - getter: 'admin_datadir', - outputFormatter: web3._extend.formatters.formatOutputString + getter: 'admin_datadir' }), new web3._extend.Property({ name: 'chainSyncStatus', - getter: 'admin_chainSyncStatus', - outputFormatter: function(obj) { return obj; } + getter: 'admin_chainSyncStatus' }) ] }); diff --git a/rpc/api/db_js.go b/rpc/api/db_js.go index 62cdcd20ee..91dc95e5b5 100644 --- a/rpc/api/db_js.go +++ b/rpc/api/db_js.go @@ -5,34 +5,6 @@ web3._extend({ property: 'db', methods: [ - new web3._extend.Method({ - name: 'getString', - call: 'db_getString', - params: 2, - inputFormatter: [web3._extend.formatters.formatInputString, web3._extend.formatters.formatInputString], - outputFormatter: web3._extend.formatters.formatOutputString - }), - new web3._extend.Method({ - name: 'putString', - call: 'db_putString', - params: 3, - inputFormatter: [web3._extend.formatters.formatInputString, web3._extend.formatters.formatInputString, web3._extend.formatters.formatInputString], - outputFormatter: web3._extend.formatters.formatOutputBool - }), - new web3._extend.Method({ - name: 'getHex', - call: 'db_getHex', - params: 2, - inputFormatter: [web3._extend.formatters.formatInputString, web3._extend.formatters.formatInputString], - outputFormatter: web3._extend.formatters.formatOutputString - }), - new web3._extend.Method({ - name: 'putHex', - call: 'db_putHex', - params: 3, - inputFormatter: [web3._extend.formatters.formatInputString, web3._extend.formatters.formatInputString, web3._extend.formatters.formatInputString], - outputFormatter: web3._extend.formatters.formatOutputBool - }), ], properties: [ diff --git a/rpc/api/debug_js.go b/rpc/api/debug_js.go index bd3a6dfb24..93fba537e3 100644 --- a/rpc/api/debug_js.go +++ b/rpc/api/debug_js.go @@ -9,50 +9,43 @@ web3._extend({ name: 'printBlock', call: 'debug_printBlock', params: 1, - inputFormatter: [web3._extend.formatters.formatInputInt], - outputFormatter: web3._extend.formatters.formatOutputString + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter] }), new web3._extend.Method({ name: 'getBlockRlp', call: 'debug_getBlockRlp', params: 1, - inputFormatter: [web3._extend.formatters.formatInputInt], - outputFormatter: web3._extend.formatters.formatOutputString + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter] }), new web3._extend.Method({ name: 'setHead', call: 'debug_setHead', params: 1, - inputFormatter: [web3._extend.formatters.formatInputInt], - outputFormatter: web3._extend.formatters.formatOutputBool + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter] }), new web3._extend.Method({ name: 'processBlock', call: 'debug_processBlock', params: 1, - inputFormatter: [web3._extend.formatters.formatInputInt], - outputFormatter: function(obj) { return obj; } + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter] }), new web3._extend.Method({ name: 'seedHash', call: 'debug_seedHash', params: 1, - inputFormatter: [web3._extend.formatters.formatInputInt], - outputFormatter: web3._extend.formatters.formatOutputString - }) , + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter] + }), new web3._extend.Method({ name: 'dumpBlock', call: 'debug_dumpBlock', params: 1, - inputFormatter: [web3._extend.formatters.formatInputInt], - outputFormatter: function(obj) { return obj; } + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter] }), new web3._extend.Method({ name: 'metrics', call: 'debug_metrics', params: 1, - inputFormatter: [web3._extend.formatters.formatInputBool], - outputFormatter: function(obj) { return obj; } + inputFormatter: [null] }) ], properties: diff --git a/rpc/api/eth_js.go b/rpc/api/eth_js.go index 4512cc147f..400eb8e895 100644 --- a/rpc/api/eth_js.go +++ b/rpc/api/eth_js.go @@ -12,23 +12,20 @@ web3._extend({ name: 'sign', call: 'eth_sign', params: 2, - inputFormatter: [web3._extend.formatters.formatInputString,web3._extend.formatters.formatInputString], - outputFormatter: web3._extend.formatters.formatOutputString + inputFormatter: [web3._extend.utils.toAddress, null] }), new web3._extend.Method({ name: 'resend', call: 'eth_resend', params: 3, - inputFormatter: [function(obj) { return obj; },web3._extend.formatters.formatInputString,web3._extend.formatters.formatInputString], - outputFormatter: web3._extend.formatters.formatOutputString + inputFormatter: [web3._extend.formatters.inputTransactionFormatter, web3._extend.utils.fromDecimal, web3._extend.utils.fromDecimal] }) ], properties: [ new web3._extend.Property({ name: 'pendingTransactions', - getter: 'eth_pendingTransactions', - outputFormatter: function(obj) { return obj; } + getter: 'eth_pendingTransactions' }) ] }); diff --git a/rpc/api/miner_js.go b/rpc/api/miner_js.go index 6290368da3..8861a177ae 100644 --- a/rpc/api/miner_js.go +++ b/rpc/api/miner_js.go @@ -9,50 +9,43 @@ web3._extend({ name: 'start', call: 'miner_start', params: 1, - inputFormatter: [web3._extend.formatters.formatInputInt], - outputFormatter: web3._extend.formatters.formatOutputBool + inputFormatter: [null] }), new web3._extend.Method({ name: 'stop', call: 'miner_stop', params: 1, - inputFormatter: [web3._extend.formatters.formatInputInt], - outputFormatter: web3._extend.formatters.formatOutputBool + inputFormatter: [null] }), new web3._extend.Method({ name: 'setExtra', call: 'miner_setExtra', params: 1, - inputFormatter: [web3._extend.utils.formatInputString], - outputFormatter: web3._extend.formatters.formatOutputBool + inputFormatter: [null] }), new web3._extend.Method({ name: 'setGasPrice', call: 'miner_setGasPrice', params: 1, - inputFormatter: [web3._extend.utils.formatInputString], - outputFormatter: web3._extend.formatters.formatOutputBool + inputFormatter: [web3._extend.utils.fromDecial] }), new web3._extend.Method({ name: 'startAutoDAG', call: 'miner_startAutoDAG', params: 0, - inputFormatter: [], - outputFormatter: web3._extend.formatters.formatOutputBool + inputFormatter: [] }), new web3._extend.Method({ name: 'stopAutoDAG', call: 'miner_stopAutoDAG', params: 0, - inputFormatter: [], - outputFormatter: web3._extend.formatters.formatOutputBool + inputFormatter: [] }), new web3._extend.Method({ name: 'makeDAG', call: 'miner_makeDAG', params: 1, - inputFormatter: [web3._extend.formatters.inputDefaultBlockNumberFormatter], - outputFormatter: web3._extend.formatters.formatOutputBool + inputFormatter: [web3._extend.formatters.inputDefaultBlockNumberFormatter] }) ], properties: diff --git a/rpc/api/net.go b/rpc/api/net.go index 7616546610..b3931ba2df 100644 --- a/rpc/api/net.go +++ b/rpc/api/net.go @@ -14,10 +14,8 @@ const ( var ( // mapping between methods and handlers netMapping = map[string]nethandler{ - "net_version": (*netApi).Version, "net_peerCount": (*netApi).PeerCount, "net_listening": (*netApi).IsListening, - "net_peers": (*netApi).Peers, } ) @@ -70,11 +68,6 @@ func (self *netApi) ApiVersion() string { return NetApiVersion } -// Network version -func (self *netApi) Version(req *shared.Request) (interface{}, error) { - return self.xeth.NetworkVersion(), nil -} - // Number of connected peers func (self *netApi) PeerCount(req *shared.Request) (interface{}, error) { return newHexNum(self.xeth.PeerCount()), nil @@ -84,6 +77,3 @@ func (self *netApi) IsListening(req *shared.Request) (interface{}, error) { return self.xeth.IsListening(), nil } -func (self *netApi) Peers(req *shared.Request) (interface{}, error) { - return self.ethereum.PeersInfo(), nil -} diff --git a/rpc/api/net_js.go b/rpc/api/net_js.go index 1677d9fa64..2f872393c9 100644 --- a/rpc/api/net_js.go +++ b/rpc/api/net_js.go @@ -2,46 +2,18 @@ package api const Net_JS = ` web3._extend({ - property: 'network', + property: 'net', methods: [ new web3._extend.Method({ name: 'addPeer', call: 'net_addPeer', params: 1, - inputFormatter: [web3._extend.utils.formatInputString], - outputFormatter: web3._extend.formatters.formatOutputBool - }), - new web3._extend.Method({ - name: 'getPeerCount', - call: 'net_peerCount', - params: 0, - inputFormatter: [], - outputFormatter: web3._extend.formatters.formatOutputString + inputFormatter: [null] }) ], properties: [ - new web3._extend.Property({ - name: 'listening', - getter: 'net_listening', - outputFormatter: web3._extend.formatters.formatOutputBool - }), - new web3._extend.Property({ - name: 'peerCount', - getter: 'net_peerCount', - outputFormatter: web3._extend.utils.toDecimal - }), - new web3._extend.Property({ - name: 'peers', - getter: 'net_peers', - outputFormatter: function(obj) { return obj; } - }), - new web3._extend.Property({ - name: 'version', - getter: 'net_version', - outputFormatter: web3._extend.formatters.formatOutputString - }) ] }); ` diff --git a/rpc/api/personal_js.go b/rpc/api/personal_js.go index 463a2c7f50..66014cc02d 100644 --- a/rpc/api/personal_js.go +++ b/rpc/api/personal_js.go @@ -9,23 +9,21 @@ web3._extend({ name: 'newAccount', call: 'personal_newAccount', params: 1, - inputFormatter: [web3._extend.formatters.formatInputString], - outputFormatter: web3._extend.formatters.formatOutputString + inputFormatter: [null], + outputFormatter: web3._extend.utils.toAddress }), new web3._extend.Method({ name: 'unlockAccount', call: 'personal_unlockAccount', params: 3, - inputFormatter: [web3._extend.formatters.formatInputString,web3._extend.formatters.formatInputString,web3._extend.formatters.formatInputInt], - outputFormatter: web3._extend.formatters.formatOutputBool + inputFormatter: [null, null, null] }) ], properties: [ new web3._extend.Property({ name: 'listAccounts', - getter: 'personal_listAccounts', - outputFormatter: function(obj) { return obj; } + getter: 'personal_listAccounts' }) ] }); diff --git a/rpc/api/ssh_js.go b/rpc/api/ssh_js.go index f401f70f19..c0591bd71d 100644 --- a/rpc/api/ssh_js.go +++ b/rpc/api/ssh_js.go @@ -5,25 +5,13 @@ web3._extend({ property: 'shh', methods: [ - new web3._extend.Method({ - name: 'post', - call: 'shh_post', - params: 6, - inputFormatter: [web3._extend.formatters.formatInputString, - web3._extend.formatters.formatInputString, - web3._extend.formatters.formatInputString, - , - , web3._extend.formatters.formatInputInt - , web3._extend.formatters.formatInputInt], - outputFormatter: web3._extend.formatters.formatOutputBool - }), + ], properties: [ new web3._extend.Property({ name: 'version', - getter: 'shh_version', - outputFormatter: web3._extend.formatters.formatOutputInt + getter: 'shh_version' }) ] }); diff --git a/rpc/api/utils.go b/rpc/api/utils.go index 54ca28774d..d64cfc7cfc 100644 --- a/rpc/api/utils.go +++ b/rpc/api/utils.go @@ -36,6 +36,7 @@ var ( "debug": []string{ "dumpBlock", "getBlockRlp", + "metrics", "printBlock", "processBlock", "seedHash", @@ -44,49 +45,38 @@ var ( "eth": []string{ "accounts", "blockNumber", - "getBalance", - "protocolVersion", - "coinbase", - "mining", - "gasPrice", - "getStorage", - "storageAt", - "getStorageAt", - "getTransactionCount", - "getBlockTransactionCountByHash", - "getBlockTransactionCountByNumber", - "getUncleCountByBlockHash", - "getUncleCountByBlockNumber", - "getData", - "getCode", - "sign", - "sendRawTransaction", - "sendTransaction", - "transact", - "estimateGas", "call", - "flush", - "getBlockByHash", - "getBlockByNumber", - "getTransactionByHash", - "getTransactionByBlockHashAndIndex", - "getUncleByBlockHashAndIndex", - "getUncleByBlockNumberAndIndex", + "contract", + "coinbase", + "compile.lll", + "compile.serpent", + "compile.solidity", + "contract", + "defaultAccount", + "defaultBlock", + "estimateGas", + "filter", + "getBalance", + "getBlock", + "getBlockTransactionCount", + "getBlockUncleCount", + "getCode", "getCompilers", - "compileSolidity", - "newFilter", - "newBlockFilter", - "newPendingTransactionFilter", - "uninstallFilter", - "getFilterChanges", - "getFilterLogs", - "getLogs", + "gasPrice", + "getStorageAt", + "getTransaction", + "getTransactionCount", + "getTransactionFromBlock", + "getTransactionReceipt", + "getUncle", "hashrate", - "getWork", - "submitWork", + "mining", + "namereg", "pendingTransactions", "resend", - "getTransactionReceipt", + "sendRawTransaction", + "sendTransaction", + "sign", }, "miner": []string{ "hashrate", @@ -109,13 +99,12 @@ var ( "unlockAccount", }, "shh": []string{ - "version", "post", + "newIdentify", "hasIdentity", - "newIdentity", - "newFilter", - "uninstallFilter", - "getFilterChanges", + "newGroup", + "addToGroup", + "filter", }, "txpool": []string{ "status", diff --git a/xeth/xeth.go b/xeth/xeth.go index f2295e6e15..a3923436af 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -203,34 +203,6 @@ func (self *XEth) AtStateNum(num int64) *XEth { return self.WithState(st) } -// applies queued transactions originating from address onto the latest state -// and creates a block -// only used in tests -// - could be removed in favour of mining on testdag (natspec e2e + networking) -// + filters -func (self *XEth) ApplyTestTxs(statedb *state.StateDB, address common.Address, txc uint64) (uint64, *XEth) { - chain := self.backend.ChainManager() - header := chain.CurrentBlock().Header() - coinbase := statedb.GetStateObject(address) - coinbase.SetGasLimit(big.NewInt(10000000)) - txs := self.backend.TxPool().GetQueuedTransactions() - - for i := 0; i < len(txs); i++ { - for _, tx := range txs { - if tx.Nonce() == txc { - _, _, err := core.ApplyMessage(core.NewEnv(statedb, self.backend.ChainManager(), tx, header), tx, coinbase) - if err != nil { - panic(err) - } - txc++ - } - } - } - - xeth := self.WithState(statedb) - return txc, xeth -} - func (self *XEth) WithState(statedb *state.StateDB) *XEth { xeth := &XEth{ backend: self.backend,