diff --git a/bzz/api.go b/bzz/api.go index 7065f76775..19f23621f4 100644 --- a/bzz/api.go +++ b/bzz/api.go @@ -5,7 +5,7 @@ import ( "bytes" "fmt" "io" - "net" + "math/big" "os" "os/exec" "path/filepath" @@ -14,15 +14,16 @@ import ( "sync" "github.com/ethereum/go-ethereum/common" - "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/p2p" "github.com/ethereum/go-ethereum/p2p/discover" ) var ( - hashMatcher = regexp.MustCompile("^[0-9A-Fa-f]{64}") - slashes = regexp.MustCompile("/+") + hashMatcher = regexp.MustCompile("^[0-9A-Fa-f]{64}") + slashes = regexp.MustCompile("/+") + domainAndVersion = regexp.MustCompile("[@:;,]+") ) /* @@ -31,11 +32,11 @@ on top of the dpa it is the public interface of the dpa which is included in the ethereum stack */ type Api struct { - Chunker *TreeChunker - Port string - Resolver *resolver.Resolver - dpa *DPA - netStore *netStore + Chunker *TreeChunker + Port string + Registrar registrar.VersionedRegistrar + dpa *DPA + netStore *netStore } /* @@ -366,12 +367,12 @@ func (self *Api) Upload(lpath, index string) (string, error) { return hs, err2 } -func (self *Api) Register(sender common.Address, hash common.Hash, domain string) (err error) { +func (self *Api) Register(sender common.Address, domain string, hash common.Hash) (err error) { domainhash := common.BytesToHash(crypto.Sha3([]byte(domain))) - if self.Resolver != nil { + if self.Registrar != nil { dpaLogger.Debugf("Swarm: host '%s' (hash: '%v') to be registered as '%v'", domain, domainhash.Hex(), hash.Hex()) - _, err = self.Resolver.RegisterContentHash(sender, domainhash, hash) + _, err = self.Registrar.Registry().SetHashToHash(sender, domainhash, hash) } else { err = fmt.Errorf("no registry: %v", err) } @@ -381,26 +382,21 @@ func (self *Api) Register(sender common.Address, hash common.Hash, domain string type errResolve error func (self *Api) Resolve(hostPort string) (contentHash Key, err error) { - var host string - host, _, err = net.SplitHostPort(hostPort) - if err != nil { - porterr := "missing port in address " + hostPort - if err.Error() == porterr { - host = hostPort - err = nil - } else { - err = fmt.Errorf("invalid host '%s': %v (<>'%s')", hostPort, err, porterr) - return - } - } + host := hostPort if hashMatcher.MatchString(host) { contentHash = Key(common.Hex2Bytes(host)) dpaLogger.Debugf("Swarm: host is a contentHash: '%064x'", contentHash) } else { - if self.Resolver != nil { - hostHash := common.BytesToHash(crypto.Sha3([]byte(host))) + if self.Registrar != nil { var hash common.Hash - hash, err = self.Resolver.KeyToContentHash(hostHash) + var version *big.Int + parts := domainAndVersion.Split(host, 3) + if len(parts) > 1 && parts[1] != "" { + host = parts[0] + version = common.Big(parts[1]) + } + hostHash := common.BytesToHash(crypto.Sha3([]byte(host))) + hash, err = self.Registrar.Resolver(version).HashToHash(hostHash) if err != nil { err = fmt.Errorf("unable to resolve '%s': %v", hostPort, err) } diff --git a/bzz/js_api.go b/bzz/js_api.go index a405d34153..ae5ca8d2f3 100644 --- a/bzz/js_api.go +++ b/bzz/js_api.go @@ -45,12 +45,12 @@ func (self *JSApi) register(call otto.FunctionCall) otto.Value { fmt.Println(err) return otto.UndefinedValue() } - contenthash, err = call.Argument(1).ToString() + domain, err = call.Argument(1).ToString() if err != nil { fmt.Println(err) return otto.UndefinedValue() } - domain, err = call.Argument(2).ToString() + contenthash, err = call.Argument(2).ToString() if err != nil { fmt.Println(err) return otto.UndefinedValue() @@ -58,7 +58,7 @@ func (self *JSApi) register(call otto.FunctionCall) otto.Value { hash := common.HexToHash(contenthash) - err = self.api.Register(common.HexToAddress(sender), hash, domain) + err = self.api.Register(common.HexToAddress(sender), domain, hash) if err != nil { fmt.Println(err) return otto.FalseValue() diff --git a/cmd/geth/admin.go b/cmd/geth/admin.go index e98a0a76b7..31fb182b68 100644 --- a/cmd/geth/admin.go +++ b/cmd/geth/admin.go @@ -14,7 +14,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/compiler" "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/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -50,6 +50,9 @@ func (js *jsre) adminBindings() { admin.Set("verbosity", js.verbosity) admin.Set("progress", js.downloadProgress) admin.Set("setSolc", js.setSolc) + admin.Set("setGlobalRegistrar", js.setGlobalRegistrar) + admin.Set("setHashReg", js.setHashReg) + admin.Set("setUrlHint", js.setUrlHint) admin.Set("contractInfo", struct{}{}) t, _ = admin.Get("contractInfo") @@ -57,7 +60,6 @@ func (js *jsre) adminBindings() { // newRegistry officially not documented temporary option cinfo.Set("start", js.startNatSpec) cinfo.Set("stop", js.stopNatSpec) - cinfo.Set("newRegistry", js.newRegistry) cinfo.Set("get", js.getContractInfo) cinfo.Set("saveInfo", js.saveInfo) cinfo.Set("register", js.register) @@ -173,6 +175,112 @@ func (js *jsre) pendingTransactions(call otto.FunctionCall) otto.Value { return v } +func (js *jsre) setGlobalRegistrar(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) == 0 { + fmt.Println("requires 1 or 2 arguments: admin.setGlobalRegistrar(sender[, contractaddress])") + return otto.UndefinedValue() + } + var namereg, addr string + var sender common.Address + var err error + if len(call.ArgumentList) > 0 { + namereg, err = call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + } + if len(call.ArgumentList) > 1 { + addr, err = call.Argument(1).ToString() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + sender = common.HexToAddress(addr) + } + reg := registrar.New(js.xeth) + err = reg.SetGlobalRegistrar(namereg, sender) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + v, _ := call.Otto.ToValue(registrar.GlobalRegistrarAddr) + return v +} + +func (js *jsre) setHashReg(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) > 2 { + fmt.Println("requires 0, 1 or 2 arguments: admin.setHashReg(sender[, contractaddress])") + return otto.UndefinedValue() + } + var hashreg, addr string + var sender common.Address + var err error + if len(call.ArgumentList) > 0 { + hashreg, err = call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + } + if len(call.ArgumentList) > 1 { + addr, err = call.Argument(1).ToString() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + sender = common.HexToAddress(addr) + } + + reg := registrar.New(js.xeth) + sender = common.HexToAddress(addr) + err = reg.SetHashReg(hashreg, sender) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + v, _ := call.Otto.ToValue(registrar.HashRegAddr) + return v +} + +func (js *jsre) setUrlHint(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) > 2 { + fmt.Println("requires 0, 1 or 2 arguments: admin.setHashReg(sender[, contractaddress])") + return otto.UndefinedValue() + } + var urlhint, addr string + var sender common.Address + var err error + if len(call.ArgumentList) > 0 { + urlhint, err = call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + } + if len(call.ArgumentList) > 1 { + addr, err = call.Argument(1).ToString() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + sender = common.HexToAddress(addr) + } + + reg := registrar.New(js.xeth) + sender = common.HexToAddress(addr) + err = reg.SetUrlHint(urlhint, sender) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + v, _ := call.Otto.ToValue(registrar.UrlHintAddr) + return v +} + func (js *jsre) resend(call otto.FunctionCall) otto.Value { if len(call.ArgumentList) == 0 { fmt.Println("first argument must be a transaction") @@ -760,9 +868,9 @@ func (js *jsre) register(call otto.FunctionCall) otto.Value { codeb := js.xeth.CodeAtBytes(address) codeHash := common.BytesToHash(crypto.Sha3(codeb)) contentHash := common.HexToHash(contentHashHex) - registry := resolver.New(js.xeth) + registry := registrar.New(js.xeth) - _, err = registry.RegisterContentHash(common.HexToAddress(sender), codeHash, contentHash) + _, err = registry.SetHashToHash(common.HexToAddress(sender), codeHash, contentHash) if err != nil { fmt.Println(err) return otto.FalseValue() @@ -832,8 +940,8 @@ func (js *jsre) registerUrl(call otto.FunctionCall) otto.Value { return otto.FalseValue() } - registry := resolver.New(js.xeth) - _, err = registry.RegisterUrl(common.HexToAddress(sender), common.HexToHash(contenthash), url) + registry := registrar.New(js.xeth) + _, err = registry.SetUrlToHash(common.HexToAddress(sender), common.HexToHash(contenthash), url) if err != nil { fmt.Println(err) return otto.FalseValue() @@ -858,7 +966,7 @@ func (js *jsre) getContractInfo(call otto.FunctionCall) otto.Value { fmt.Println(err) return otto.UndefinedValue() } - var info compiler.ContractInfo + var info interface{} err = json.Unmarshal(infoDoc, &info) if err != nil { fmt.Println(err) @@ -878,29 +986,6 @@ func (js *jsre) stopNatSpec(call otto.FunctionCall) otto.Value { return otto.TrueValue() } -func (js *jsre) newRegistry(call otto.FunctionCall) otto.Value { - - if len(call.ArgumentList) != 1 { - fmt.Println("requires 1 argument: admin.contractInfo.newRegistry(adminaddress)") - return otto.UndefinedValue() - } - addr, err := call.Argument(0).ToString() - if err != nil { - fmt.Println(err) - return otto.UndefinedValue() - } - var hashReg, urlHint string - registry := resolver.New(js.xeth) - hashReg, urlHint, err = registry.CreateContracts(common.HexToAddress(addr)) - if err != nil { - fmt.Println(err) - return otto.UndefinedValue() - } - - v, _ := call.Otto.ToValue([]string{hashReg, urlHint}) - return v -} - // internal transaction type which will allow us to resend transactions using `eth.resend` type tx struct { tx *types.Transaction diff --git a/cmd/geth/js.go b/cmd/geth/js.go index 7023610b63..4fb384a77e 100644 --- a/cmd/geth/js.go +++ b/cmd/geth/js.go @@ -31,7 +31,8 @@ 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/resolver" + "github.com/ethereum/go-ethereum/common/registrar" + "github.com/ethereum/go-ethereum/common/registrar/ethreg" "github.com/ethereum/go-ethereum/eth" re "github.com/ethereum/go-ethereum/jsre" "github.com/ethereum/go-ethereum/rpc" @@ -92,10 +93,10 @@ func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain string, bzzEnabled bool js.adminBindings() if bzzEnabled { // register the swarm rountripper with the bzz scheme on the docserver - js.ds.RegisterProtocol("bzz", &bzz.RoundTripper{Port: bzzPort}) + js.ds.RegisterScheme("bzz", &bzz.RoundTripper{Port: bzzPort}) // swarm js bindings bzz.NewJSApi(js.re, js.ethereum.Swarm) - js.ethereum.Swarm.Resolver = resolver.New(xeth.New(ethereum, f)) + js.ethereum.Swarm.Registrar = ethreg.New(js.xeth) } if !liner.TerminalSupported() || !interactive { @@ -155,7 +156,7 @@ var net = web3.net; utils.Fatalf("Error setting namespaces: %v", err) } - js.re.Eval(resolver.GlobalRegistrar + "registrar = GlobalRegistrar.at(\"" + resolver.GlobalRegistrarAddr + "\");") + js.re.Eval(`var GlobalRegistrar = eth.contract(` + registrar.GlobalRegistrarAbi + `); registrar = GlobalRegistrar.at("` + registrar.GlobalRegistrarAddr + `");`) } func (self *jsre) ConfirmTransaction(tx string) bool { diff --git a/cmd/geth/js_bzz_test.go b/cmd/geth/js_bzz_test.go index feaa2a75a1..4472cb6233 100644 --- a/cmd/geth/js_bzz_test.go +++ b/cmd/geth/js_bzz_test.go @@ -1,14 +1,9 @@ package main import ( - // "io/ioutil" "os" - // "path" - // "runtime" "testing" - // "github.com/ethereum/go-ethereum/bzz" - // "github.com/ethereum/go-ethereum/common/resolver" "github.com/ethereum/go-ethereum/eth" ) diff --git a/cmd/geth/js_test.go b/cmd/geth/js_test.go index c7d767f5e0..57f9a0ab7f 100644 --- a/cmd/geth/js_test.go +++ b/cmd/geth/js_test.go @@ -17,7 +17,7 @@ import ( "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/crypto" "github.com/ethereum/go-ethereum/eth" @@ -260,7 +260,19 @@ func TestContract(t *testing.T) { defer os.RemoveAll(tmp) coinbase := common.HexToAddress(testAddress) - resolver.New(repl.xeth).CreateContracts(coinbase) + 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) + } source := `contract test {\n` + " /// @notice Will multiply `a` by 7." + `\n` + @@ -313,13 +325,14 @@ func TestContract(t *testing.T) { if checkEvalJSON( t, repl, - `contractaddress = eth.sendTransaction({from: primary, data: contract.code, gasPrice:"1000", gas:"100000", amount:100 })`, - `"0x291293d57e0a0ab47effe97c02577f90d9211567"`, + `contractaddress = eth.sendTransaction({from: primary, data: contract.code})`, + `"0x46d69d55c3c4b86a924a92c9fc4720bb7bce1d74"`, + // `"0x291293d57e0a0ab47effe97c02577f90d9211567"`, ) != nil { return } - if !processTxs(repl, t, 7) { + if !processTxs(repl, t, 8) { return } @@ -350,7 +363,7 @@ multiply7 = Multiply7.at(contractaddress); return } - expNotice = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x87e2802265838c7f14bb69eecd2112911af6767907a702eeaa445239fb20711b'): {"params":[{"to":"0x291293d57e0a0ab47effe97c02577f90d9211567","data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000006"}]}` + 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\n%v, got\n%v", expNotice, repl.lastConfirm) return diff --git a/common/natspec/natspec.go b/common/natspec/natspec.go index dcd1fba477..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" ) @@ -99,15 +99,15 @@ func FetchDocsForContract(contractAddress string, xeth *xeth.XEth, ds *docserver } 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 - hash, err := res.KeyToContentHash(codehash) + hash, err := reg.HashToHash(codehash) if err != nil { return } if ds.HasScheme("bzz") { - content, err = ds.Get("bzz://"+hash.Hex(), "") + content, err = ds.Get("bzz://"+hash.Hex()[2:], "") if err == nil { // non-fatal return } @@ -115,7 +115,7 @@ func FetchDocsForContract(contractAddress string, xeth *xeth.XEth, ds *docserver //falling back to urlhint } - uri, err := res.ContentHashToUrl(hash) + uri, err := reg.HashToUrl(hash) if err != nil { return } diff --git a/common/natspec/natspec_e2e_test.go b/common/natspec/natspec_e2e_test.go index 49bd04f4e6..b8b4c36a88 100644 --- a/common/natspec/natspec_e2e_test.go +++ b/common/natspec/natspec_e2e_test.go @@ -10,7 +10,7 @@ import ( "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" @@ -73,8 +73,7 @@ const ( ) type testFrontend struct { - t *testing.T - // resolver *resolver.Resolver + t *testing.T ethereum *eth.Ethereum xeth *xe.XEth coinbase common.Address @@ -156,11 +155,16 @@ func testInit(t *testing.T) (self *testFrontend) { self.stateDb = self.ethereum.ChainManager().State().Copy() // initialise the registry contracts - // self.resolver.CreateContracts(addr) - resolver.New(self.xeth).CreateContracts(addr) + 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() - // t.Logf("HashReg contract registered at %v", resolver.HashRegContractAddress) - // t.Logf("URLHint contract registered at %v", resolver.UrlHintContractAddress) return @@ -189,16 +193,20 @@ func TestNatspecE2E(t *testing.T) { dochash := common.BytesToHash(crypto.Sha3([]byte(testContractInfo))) // take the codehash for the contract we wanna test - // codehex := tf.xeth.CodeAt(resolver.HashRegContractAddress) - codeb := tf.xeth.CodeAtBytes(resolver.HashRegContractAddress) + // 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 - // resolver.HashRefContractAddress = "0x0" - // resolver.UrlHintContractAddress = "0x0" - registry := resolver.New(tf.xeth) - _, err := registry.RegisterAddrWithUrl(tf.coinbase, codehash, dochash, "file:///"+testFileName) + // 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) } @@ -209,18 +217,19 @@ func TestNatspecE2E(t *testing.T) { // 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(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, resolver.HashRegContractAddress) - _, err = registry.SetOwner(tf.coinbase) + exp := fmt.Sprintf(testExpNotice2, registrar.HashRegAddr) + _, err = reg.SetOwner(tf.coinbase) if err != nil { t.Errorf("error setting owner: %v", err) } @@ -230,9 +239,9 @@ func TestNatspecE2E(t *testing.T) { } // 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(tf.coinbase, dochash, "file:///test.content") if err != nil { t.Errorf("error registering: %v", err) } 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/registrar.go b/common/registrar/registrar.go new file mode 100644 index 0000000000..e88cc54e76 --- /dev/null +++ b/common/registrar/registrar.go @@ -0,0 +1,391 @@ +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) (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 { + GlobalRegistrarAddr, 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) (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) + HashRegAddr, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", hashRegAbi) + if err != nil || zero.MatchString(HashRegAddr) { + if (addr == common.Address{}) { + err = fmt.Errorf("HashReg address not found and sender for creation not given") + return + } + + HashRegAddr, err = self.backend.Transact(addr.Hex(), "", "", "", "200000", "", 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 @ %v\n", HashRegAddr) + } else { + glog.V(logger.Detail).Infof("HashRegAddr found at @ %v\n", HashRegAddr) + return + } + } + + // register as HashReg + self.ReserveName(addr, HashRegName) + self.SetAddressToName(addr, HashRegName, common.HexToAddress(HashRegAddr)) + + return +} + +func (self *Registrar) SetUrlHint(urlhint string, addr common.Address) (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) + UrlHintAddr, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", urlHintAbi) + if err != nil || zero.MatchString(UrlHintAddr) { + if (addr == common.Address{}) { + err = fmt.Errorf("UrlHint address not found and sender for creation not given") + return + } + UrlHintAddr, 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 @ %v\n", HashRegAddr) + } else { + glog.V(logger.Detail).Infof("UrlHint found @ %v\n", HashRegAddr) + return + } + } + + // register as UrlHint + self.ReserveName(addr, UrlHintName) + self.SetAddressToName(addr, UrlHintName, common.HexToAddress(UrlHintAddr)) + + 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 { + 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 := 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 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 58% rename from common/resolver/resolver_test.go rename to common/registrar/registrar_test.go index 958d0f97fd..5612e691ca 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[2:]] = make(map[string]string) + self.contracts[HashRegAddr[2:]] = make(map[string]string) key := storageAddress(storageMapping(storageIdx2Addr(1), codehash[:])) - self.contracts[HashRegContractAddress[2:]][key] = hash.Hex() + self.contracts[HashRegAddr[2:]][key] = hash.Hex() - self.contracts[UrlHintContractAddress[2:]] = 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[2:]][key] = common.ToHex([]byte(url)) + self.contracts[UrlHintAddr[2:]][key] = common.ToHex([]byte(url)) key = storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(1))) - self.contracts[UrlHintContractAddress[2:]][key] = "0x0" + self.contracts[UrlHintAddr[2:]][key] = "0x0" return self } @@ -56,21 +56,21 @@ func (self *testBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, cod return "", "", nil } -func TestCreateContractsExists(t *testing.T) { +func TestSetGlobalRegistrar(t *testing.T) { b := NewTestBackend() res := New(b) - _, _, err := res.CreateContracts(common.BigToAddress(common.Big0)) - exp := "HashReg already exists at 0x0000000000000000000000000000000000000000" - if err == nil || err.Error() != exp { - t.Errorf("expected error '%s', got '%v'", exp, err) + err := res.SetGlobalRegistrar("addresshex", common.BigToAddress(common.Big1)) + if err != nil { + t.Errorf("unexpected error: %v'", err) } } -func TestKeyToContentHash(t *testing.T) { +func TestHashToHash(t *testing.T) { b := NewTestBackend() res := New(b) + // res.SetHashReg() - got, err := res.KeyToContentHash(codehash) + got, err := res.HashToHash(codehash) if err != nil { t.Errorf("expected no error, got %v", err) } else { @@ -80,10 +80,12 @@ 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 error, got %v", err) } else { @@ -92,16 +94,3 @@ func TestContentHashToUrl(t *testing.T) { } } } - -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 eba5de1b58..0000000000 --- a/common/resolver/contracts.go +++ /dev/null @@ -1,38 +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; - } - */ - - 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/common/resolver/resolver.go b/common/resolver/resolver.go deleted file mode 100644 index b2400fe57e..0000000000 --- a/common/resolver/resolver.go +++ /dev/null @@ -1,402 +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 - -The resolver is used by -* the roundtripper transport implementation of -url schemes to register and resolve domain names -* contract info retrieval (NatSpec) - -The resolver uses 2 contracts on the blockchain: -* 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. -CreateContracts needs to be called once on each blockchain/network once. - -Afterwards contract addresses are retrieved from the global registrar -the first time the Resolver constructor is called in a client session -(see setContracts) -*/ -var ( - UrlHintContractAddress = "0x0" - HashRegContractAddress = "0x0" -) - -const ( - txValue = "0" - txGas = "" - txGasPrice = "" -) - -func abiSignature(s string) string { - return common.ToHex(crypto.Sha3([]byte(s))[:4]) -} - -var ( - HashReg = "HashReg" - UrlHint = "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 = string(make([]byte, 24)) -) - -// resolver'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) -} - -type Resolver struct { - backend Backend -} - -func New(eth Backend) (res *Resolver) { - res = &Resolver{eth} - res.setContracts() - return -} - -// setContracts retrieves contract addresses from the global registrar -// if they are unset -// the addresses are package level variables so this is done only once every -// client session -func (self *Resolver) setContracts() { - var err error - if HashRegContractAddress != "0x0" { - return - } - // reset iff error anywhere - defer func() { - if err != nil { - HashRegContractAddress = "0x0" - } - }() - hashRegAbi := registerAbi + string(common.Hex2BytesFixed(HashRegContractAddress[2:], 32)) - HashRegContractAddress, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", hashRegAbi) - if err != nil { - err = fmt.Errorf("HashReg address not found: %v", err) - return - } - - if UrlHintContractAddress != "0x0" { - return - } - // reset iff error anywhere - defer func() { - if err != nil { - UrlHintContractAddress = "0x0" - } - }() - - urlHintAbi := registerAbi + string(common.Hex2BytesFixed(UrlHintContractAddress[2:], 32)) - UrlHintContractAddress, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", urlHintAbi) - if err != nil { - err = fmt.Errorf("UrlHint address not found: %v", err) - return - } - - glog.V(logger.Detail).Infof("HashReg @ %v\nUrlHint @ %v\n", HashRegContractAddress, UrlHintContractAddress) -} - -// CreateContracts creates new HashReg and UrlHint contracts -// and registers their address on the global registrar. -// creates 6 transactions which need to be mined too take effect -// It does nothing if addresses are set -// needs to be called only once ever for a blockchain -func (self *Resolver) CreateContracts(addr common.Address) (hashReg, urlHint string, err error) { - if HashRegContractAddress != "0x0" { - err = fmt.Errorf("HashReg already exists at %v", HashRegContractAddress) - return - } - hashReg, err = self.backend.Transact(addr.Hex(), "", "", txValue, txGas, txGasPrice, ContractCodeHashReg) - if err != nil { - return - } - _, err = self.Reserve(addr, HashReg) - if err != nil { - return - } - _, err = self.RegisterAddress(addr, HashReg, common.HexToAddress(hashReg)) - if err != nil { - return - } - - if UrlHintContractAddress != "0x0" { - err = fmt.Errorf("UrlHint already exists at %v", UrlHintContractAddress) - return - } - urlHint, err = self.backend.Transact(addr.Hex(), "", "", txValue, txGas, txGasPrice, ContractCodeURLhint) - if err != nil { - return - } - _, err = self.Reserve(addr, UrlHint) - if err != nil { - return - } - _, err = self.RegisterAddress(addr, UrlHint, common.HexToAddress(urlHint)) - if err != nil { - return - } - HashRegContractAddress = hashReg - UrlHintContractAddress = urlHint - glog.V(logger.Detail).Infof("HashReg @ %v\nUrlHint @ %v\n", HashRegContractAddress, UrlHintContractAddress) - - return -} - -// Reserve(from, name) reserves name for the sender address in the globalRegistrar -// the tx needs to be mined to take effect -func (self *Resolver) Reserve(address common.Address, name string) (txh string, err error) { - nameHex, extra := encodeName(name, 6) - abi := reserveAbi + nameHex + extra - return self.backend.Transact( - address.Hex(), - GlobalRegistrarAddr, - "", txValue, txGas, txGasPrice, - abi, - ) -} - -// RegisterAddress(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 *Resolver) RegisterAddress(from common.Address, name string, address common.Address) (txh string, err error) { - nameHex, extra := encodeName(name, 6) - addrHex := encodeAddress(address) - - trueHex := make([]byte, 64) - trueHex[63] = 1 - - abi := registerAbi + nameHex + addrHex + extra - return self.backend.Transact( - from.Hex(), - GlobalRegistrarAddr, - "", txValue, txGas, txGasPrice, - abi, - ) -} - -// NameToAddr(from, name) queries the registrar for the address on name -func (self *Resolver) NameToAddr(from common.Address, name string) (address common.Address, err error) { - nameHex, extra := encodeName(name, 2) - abi := resolveAbi + nameHex + extra - res, _, err := self.backend.Call( - from.Hex(), - GlobalRegistrarAddr, - txValue, txGas, txGasPrice, - abi, - ) - if err != nil { - return - } - address = common.HexToAddress(res) - 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 -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 -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 -} - -// RegisterAddrWithUrl(address, key, contenthash, url) is a convenience method -// registers key -> conenthash in HashReg and -// registers contenthash -> url in UrlHint -// creates 3 transaction using address as sender -// transactions need to be mined to take effect -func (self *Resolver) RegisterAddrWithUrl(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) -} - -// KeyToContentHash(key) resolves contenthash for key (a hash) using HashReg -// 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 := HashRegContractAddress[2:] - 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 -} - -// ContentHashToUrl(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 *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[2:], 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 -} - -// KeyToUrl(key) is a convenience method -// resolves contenthash for key (a hash) using HashReg, then -// resolves the url for contenthash using UrlHint -// resolution is costless non-transactional -// implemented as direct retrieval from db -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) -} - -func encodeAddress(address common.Address) string { - return addressAbiPrefix + address.Hex()[2:] -} - -func encodeName(name string, index uint8) (nameHex, extra string) { - nameHexBytes := make([]byte, 64) - if len(name) > 32 { - nameHexBytes[63] = byte(index) - extra = common.Bytes2Hex([]byte(name)) - } else { - copy(nameHexBytes, []byte(name)) - } - return string(nameHexBytes), extra -}