github.com/jeffallen/go-ethereum@v1.1.4-0.20150910155051-571d3236c49c/common/registrar/registrar.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package registrar
    18  
    19  import (
    20  	"encoding/binary"
    21  	"fmt"
    22  	"math/big"
    23  	"regexp"
    24  
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/ethereum/go-ethereum/crypto"
    27  	"github.com/ethereum/go-ethereum/logger"
    28  	"github.com/ethereum/go-ethereum/logger/glog"
    29  )
    30  
    31  /*
    32  Registrar implements the Ethereum name registrar services mapping
    33  - arbitrary strings to ethereum addresses
    34  - hashes to hashes
    35  - hashes to arbitrary strings
    36  (likely will provide lookup service for all three)
    37  
    38  The Registrar is used by
    39  * the roundtripper transport implementation of
    40  url schemes to resolve domain names and services that register these names
    41  * contract info retrieval (NatSpec).
    42  
    43  The Registrar uses 3 contracts on the blockchain:
    44  * GlobalRegistrar: Name (string) -> Address (Owner)
    45  * HashReg : Key Hash (hash of domain name or contract code) -> Content Hash
    46  * UrlHint : Content Hash -> Url Hint
    47  
    48  These contracts are (currently) not included in the genesis block.
    49  Each Set<X> needs to be called once on each blockchain/network once.
    50  
    51  Contract addresses need to be set (HashReg and UrlHint retrieved from the global
    52  registrar the first time any Registrar method is called in a client session
    53  
    54  So the caller needs to make sure the relevant environment initialised the desired
    55  contracts
    56  */
    57  var (
    58  	UrlHintAddr         = "0x0"
    59  	HashRegAddr         = "0x0"
    60  	GlobalRegistrarAddr = "0x0"
    61  	// GlobalRegistrarAddr = "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
    62  
    63  	zero = regexp.MustCompile("^(0x)?0*$")
    64  )
    65  
    66  const (
    67  	trueHex  = "0000000000000000000000000000000000000000000000000000000000000001"
    68  	falseHex = "0000000000000000000000000000000000000000000000000000000000000000"
    69  )
    70  
    71  func abiSignature(s string) string {
    72  	return common.ToHex(crypto.Sha3([]byte(s))[:4])
    73  }
    74  
    75  var (
    76  	HashRegName = "HashReg"
    77  	UrlHintName = "UrlHint"
    78  
    79  	registerContentHashAbi = abiSignature("register(uint256,uint256)")
    80  	registerUrlAbi         = abiSignature("register(uint256,uint8,uint256)")
    81  	setOwnerAbi            = abiSignature("setowner()")
    82  	reserveAbi             = abiSignature("reserve(bytes32)")
    83  	resolveAbi             = abiSignature("addr(bytes32)")
    84  	registerAbi            = abiSignature("setAddress(bytes32,address,bool)")
    85  	addressAbiPrefix       = falseHex[:24]
    86  )
    87  
    88  // Registrar's backend is defined as an interface (implemented by xeth, but could be remote)
    89  type Backend interface {
    90  	StorageAt(string, string) string
    91  	Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error)
    92  	Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error)
    93  }
    94  
    95  // TODO Registrar should also just implement The Resolver and Registry interfaces
    96  // Simplify for now.
    97  type VersionedRegistrar interface {
    98  	Resolver(*big.Int) *Registrar
    99  	Registry() *Registrar
   100  }
   101  
   102  type Registrar struct {
   103  	backend Backend
   104  }
   105  
   106  func New(b Backend) (res *Registrar) {
   107  	res = &Registrar{b}
   108  	return
   109  }
   110  
   111  func (self *Registrar) SetGlobalRegistrar(namereg string, addr common.Address) (txhash string, err error) {
   112  	if namereg != "" {
   113  		GlobalRegistrarAddr = namereg
   114  		return
   115  	}
   116  	if GlobalRegistrarAddr == "0x0" || GlobalRegistrarAddr == "0x" {
   117  		if (addr == common.Address{}) {
   118  			err = fmt.Errorf("GlobalRegistrar address not found and sender for creation not given")
   119  			return
   120  		} else {
   121  			txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "800000", "", GlobalRegistrarCode)
   122  			if err != nil {
   123  				err = fmt.Errorf("GlobalRegistrar address not found and sender for creation failed: %v", err)
   124  				return
   125  			}
   126  		}
   127  	}
   128  	return
   129  }
   130  
   131  func (self *Registrar) SetHashReg(hashreg string, addr common.Address) (txhash string, err error) {
   132  	if hashreg != "" {
   133  		HashRegAddr = hashreg
   134  	} else {
   135  		if !zero.MatchString(HashRegAddr) {
   136  			return
   137  		}
   138  		nameHex, extra := encodeName(HashRegName, 2)
   139  		hashRegAbi := resolveAbi + nameHex + extra
   140  		glog.V(logger.Detail).Infof("\ncall HashRegAddr %v with %v\n", GlobalRegistrarAddr, hashRegAbi)
   141  		var res string
   142  		res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", hashRegAbi)
   143  		if len(res) >= 40 {
   144  			HashRegAddr = "0x" + res[len(res)-40:len(res)]
   145  		}
   146  		if err != nil || zero.MatchString(HashRegAddr) {
   147  			if (addr == common.Address{}) {
   148  				err = fmt.Errorf("HashReg address not found and sender for creation not given")
   149  				return
   150  			}
   151  
   152  			txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "", "", HashRegCode)
   153  			if err != nil {
   154  				err = fmt.Errorf("HashReg address not found and sender for creation failed: %v", err)
   155  			}
   156  			glog.V(logger.Detail).Infof("created HashRegAddr @ txhash %v\n", txhash)
   157  		} else {
   158  			glog.V(logger.Detail).Infof("HashRegAddr found at @ %v\n", HashRegAddr)
   159  			return
   160  		}
   161  	}
   162  
   163  	return
   164  }
   165  
   166  func (self *Registrar) SetUrlHint(urlhint string, addr common.Address) (txhash string, err error) {
   167  	if urlhint != "" {
   168  		UrlHintAddr = urlhint
   169  	} else {
   170  		if !zero.MatchString(UrlHintAddr) {
   171  			return
   172  		}
   173  		nameHex, extra := encodeName(UrlHintName, 2)
   174  		urlHintAbi := resolveAbi + nameHex + extra
   175  		glog.V(logger.Detail).Infof("UrlHint address query data: %s to %s", urlHintAbi, GlobalRegistrarAddr)
   176  		var res string
   177  		res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", urlHintAbi)
   178  		if len(res) >= 40 {
   179  			UrlHintAddr = "0x" + res[len(res)-40:len(res)]
   180  		}
   181  		if err != nil || zero.MatchString(UrlHintAddr) {
   182  			if (addr == common.Address{}) {
   183  				err = fmt.Errorf("UrlHint address not found and sender for creation not given")
   184  				return
   185  			}
   186  			txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "210000", "", UrlHintCode)
   187  			if err != nil {
   188  				err = fmt.Errorf("UrlHint address not found and sender for creation failed: %v", err)
   189  			}
   190  			glog.V(logger.Detail).Infof("created UrlHint @ txhash %v\n", txhash)
   191  		} else {
   192  			glog.V(logger.Detail).Infof("UrlHint found @ %v\n", HashRegAddr)
   193  			return
   194  		}
   195  	}
   196  
   197  	return
   198  }
   199  
   200  // ReserveName(from, name) reserves name for the sender address in the globalRegistrar
   201  // the tx needs to be mined to take effect
   202  func (self *Registrar) ReserveName(address common.Address, name string) (txh string, err error) {
   203  	nameHex, extra := encodeName(name, 2)
   204  	abi := reserveAbi + nameHex + extra
   205  	glog.V(logger.Detail).Infof("Reserve data: %s", abi)
   206  	return self.backend.Transact(
   207  		address.Hex(),
   208  		GlobalRegistrarAddr,
   209  		"", "", "", "",
   210  		abi,
   211  	)
   212  }
   213  
   214  // SetAddressToName(from, name, addr) will set the Address to address for name
   215  // in the globalRegistrar using from as the sender of the transaction
   216  // the tx needs to be mined to take effect
   217  func (self *Registrar) SetAddressToName(from common.Address, name string, address common.Address) (txh string, err error) {
   218  	nameHex, extra := encodeName(name, 6)
   219  	addrHex := encodeAddress(address)
   220  
   221  	abi := registerAbi + nameHex + addrHex + trueHex + extra
   222  	glog.V(logger.Detail).Infof("SetAddressToName data: %s to %s ", abi, GlobalRegistrarAddr)
   223  
   224  	return self.backend.Transact(
   225  		from.Hex(),
   226  		GlobalRegistrarAddr,
   227  		"", "", "", "",
   228  		abi,
   229  	)
   230  }
   231  
   232  // NameToAddr(from, name) queries the registrar for the address on name
   233  func (self *Registrar) NameToAddr(from common.Address, name string) (address common.Address, err error) {
   234  	nameHex, extra := encodeName(name, 2)
   235  	abi := resolveAbi + nameHex + extra
   236  	glog.V(logger.Detail).Infof("NameToAddr data: %s", abi)
   237  	res, _, err := self.backend.Call(
   238  		from.Hex(),
   239  		GlobalRegistrarAddr,
   240  		"", "", "",
   241  		abi,
   242  	)
   243  	if err != nil {
   244  		return
   245  	}
   246  	address = common.HexToAddress(res)
   247  	return
   248  }
   249  
   250  // called as first step in the registration process on HashReg
   251  func (self *Registrar) SetOwner(address common.Address) (txh string, err error) {
   252  	return self.backend.Transact(
   253  		address.Hex(),
   254  		HashRegAddr,
   255  		"", "", "", "",
   256  		setOwnerAbi,
   257  	)
   258  }
   259  
   260  // registers some content hash to a key/code hash
   261  // e.g., the contract Info combined Json Doc's ContentHash
   262  // to CodeHash of a contract or hash of a domain
   263  func (self *Registrar) SetHashToHash(address common.Address, codehash, dochash common.Hash) (txh string, err error) {
   264  	_, err = self.SetOwner(address)
   265  	if err != nil {
   266  		return
   267  	}
   268  	codehex := common.Bytes2Hex(codehash[:])
   269  	dochex := common.Bytes2Hex(dochash[:])
   270  
   271  	data := registerContentHashAbi + codehex + dochex
   272  	glog.V(logger.Detail).Infof("SetHashToHash data: %s sent  to %v\n", data, HashRegAddr)
   273  	return self.backend.Transact(
   274  		address.Hex(),
   275  		HashRegAddr,
   276  		"", "", "", "",
   277  		data,
   278  	)
   279  }
   280  
   281  // SetUrlToHash(from, hash, url) registers a url to a content hash so that the content can be fetched
   282  // address is used as sender for the transaction and will be the owner of a new
   283  // registry entry on first time use
   284  // FIXME: silently doing nothing if sender is not the owner
   285  // note that with content addressed storage, this step is no longer necessary
   286  func (self *Registrar) SetUrlToHash(address common.Address, hash common.Hash, url string) (txh string, err error) {
   287  	hashHex := common.Bytes2Hex(hash[:])
   288  	var urlHex string
   289  	urlb := []byte(url)
   290  	var cnt byte
   291  	n := len(urlb)
   292  
   293  	for n > 0 {
   294  		if n > 32 {
   295  			n = 32
   296  		}
   297  		urlHex = common.Bytes2Hex(urlb[:n])
   298  		urlb = urlb[n:]
   299  		n = len(urlb)
   300  		bcnt := make([]byte, 32)
   301  		bcnt[31] = cnt
   302  		data := registerUrlAbi +
   303  			hashHex +
   304  			common.Bytes2Hex(bcnt) +
   305  			common.Bytes2Hex(common.Hex2BytesFixed(urlHex, 32))
   306  		txh, err = self.backend.Transact(
   307  			address.Hex(),
   308  			UrlHintAddr,
   309  			"", "", "", "",
   310  			data,
   311  		)
   312  		if err != nil {
   313  			return
   314  		}
   315  		cnt++
   316  	}
   317  	return
   318  }
   319  
   320  // HashToHash(key) resolves contenthash for key (a hash) using HashReg
   321  // resolution is costless non-transactional
   322  // implemented as direct retrieval from  db
   323  func (self *Registrar) HashToHash(khash common.Hash) (chash common.Hash, err error) {
   324  	// look up in hashReg
   325  	at := HashRegAddr[2:]
   326  	key := storageAddress(storageMapping(storageIdx2Addr(1), khash[:]))
   327  	hash := self.backend.StorageAt(at, key)
   328  
   329  	if hash == "0x0" || len(hash) < 3 || (hash == common.Hash{}.Hex()) {
   330  		err = fmt.Errorf("content hash not found for '%v'", khash.Hex())
   331  		return
   332  	}
   333  	copy(chash[:], common.Hex2BytesFixed(hash[2:], 32))
   334  	return
   335  }
   336  
   337  // HashToUrl(contenthash) resolves the url for contenthash using UrlHint
   338  // resolution is costless non-transactional
   339  // implemented as direct retrieval from  db
   340  // if we use content addressed storage, this step is no longer necessary
   341  func (self *Registrar) HashToUrl(chash common.Hash) (uri string, err error) {
   342  	// look up in URL reg
   343  	var str string = " "
   344  	var idx uint32
   345  	for len(str) > 0 {
   346  		mapaddr := storageMapping(storageIdx2Addr(1), chash[:])
   347  		key := storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(idx)))
   348  		hex := self.backend.StorageAt(UrlHintAddr[2:], key)
   349  		str = string(common.Hex2Bytes(hex[2:]))
   350  		l := 0
   351  		for (l < len(str)) && (str[l] == 0) {
   352  			l++
   353  		}
   354  
   355  		str = str[l:]
   356  		uri = uri + str
   357  		idx++
   358  	}
   359  
   360  	if len(uri) == 0 {
   361  		err = fmt.Errorf("GetURLhint: URL hint not found for '%v'", chash.Hex())
   362  	}
   363  	return
   364  }
   365  
   366  func storageIdx2Addr(varidx uint32) []byte {
   367  	data := make([]byte, 32)
   368  	binary.BigEndian.PutUint32(data[28:32], varidx)
   369  	return data
   370  }
   371  
   372  func storageMapping(addr, key []byte) []byte {
   373  	data := make([]byte, 64)
   374  	copy(data[0:32], key[0:32])
   375  	copy(data[32:64], addr[0:32])
   376  	sha := crypto.Sha3(data)
   377  	return sha
   378  }
   379  
   380  func storageFixedArray(addr, idx []byte) []byte {
   381  	var carry byte
   382  	for i := 31; i >= 0; i-- {
   383  		var b byte = addr[i] + idx[i] + carry
   384  		if b < addr[i] {
   385  			carry = 1
   386  		} else {
   387  			carry = 0
   388  		}
   389  		addr[i] = b
   390  	}
   391  	return addr
   392  }
   393  
   394  func storageAddress(addr []byte) string {
   395  	return common.ToHex(addr)
   396  }
   397  
   398  func encodeAddress(address common.Address) string {
   399  	return addressAbiPrefix + address.Hex()[2:]
   400  }
   401  
   402  func encodeName(name string, index uint8) (string, string) {
   403  	extra := common.Bytes2Hex([]byte(name))
   404  	if len(name) > 32 {
   405  		return fmt.Sprintf("%064x", index), extra
   406  	}
   407  	return extra + falseHex[len(extra):], ""
   408  }