aboutsummaryrefslogtreecommitdiffstats
path: root/common/registrar/registrar.go
diff options
context:
space:
mode:
Diffstat (limited to 'common/registrar/registrar.go')
-rw-r--r--common/registrar/registrar.go398
1 files changed, 398 insertions, 0 deletions
diff --git a/common/registrar/registrar.go b/common/registrar/registrar.go
new file mode 100644
index 000000000..061fe9c2c
--- /dev/null
+++ b/common/registrar/registrar.go
@@ -0,0 +1,398 @@
+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) (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 || (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 := len(str)
+ for (l > 0) && (str[l-1] == 0) {
+ l--
+ }
+
+ str = str[:l]
+ uri = uri + str
+ idx++
+ }
+
+ l := 0
+ for (l < len(uri)) && (uri[l] == 0) {
+ l++
+ }
+ uri = uri[l:]
+
+ 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):], ""
+}