| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- // Copyright 2015 The go-ethereum Authors
- // This file is part of go-ethereum.
- //
- // go-ethereum is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Lesser General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // go-ethereum is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Lesser General Public License for more details.
- //
- // You should have received a copy of the GNU Lesser General Public License
- // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
- 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<X> 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):], ""
- }
|