github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/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/ethereumproject/go-ethereum/common"
    26  	"github.com/ethereumproject/go-ethereum/crypto"
    27  	"github.com/ethereumproject/go-ethereum/logger"
    28  	"github.com/ethereumproject/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 the first time any Registrar method is called
    52  in a client session.
    53  This is done for frontier by default, otherwise the caller needs to make sure
    54  the relevant environment initialised the desired contracts
    55  */
    56  var (
    57  	GlobalRegistrarAddr = "0x33990122638b9132ca29c723bdf037f1a891a70c" // frontier
    58  	HashRegAddr         = "0x23bf622b5a65f6060d855fca401133ded3520620" // frontier
    59  	UrlHintAddr         = "0x73ed5ef6c010727dfd2671dbb70faac19ec18626" // frontier
    60  
    61  	zero = regexp.MustCompile("^(0x)?0*$")
    62  )
    63  
    64  const (
    65  	trueHex  = "0000000000000000000000000000000000000000000000000000000000000001"
    66  	falseHex = "0000000000000000000000000000000000000000000000000000000000000000"
    67  )
    68  
    69  func abiSignature(s string) string {
    70  	return common.ToHex(crypto.Keccak256([]byte(s))[:4])
    71  }
    72  
    73  var (
    74  	HashRegName = "HashReg"
    75  	UrlHintName = "UrlHint"
    76  
    77  	registerContentHashAbi = abiSignature("register(uint256,uint256)")
    78  	registerUrlAbi         = abiSignature("register(uint256,uint8,uint256)")
    79  	setOwnerAbi            = abiSignature("setowner()")
    80  	reserveAbi             = abiSignature("reserve(bytes32)")
    81  	resolveAbi             = abiSignature("addr(bytes32)")
    82  	registerAbi            = abiSignature("setAddress(bytes32,address,bool)")
    83  	addressAbiPrefix       = falseHex[:24]
    84  )
    85  
    86  // Registrar's backend is defined as an interface (implemented by xeth, but could be remote)
    87  type Backend interface {
    88  	StorageAt(string, string) string
    89  	Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error)
    90  	Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error)
    91  }
    92  
    93  // TODO Registrar should also just implement The Resolver and Registry interfaces
    94  // Simplify for now.
    95  type VersionedRegistrar interface {
    96  	Resolver(*big.Int) *Registrar
    97  	Registry() *Registrar
    98  }
    99  
   100  type Registrar struct {
   101  	backend Backend
   102  }
   103  
   104  func New(b Backend) (res *Registrar) {
   105  	res = &Registrar{b}
   106  	return
   107  }
   108  
   109  func (self *Registrar) SetGlobalRegistrar(namereg string, addr common.Address) (txhash string, err error) {
   110  	if namereg != "" {
   111  		GlobalRegistrarAddr = namereg
   112  		return
   113  	}
   114  	if zero.MatchString(GlobalRegistrarAddr) {
   115  		if addr.IsEmpty() {
   116  			err = fmt.Errorf("GlobalRegistrar address not found and sender for creation not given")
   117  			return
   118  		} else {
   119  			txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "800000", "", GlobalRegistrarCode)
   120  			if err != nil {
   121  				err = fmt.Errorf("GlobalRegistrar address not found and sender for creation failed: %v", err)
   122  				return
   123  			}
   124  		}
   125  	}
   126  	return
   127  }
   128  
   129  func (self *Registrar) SetHashReg(hashreg string, addr common.Address) (txhash string, err error) {
   130  	if hashreg != "" {
   131  		HashRegAddr = hashreg
   132  	} else {
   133  		if !zero.MatchString(HashRegAddr) {
   134  			return
   135  		}
   136  		nameHex, extra := encodeName(HashRegName, 2)
   137  		hashRegAbi := resolveAbi + nameHex + extra
   138  		glog.V(logger.Detail).Infof("\ncall HashRegAddr %v with %v\n", GlobalRegistrarAddr, hashRegAbi)
   139  		var res string
   140  		res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", hashRegAbi)
   141  		if len(res) >= 40 {
   142  			HashRegAddr = "0x" + res[len(res)-40:]
   143  		}
   144  		if err != nil || zero.MatchString(HashRegAddr) {
   145  			if addr.IsEmpty() {
   146  				err = fmt.Errorf("HashReg address not found and sender for creation not given")
   147  				return
   148  			}
   149  
   150  			txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "", "", HashRegCode)
   151  			if err != nil {
   152  				err = fmt.Errorf("HashReg address not found and sender for creation failed: %v", err)
   153  			}
   154  			glog.V(logger.Detail).Infof("created HashRegAddr @ txhash %v\n", txhash)
   155  		} else {
   156  			glog.V(logger.Detail).Infof("HashRegAddr found at @ %v\n", HashRegAddr)
   157  			return
   158  		}
   159  	}
   160  
   161  	return
   162  }
   163  
   164  func (self *Registrar) SetUrlHint(urlhint string, addr common.Address) (txhash string, err error) {
   165  	if urlhint != "" {
   166  		UrlHintAddr = urlhint
   167  	} else {
   168  		if !zero.MatchString(UrlHintAddr) {
   169  			return
   170  		}
   171  		nameHex, extra := encodeName(UrlHintName, 2)
   172  		urlHintAbi := resolveAbi + nameHex + extra
   173  		glog.V(logger.Detail).Infof("UrlHint address query data: %s to %s", urlHintAbi, GlobalRegistrarAddr)
   174  		var res string
   175  		res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", urlHintAbi)
   176  		if len(res) >= 40 {
   177  			UrlHintAddr = "0x" + res[len(res)-40:]
   178  		}
   179  		if err != nil || zero.MatchString(UrlHintAddr) {
   180  			if addr.IsEmpty() {
   181  				err = fmt.Errorf("UrlHint address not found and sender for creation not given")
   182  				return
   183  			}
   184  			txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "210000", "", UrlHintCode)
   185  			if err != nil {
   186  				err = fmt.Errorf("UrlHint address not found and sender for creation failed: %v", err)
   187  			}
   188  			glog.V(logger.Detail).Infof("created UrlHint @ txhash %v\n", txhash)
   189  		} else {
   190  			glog.V(logger.Detail).Infof("UrlHint found @ %v\n", HashRegAddr)
   191  			return
   192  		}
   193  	}
   194  
   195  	return
   196  }
   197  
   198  // called as first step in the registration process on HashReg
   199  func (self *Registrar) SetOwner(address common.Address) (txh string, err error) {
   200  	if zero.MatchString(HashRegAddr) {
   201  		return "", fmt.Errorf("HashReg address is not set")
   202  	}
   203  	return self.backend.Transact(
   204  		address.Hex(),
   205  		HashRegAddr,
   206  		"", "", "", "",
   207  		setOwnerAbi,
   208  	)
   209  }
   210  
   211  // registers some content hash to a key/code hash
   212  // e.g., the contract Info combined Json Doc's ContentHash
   213  // to CodeHash of a contract or hash of a domain
   214  func (self *Registrar) SetHashToHash(address common.Address, codehash, dochash common.Hash) (txh string, err error) {
   215  	if zero.MatchString(HashRegAddr) {
   216  		return "", fmt.Errorf("HashReg address is not set")
   217  	}
   218  
   219  	_, err = self.SetOwner(address)
   220  	if err != nil {
   221  		return
   222  	}
   223  	codehex := common.Bytes2Hex(codehash[:])
   224  	dochex := common.Bytes2Hex(dochash[:])
   225  
   226  	data := registerContentHashAbi + codehex + dochex
   227  	glog.V(logger.Detail).Infof("SetHashToHash data: %s sent  to %v\n", data, HashRegAddr)
   228  	return self.backend.Transact(
   229  		address.Hex(),
   230  		HashRegAddr,
   231  		"", "", "", "",
   232  		data,
   233  	)
   234  }
   235  
   236  // SetUrlToHash(from, hash, url) registers a url to a content hash so that the content can be fetched
   237  // address is used as sender for the transaction and will be the owner of a new
   238  // registry entry on first time use
   239  // FIXME: silently doing nothing if sender is not the owner
   240  // note that with content addressed storage, this step is no longer necessary
   241  func (self *Registrar) SetUrlToHash(address common.Address, hash common.Hash, url string) (txh string, err error) {
   242  	if zero.MatchString(UrlHintAddr) {
   243  		return "", fmt.Errorf("UrlHint address is not set")
   244  	}
   245  
   246  	hashHex := common.Bytes2Hex(hash[:])
   247  	var urlHex string
   248  	urlb := []byte(url)
   249  	var cnt byte
   250  	n := len(urlb)
   251  
   252  	for n > 0 {
   253  		if n > 32 {
   254  			n = 32
   255  		}
   256  		urlHex = common.Bytes2Hex(urlb[:n])
   257  		urlb = urlb[n:]
   258  		n = len(urlb)
   259  		bcnt := make([]byte, 32)
   260  		bcnt[31] = cnt
   261  		data := registerUrlAbi +
   262  			hashHex +
   263  			common.Bytes2Hex(bcnt) +
   264  			common.Bytes2Hex(common.Hex2BytesFixed(urlHex, 32))
   265  		txh, err = self.backend.Transact(
   266  			address.Hex(),
   267  			UrlHintAddr,
   268  			"", "", "", "",
   269  			data,
   270  		)
   271  		if err != nil {
   272  			return
   273  		}
   274  		cnt++
   275  	}
   276  	return
   277  }
   278  
   279  // HashToHash(key) resolves contenthash for key (a hash) using HashReg
   280  // resolution is costless non-transactional
   281  // implemented as direct retrieval from  db
   282  func (self *Registrar) HashToHash(khash common.Hash) (chash common.Hash, err error) {
   283  	if zero.MatchString(HashRegAddr) {
   284  		return common.Hash{}, fmt.Errorf("HashReg address is not set")
   285  	}
   286  
   287  	// look up in hashReg
   288  	at := HashRegAddr[2:]
   289  	key := storageAddress(storageMapping(storageIdx2Addr(1), khash[:]))
   290  	hash := self.backend.StorageAt(at, key)
   291  
   292  	if hash == "0x0" || len(hash) < 3 || (hash == common.Hash{}.Hex()) {
   293  		err = fmt.Errorf("HashToHash: content hash not found for '%v'", khash.Hex())
   294  		return
   295  	}
   296  	copy(chash[:], common.Hex2BytesFixed(hash[2:], 32))
   297  	return
   298  }
   299  
   300  func storageIdx2Addr(varidx uint32) []byte {
   301  	data := make([]byte, 32)
   302  	binary.BigEndian.PutUint32(data[28:32], varidx)
   303  	return data
   304  }
   305  
   306  func storageMapping(addr, key []byte) []byte {
   307  	data := make([]byte, 64)
   308  	copy(data[0:32], key[0:32])
   309  	copy(data[32:64], addr[0:32])
   310  	sha := crypto.Keccak256(data)
   311  	return sha
   312  }
   313  
   314  func storageAddress(addr []byte) string {
   315  	return common.ToHex(addr)
   316  }
   317  
   318  func encodeName(name string, index uint8) (string, string) {
   319  	extra := common.Bytes2Hex([]byte(name))
   320  	if len(name) > 32 {
   321  		return fmt.Sprintf("%064x", index), extra
   322  	}
   323  	return extra + falseHex[len(extra):], ""
   324  }