399 lines
12 KiB
Go
399 lines
12 KiB
Go
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(), "", "", "", "", "", 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):], ""
|
|
}
|