resolver.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. package resolver
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. "github.com/ethereum/go-ethereum/common"
  6. "github.com/ethereum/go-ethereum/crypto"
  7. "github.com/ethereum/go-ethereum/logger"
  8. "github.com/ethereum/go-ethereum/logger/glog"
  9. )
  10. /*
  11. Resolver implements the Ethereum DNS mapping
  12. HashReg : Key Hash (hash of domain name or contract code) -> Content Hash
  13. UrlHint : Content Hash -> Url Hint
  14. The resolver is meant to be called by the roundtripper transport implementation
  15. of a url scheme
  16. */
  17. // // contract addresses will be hardcoded after they're created
  18. var UrlHintContractAddress, HashRegContractAddress string
  19. const (
  20. txValue = "0"
  21. txGas = "100000"
  22. txGasPrice = "1000000000000"
  23. )
  24. func abi(s string) string {
  25. return common.ToHex(crypto.Sha3([]byte(s))[:4])
  26. }
  27. var (
  28. registerContentHashAbi = abi("register(uint256,uint256)")
  29. registerUrlAbi = abi("register(uint256,uint8,uint256)")
  30. setOwnerAbi = abi("setowner()")
  31. )
  32. type Backend interface {
  33. StorageAt(string, string) string
  34. Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error)
  35. }
  36. type Resolver struct {
  37. backend Backend
  38. }
  39. func New(eth Backend) *Resolver {
  40. return &Resolver{eth}
  41. }
  42. // for testing and play temporarily
  43. // ideally the HashReg and UrlHint contracts should be in the genesis block
  44. // if we got build-in support for natspec/contract info
  45. // there should be only one of these officially endorsed
  46. // addresses as constants
  47. // TODO: could get around this with namereg, check
  48. func (self *Resolver) CreateContracts(addr common.Address) (err error) {
  49. HashRegContractAddress, err = self.backend.Transact(addr.Hex(), "", "", txValue, txGas, txGasPrice, ContractCodeHashReg)
  50. if err != nil {
  51. return
  52. }
  53. UrlHintContractAddress, err = self.backend.Transact(addr.Hex(), "", "", txValue, txGas, txGasPrice, ContractCodeURLhint)
  54. glog.V(logger.Detail).Infof("HashReg @ %v\nUrlHint @ %v\n", HashRegContractAddress, UrlHintContractAddress)
  55. return
  56. }
  57. // called as first step in the registration process on HashReg
  58. func (self *Resolver) SetOwner(address common.Address) (txh string, err error) {
  59. return self.backend.Transact(
  60. address.Hex(),
  61. HashRegContractAddress,
  62. "", txValue, txGas, txGasPrice,
  63. setOwnerAbi,
  64. )
  65. }
  66. // registers some content hash to a key/code hash
  67. // e.g., the contract Info combined Json Doc's ContentHash
  68. // to CodeHash of a contract or hash of a domain
  69. // kept
  70. func (self *Resolver) RegisterContentHash(address common.Address, codehash, dochash common.Hash) (txh string, err error) {
  71. _, err = self.SetOwner(address)
  72. if err != nil {
  73. return
  74. }
  75. codehex := common.Bytes2Hex(codehash[:])
  76. dochex := common.Bytes2Hex(dochash[:])
  77. data := registerContentHashAbi + codehex + dochex
  78. return self.backend.Transact(
  79. address.Hex(),
  80. HashRegContractAddress,
  81. "", txValue, txGas, txGasPrice,
  82. data,
  83. )
  84. }
  85. // registers a url to a content hash so that the content can be fetched
  86. // address is used as sender for the transaction and will be the owner of a new
  87. // registry entry on first time use
  88. // FIXME: silently doing nothing if sender is not the owner
  89. // note that with content addressed storage, this step is no longer necessary
  90. // it could be purely
  91. func (self *Resolver) RegisterUrl(address common.Address, hash common.Hash, url string) (txh string, err error) {
  92. hashHex := common.Bytes2Hex(hash[:])
  93. var urlHex string
  94. urlb := []byte(url)
  95. var cnt byte
  96. n := len(urlb)
  97. for n > 0 {
  98. if n > 32 {
  99. n = 32
  100. }
  101. urlHex = common.Bytes2Hex(urlb[:n])
  102. urlb = urlb[n:]
  103. n = len(urlb)
  104. bcnt := make([]byte, 32)
  105. bcnt[31] = cnt
  106. data := registerUrlAbi +
  107. hashHex +
  108. common.Bytes2Hex(bcnt) +
  109. common.Bytes2Hex(common.Hex2BytesFixed(urlHex, 32))
  110. txh, err = self.backend.Transact(
  111. address.Hex(),
  112. UrlHintContractAddress,
  113. "", txValue, txGas, txGasPrice,
  114. data,
  115. )
  116. if err != nil {
  117. return
  118. }
  119. cnt++
  120. }
  121. return
  122. }
  123. func (self *Resolver) Register(address common.Address, codehash, dochash common.Hash, url string) (txh string, err error) {
  124. _, err = self.RegisterContentHash(address, codehash, dochash)
  125. if err != nil {
  126. return
  127. }
  128. return self.RegisterUrl(address, dochash, url)
  129. }
  130. // resolution is costless non-transactional
  131. // implemented as direct retrieval from db
  132. func (self *Resolver) KeyToContentHash(khash common.Hash) (chash common.Hash, err error) {
  133. // look up in hashReg
  134. at := common.Bytes2Hex(common.FromHex(HashRegContractAddress))
  135. key := storageAddress(storageMapping(storageIdx2Addr(1), khash[:]))
  136. hash := self.backend.StorageAt(at, key)
  137. if hash == "0x0" || len(hash) < 3 {
  138. err = fmt.Errorf("content hash not found for '%v'", khash.Hex())
  139. return
  140. }
  141. copy(chash[:], common.Hex2BytesFixed(hash[2:], 32))
  142. return
  143. }
  144. // retrieves the url-hint for the content hash -
  145. // if we use content addressed storage, this step is no longer necessary
  146. func (self *Resolver) ContentHashToUrl(chash common.Hash) (uri string, err error) {
  147. // look up in URL reg
  148. var str string = " "
  149. var idx uint32
  150. for len(str) > 0 {
  151. mapaddr := storageMapping(storageIdx2Addr(1), chash[:])
  152. key := storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(idx)))
  153. hex := self.backend.StorageAt(UrlHintContractAddress, key)
  154. str = string(common.Hex2Bytes(hex[2:]))
  155. l := len(str)
  156. for (l > 0) && (str[l-1] == 0) {
  157. l--
  158. }
  159. str = str[:l]
  160. uri = uri + str
  161. idx++
  162. }
  163. if len(uri) == 0 {
  164. err = fmt.Errorf("GetURLhint: URL hint not found for '%v'", chash.Hex())
  165. }
  166. return
  167. }
  168. func (self *Resolver) KeyToUrl(key common.Hash) (uri string, hash common.Hash, err error) {
  169. // look up in urlHint
  170. hash, err = self.KeyToContentHash(key)
  171. if err != nil {
  172. return
  173. }
  174. uri, err = self.ContentHashToUrl(hash)
  175. return
  176. }
  177. func storageIdx2Addr(varidx uint32) []byte {
  178. data := make([]byte, 32)
  179. binary.BigEndian.PutUint32(data[28:32], varidx)
  180. return data
  181. }
  182. func storageMapping(addr, key []byte) []byte {
  183. data := make([]byte, 64)
  184. copy(data[0:32], key[0:32])
  185. copy(data[32:64], addr[0:32])
  186. sha := crypto.Sha3(data)
  187. return sha
  188. }
  189. func storageFixedArray(addr, idx []byte) []byte {
  190. var carry byte
  191. for i := 31; i >= 0; i-- {
  192. var b byte = addr[i] + idx[i] + carry
  193. if b < addr[i] {
  194. carry = 1
  195. } else {
  196. carry = 0
  197. }
  198. addr[i] = b
  199. }
  200. return addr
  201. }
  202. func storageAddress(addr []byte) string {
  203. return common.ToHex(addr)
  204. }