aboutsummaryrefslogblamecommitdiffstats
path: root/common/registrar/registrar.go
blob: 0606f6985fb6ba86acec4787139d867b23bdc419 (plain) (tree)
1
2
3
4
5
6
7
8
9
                                         
                                                
  
                                                                                  



                                                                              
                                                                             
                                                                 
                                                               


                                                                           
                                                                                  
 

































                                                                           



                                                                               

     



                                                                                        









                                                                                     
                                                            





































                                                                                                         
                                                                                                           



                                             
                                                  



                                                                                                               
                                                                                                                      








                                                                                                                             
                                                                                                   








                                                                                                               




                                                                                                





                                                                                                               
                                                                                                        


                                                                                                                     
                                                                                                





                                                                                               


              
                                                                                                   








                                                                                                                    




                                                                                                




                                                                                                               
                                                                                                              


                                                                                                                     
                                                                                            





                                                                                        





                                                                                                 


                                                                           














                                                                                                                           



                                                                           















                                                                                                         



                                                                                

















                                                                                 


                                                                   











                                                                                                                     



                                                                   






















                                                                                                                   



                                                                   




































                                                                                     



                                                                              





                                                                            
                                                                                            










                                                                             


                                                                   







                                                                                       


                                                     

                 
                             


                               
 
                          
                                                                                       













                                                       
                                     































                                                            
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library 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.
//
// The go-ethereum library 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 the go-ethereum library. 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 the first time any Registrar method is called
in a client session.
This is done for frontier by default, otherwise the caller needs to make sure
the relevant environment initialised the desired contracts
*/
var (
    // GlobalRegistrarAddr = "0xc6d9d2cd449a754c494264e1809c50e34d64562b" // olympic
    GlobalRegistrarAddr = "0x33990122638b9132ca29c723bdf037f1a891a70c" // frontier
    HashRegAddr         = "0x23bf622b5a65f6060d855fca401133ded3520620" // frontier
    UrlHintAddr         = "0x73ed5ef6c010727dfd2671dbb70faac19ec18626" // frontier

    zero = regexp.MustCompile("^(0x)?0*$")
)

const (
    trueHex  = "0000000000000000000000000000000000000000000000000000000000000001"
    falseHex = "0000000000000000000000000000000000000000000000000000000000000000"
)

func abiSignature(s string) string {
    return common.ToHex(crypto.Keccak256([]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 zero.MatchString(GlobalRegistrarAddr) {
        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) {
    if zero.MatchString(GlobalRegistrarAddr) {
        return "", fmt.Errorf("GlobalRegistrar address is not set")
    }
    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) {
    if zero.MatchString(GlobalRegistrarAddr) {
        return "", fmt.Errorf("GlobalRegistrar address is not set")
    }

    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) {
    if zero.MatchString(GlobalRegistrarAddr) {
        return address, fmt.Errorf("GlobalRegistrar address is not set")
    }

    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) {
    if zero.MatchString(HashRegAddr) {
        return "", fmt.Errorf("HashReg address is not set")
    }
    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) {
    if zero.MatchString(HashRegAddr) {
        return "", fmt.Errorf("HashReg address is not set")
    }

    _, 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) {
    if zero.MatchString(UrlHintAddr) {
        return "", fmt.Errorf("UrlHint address is not set")
    }

    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) {
    if zero.MatchString(HashRegAddr) {
        return common.Hash{}, fmt.Errorf("HashReg address is not set")
    }

    // 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("HashToHash: 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) {
    if zero.MatchString(UrlHintAddr) {
        return "", fmt.Errorf("UrlHint address is not set")
    }
    // 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("HashToUrl: 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.Keccak256(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):], ""
}