aboutsummaryrefslogtreecommitdiffstats
path: root/common/registrar
diff options
context:
space:
mode:
Diffstat (limited to 'common/registrar')
-rw-r--r--common/registrar/contracts.go147
-rw-r--r--common/registrar/ethreg/ethreg.go32
-rw-r--r--common/registrar/registrar.go398
-rw-r--r--common/registrar/registrar_test.go96
4 files changed, 673 insertions, 0 deletions
diff --git a/common/registrar/contracts.go b/common/registrar/contracts.go
new file mode 100644
index 000000000..6c624030e
--- /dev/null
+++ b/common/registrar/contracts.go
@@ -0,0 +1,147 @@
+package registrar
+
+const ( // built-in contracts address source code and evm code
+ UrlHintCode = "0x60c180600c6000396000f30060003560e060020a90048063300a3bbf14601557005b6024600435602435604435602a565b60006000f35b6000600084815260200190815260200160002054600160a060020a0316600014806078575033600160a060020a03166000600085815260200190815260200160002054600160a060020a0316145b607f5760bc565b336000600085815260200190815260200160002081905550806001600085815260200190815260200160002083610100811060b657005b01819055505b50505056"
+
+ UrlHintSrc = `
+contract URLhint {
+ function register(uint256 _hash, uint8 idx, uint256 _url) {
+ if (owner[_hash] == 0 || owner[_hash] == msg.sender) {
+ owner[_hash] = msg.sender;
+ url[_hash][idx] = _url;
+ }
+ }
+ mapping (uint256 => address) owner;
+ (uint256 => uint256[256]) url;
+}
+ `
+
+ HashRegCode = "0x609880600c6000396000f30060003560e060020a9004806331e12c2014601f578063d66d6c1014602b57005b6025603d565b60006000f35b6037600435602435605d565b60006000f35b600054600160a060020a0316600014605357605b565b336000819055505b565b600054600160a060020a031633600160a060020a031614607b576094565b8060016000848152602001908152602001600020819055505b505056"
+
+ HashRegSrc = `
+contract HashReg {
+ function setowner() {
+ if (owner == 0) {
+ owner = msg.sender;
+ }
+ }
+ function register(uint256 _key, uint256 _content) {
+ if (msg.sender == owner) {
+ content[_key] = _content;
+ }
+ }
+ address owner;
+ mapping (uint256 => uint256) content;
+}
+`
+
+ GlobalRegistrarSrc = `
+//sol
+
+import "owned";
+
+contract NameRegister {
+ function addr(bytes32 _name) constant returns (address o_owner) {}
+ function name(address _owner) constant returns (bytes32 o_name) {}
+}
+
+contract Registrar is NameRegister {
+ event Changed(bytes32 indexed name);
+ event PrimaryChanged(bytes32 indexed name, address indexed addr);
+
+ function owner(bytes32 _name) constant returns (address o_owner) {}
+ function addr(bytes32 _name) constant returns (address o_address) {}
+ function subRegistrar(bytes32 _name) constant returns (address o_subRegistrar) {}
+ function content(bytes32 _name) constant returns (bytes32 o_content) {}
+
+ function name(address _owner) constant returns (bytes32 o_name) {}
+}
+
+contract GlobalRegistrar is Registrar {
+ struct Record {
+ address owner;
+ address primary;
+ address subRegistrar;
+ bytes32 content;
+ uint value;
+ uint renewalDate;
+ }
+
+ function Registrar() {
+ // TODO: Populate with hall-of-fame.
+ }
+
+ function reserve(bytes32 _name) {
+ // Don't allow the same name to be overwritten.
+ // TODO: bidding mechanism
+ if (m_toRecord[_name].owner == 0) {
+ m_toRecord[_name].owner = msg.sender;
+ Changed(_name);
+ }
+ }
+
+ /*
+ TODO
+ > 12 chars: free
+ <= 12 chars: auction:
+ 1. new names are auctioned
+ - 7 day period to collect all bid bytes32es + deposits
+ - 1 day period to collect all bids to be considered (validity requires associated deposit to be >10% of bid)
+ - all valid bids are burnt except highest - difference between that and second highest is returned to winner
+ 2. remember when last auctioned/renewed
+ 3. anyone can force renewal process:
+ - 7 day period to collect all bid bytes32es + deposits
+ - 1 day period to collect all bids & full amounts - bids only uncovered if sufficiently high.
+ - 1% of winner burnt; original owner paid rest.
+ */
+
+ modifier onlyrecordowner(bytes32 _name) { if (m_toRecord[_name].owner == msg.sender) _ }
+
+ function transfer(bytes32 _name, address _newOwner) onlyrecordowner(_name) {
+ m_toRecord[_name].owner = _newOwner;
+ Changed(_name);
+ }
+
+ function disown(bytes32 _name) onlyrecordowner(_name) {
+ if (m_toName[m_toRecord[_name].primary] == _name)
+ {
+ PrimaryChanged(_name, m_toRecord[_name].primary);
+ m_toName[m_toRecord[_name].primary] = "";
+ }
+ delete m_toRecord[_name];
+ Changed(_name);
+ }
+
+ function setAddress(bytes32 _name, address _a, bool _primary) onlyrecordowner(_name) {
+ m_toRecord[_name].primary = _a;
+ if (_primary)
+ {
+ PrimaryChanged(_name, _a);
+ m_toName[_a] = _name;
+ }
+ Changed(_name);
+ }
+ function setSubRegistrar(bytes32 _name, address _registrar) onlyrecordowner(_name) {
+ m_toRecord[_name].subRegistrar = _registrar;
+ Changed(_name);
+ }
+ function setContent(bytes32 _name, bytes32 _content) onlyrecordowner(_name) {
+ m_toRecord[_name].content = _content;
+ Changed(_name);
+ }
+
+ function owner(bytes32 _name) constant returns (address) { return m_toRecord[_name].owner; }
+ function addr(bytes32 _name) constant returns (address) { return m_toRecord[_name].primary; }
+// function subRegistrar(bytes32 _name) constant returns (address) { return m_toRecord[_name].subRegistrar; } // TODO: bring in on next iteration.
+ function register(bytes32 _name) constant returns (address) { return m_toRecord[_name].subRegistrar; } // only possible for now
+ function content(bytes32 _name) constant returns (bytes32) { return m_toRecord[_name].content; }
+ function name(address _owner) constant returns (bytes32 o_name) { return m_toName[_owner]; }
+
+ mapping (address => bytes32) m_toName;
+ mapping (bytes32 => Record) m_toRecord;
+}
+`
+ GlobalRegistrarAbi = `[{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"name","outputs":[{"name":"o_name","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"content","outputs":[{"name":"","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"addr","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserve","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"subRegistrar","outputs":[{"name":"o_subRegistrar","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_newOwner","type":"address"}],"name":"transfer","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_registrar","type":"address"}],"name":"setSubRegistrar","outputs":[],"type":"function"},{"constant":false,"inputs":[],"name":"Registrar","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_a","type":"address"},{"name":"_primary","type":"bool"}],"name":"setAddress","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_content","type":"bytes32"}],"name":"setContent","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"disown","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"register","outputs":[{"name":"","type":"address"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"}],"name":"Changed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"addr","type":"address"}],"name":"PrimaryChanged","type":"event"}]`
+
+ GlobalRegistrarCode = `0x610b408061000e6000396000f3006000357c01000000000000000000000000000000000000000000000000000000009004806301984892146100b357806302571be3146100ce5780632dff6941146100ff5780633b3b57de1461011a578063432ced041461014b5780635a3a05bd1461016257806379ce9fac1461019357806389a69c0e146101b0578063b9f37c86146101cd578063be99a980146101de578063c3d014d614610201578063d93e75731461021e578063e1fa8e841461023557005b6100c4600480359060200150610b02565b8060005260206000f35b6100df6004803590602001506109f3565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b610110600480359060200150610ad4565b8060005260206000f35b61012b600480359060200150610a3e565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b61015c600480359060200150610271565b60006000f35b610173600480359060200150610266565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b6101aa600480359060200180359060200150610341565b60006000f35b6101c7600480359060200180359060200150610844565b60006000f35b6101d860045061026e565b60006000f35b6101fb6004803590602001803590602001803590602001506106de565b60006000f35b61021860048035906020018035906020015061092c565b60006000f35b61022f600480359060200150610429565b60006000f35b610246600480359060200150610a89565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b60005b919050565b5b565b60006001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561033d57336001600050600083815260200190815260200160002060005060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550807fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b5b50565b813373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561042357816001600050600085815260200190815260200160002060005060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b5050565b803373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156106d95781600060005060006001600050600086815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000505414156105fd576001600050600083815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16827ff63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a85456040604090036040a36000600060005060006001600050600086815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050819055505b6001600050600083815260200190815260200160002060006000820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556002820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556003820160005060009055600482016000506000905560058201600050600090555050817fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b50565b823373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561083d57826001600050600086815260200190815260200160002060005060010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908302179055508115610811578273ffffffffffffffffffffffffffffffffffffffff16847ff63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a85456040604090036040a383600060005060008573ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050819055505b837fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b505050565b813373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561092657816001600050600085815260200190815260200160002060005060020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b5050565b813373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156109ed57816001600050600085815260200190815260200160002060005060030160005081905550827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b5050565b60006001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050610a39565b919050565b60006001600050600083815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050610a84565b919050565b60006001600050600083815260200190815260200160002060005060020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050610acf565b919050565b600060016000506000838152602001908152602001600020600050600301600050549050610afd565b919050565b6000600060005060008373ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050549050610b3b565b91905056`
+)
diff --git a/common/registrar/ethreg/ethreg.go b/common/registrar/ethreg/ethreg.go
new file mode 100644
index 000000000..f5ad3345b
--- /dev/null
+++ b/common/registrar/ethreg/ethreg.go
@@ -0,0 +1,32 @@
+package ethreg
+
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common/registrar"
+ "github.com/ethereum/go-ethereum/xeth"
+)
+
+// implements a versioned Registrar on an archiving full node
+type EthReg struct {
+ backend *xeth.XEth
+ registry *registrar.Registrar
+}
+
+func New(xe *xeth.XEth) (self *EthReg) {
+ self = &EthReg{backend: xe}
+ self.registry = registrar.New(xe)
+ return
+}
+
+func (self *EthReg) Registry() *registrar.Registrar {
+ return self.registry
+}
+
+func (self *EthReg) Resolver(n *big.Int) *registrar.Registrar {
+ xe := self.backend
+ if n != nil {
+ xe = self.backend.AtStateNum(n.Int64())
+ }
+ return registrar.New(xe)
+}
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):], ""
+}
diff --git a/common/registrar/registrar_test.go b/common/registrar/registrar_test.go
new file mode 100644
index 000000000..5612e691c
--- /dev/null
+++ b/common/registrar/registrar_test.go
@@ -0,0 +1,96 @@
+package registrar
+
+import (
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+)
+
+type testBackend struct {
+ // contracts mock
+ contracts map[string](map[string]string)
+}
+
+var (
+ text = "test"
+ codehash = common.StringToHash("1234")
+ hash = common.BytesToHash(crypto.Sha3([]byte(text)))
+ url = "bzz://bzzhash/my/path/contr.act"
+)
+
+func NewTestBackend() *testBackend {
+ HashRegAddr = common.BigToAddress(common.Big0).Hex() //[2:]
+ UrlHintAddr = common.BigToAddress(common.Big1).Hex() //[2:]
+ self := &testBackend{}
+ self.contracts = make(map[string](map[string]string))
+
+ self.contracts[HashRegAddr[2:]] = make(map[string]string)
+ key := storageAddress(storageMapping(storageIdx2Addr(1), codehash[:]))
+ self.contracts[HashRegAddr[2:]][key] = hash.Hex()
+
+ self.contracts[UrlHintAddr[2:]] = make(map[string]string)
+ mapaddr := storageMapping(storageIdx2Addr(1), hash[:])
+
+ key = storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(0)))
+ self.contracts[UrlHintAddr[2:]][key] = common.ToHex([]byte(url))
+ key = storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(1)))
+ self.contracts[UrlHintAddr[2:]][key] = "0x0"
+ return self
+}
+
+func (self *testBackend) StorageAt(ca, sa string) (res string) {
+ c := self.contracts[ca]
+ if c == nil {
+ return
+ }
+ res = c[sa]
+ return
+}
+
+func (self *testBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
+ return "", nil
+}
+
+func (self *testBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error) {
+ return "", "", nil
+}
+
+func TestSetGlobalRegistrar(t *testing.T) {
+ b := NewTestBackend()
+ res := New(b)
+ err := res.SetGlobalRegistrar("addresshex", common.BigToAddress(common.Big1))
+ if err != nil {
+ t.Errorf("unexpected error: %v'", err)
+ }
+}
+
+func TestHashToHash(t *testing.T) {
+ b := NewTestBackend()
+ res := New(b)
+ // res.SetHashReg()
+
+ got, err := res.HashToHash(codehash)
+ if err != nil {
+ t.Errorf("expected no error, got %v", err)
+ } else {
+ if got != hash {
+ t.Errorf("incorrect result, expected '%v', got '%v'", hash.Hex(), got.Hex())
+ }
+ }
+}
+
+func TestHashToUrl(t *testing.T) {
+ b := NewTestBackend()
+ res := New(b)
+ // res.SetUrlHint()
+
+ got, err := res.HashToUrl(hash)
+ if err != nil {
+ t.Errorf("expected error, got %v", err)
+ } else {
+ if got != url {
+ t.Errorf("incorrect result, expected '%v', got '%s'", url, got)
+ }
+ }
+}