| 
									
										
										
										
											2015-03-31 01:57:17 +01:00
										 |  |  | package resolver | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2015-03-31 16:02:55 +01:00
										 |  |  | 	"encoding/binary" | 
					
						
							| 
									
										
										
										
											2015-03-31 01:57:17 +01:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							| 
									
										
										
										
											2015-03-31 16:02:55 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/crypto" | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/logger" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/logger/glog" | 
					
						
							| 
									
										
										
										
											2015-03-31 01:57:17 +01:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* | 
					
						
							|  |  |  | Resolver implements the Ethereum DNS mapping | 
					
						
							| 
									
										
										
										
											2015-04-07 11:50:17 +02:00
										 |  |  | HashReg : Key Hash (hash of domain name or contract code) -> Content Hash | 
					
						
							| 
									
										
										
										
											2015-03-31 01:57:17 +01:00
										 |  |  | UrlHint : Content Hash -> Url Hint | 
					
						
							| 
									
										
										
										
											2015-03-31 16:02:55 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | The resolver is meant to be called by the roundtripper transport implementation | 
					
						
							|  |  |  | of a url scheme | 
					
						
							| 
									
										
										
										
											2015-03-31 01:57:17 +01:00
										 |  |  | */ | 
					
						
							| 
									
										
										
										
											2015-04-14 15:20:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | // // contract addresses will be hardcoded after they're created | 
					
						
							|  |  |  | var UrlHintContractAddress, HashRegContractAddress string | 
					
						
							| 
									
										
										
										
											2015-04-14 15:20:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | const ( | 
					
						
							|  |  |  | 	txValue    = "0" | 
					
						
							|  |  |  | 	txGas      = "100000" | 
					
						
							|  |  |  | 	txGasPrice = "1000000000000" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func abi(s string) string { | 
					
						
							|  |  |  | 	return common.ToHex(crypto.Sha3([]byte(s))[:4]) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							|  |  |  | 	registerContentHashAbi = abi("register(uint256,uint256)") | 
					
						
							|  |  |  | 	registerUrlAbi         = abi("register(uint256,uint8,uint256)") | 
					
						
							|  |  |  | 	setOwnerAbi            = abi("setowner()") | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type Backend interface { | 
					
						
							|  |  |  | 	StorageAt(string, string) string | 
					
						
							|  |  |  | 	Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type Resolver struct { | 
					
						
							|  |  |  | 	backend Backend | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func New(eth Backend) *Resolver { | 
					
						
							|  |  |  | 	return &Resolver{eth} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // for testing and play temporarily | 
					
						
							|  |  |  | // ideally the HashReg and UrlHint contracts should be in the genesis block | 
					
						
							|  |  |  | // if we got build-in support for natspec/contract info | 
					
						
							|  |  |  | // there should be only one of these officially endorsed | 
					
						
							|  |  |  | // addresses as constants | 
					
						
							|  |  |  | // TODO: could get around this with namereg, check | 
					
						
							|  |  |  | func (self *Resolver) CreateContracts(addr common.Address) (err error) { | 
					
						
							|  |  |  | 	HashRegContractAddress, err = self.backend.Transact(addr.Hex(), "", "", txValue, txGas, txGasPrice, ContractCodeHashReg) | 
					
						
							| 
									
										
										
										
											2015-04-14 15:20:52 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 		return | 
					
						
							| 
									
										
										
										
											2015-04-14 15:20:52 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	UrlHintContractAddress, err = self.backend.Transact(addr.Hex(), "", "", txValue, txGas, txGasPrice, ContractCodeURLhint) | 
					
						
							|  |  |  | 	glog.V(logger.Detail).Infof("HashReg @ %v\nUrlHint @ %v\n", HashRegContractAddress, UrlHintContractAddress) | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // called as first step in the registration process on HashReg | 
					
						
							|  |  |  | func (self *Resolver) SetOwner(address common.Address) (txh string, err error) { | 
					
						
							|  |  |  | 	return self.backend.Transact( | 
					
						
							|  |  |  | 		address.Hex(), | 
					
						
							|  |  |  | 		HashRegContractAddress, | 
					
						
							|  |  |  | 		"", txValue, txGas, txGasPrice, | 
					
						
							|  |  |  | 		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 | 
					
						
							|  |  |  | // kept | 
					
						
							|  |  |  | func (self *Resolver) RegisterContentHash(address common.Address, codehash, dochash common.Hash) (txh string, err error) { | 
					
						
							|  |  |  | 	_, err = self.SetOwner(address) | 
					
						
							| 
									
										
										
										
											2015-04-14 15:20:52 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 		return | 
					
						
							| 
									
										
										
										
											2015-04-14 15:20:52 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	codehex := common.Bytes2Hex(codehash[:]) | 
					
						
							|  |  |  | 	dochex := common.Bytes2Hex(dochash[:]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data := registerContentHashAbi + codehex + dochex | 
					
						
							|  |  |  | 	return self.backend.Transact( | 
					
						
							|  |  |  | 		address.Hex(), | 
					
						
							|  |  |  | 		HashRegContractAddress, | 
					
						
							|  |  |  | 		"", txValue, txGas, txGasPrice, | 
					
						
							|  |  |  | 		data, | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2015-04-14 15:20:52 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2015-03-31 01:57:17 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | // 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 | 
					
						
							|  |  |  | // it could be purely | 
					
						
							|  |  |  | func (self *Resolver) RegisterUrl(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(), | 
					
						
							|  |  |  | 			UrlHintContractAddress, | 
					
						
							|  |  |  | 			"", txValue, txGas, txGasPrice, | 
					
						
							|  |  |  | 			data, | 
					
						
							|  |  |  | 		) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		cnt++ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							| 
									
										
										
										
											2015-03-31 16:02:55 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | func (self *Resolver) Register(address common.Address, codehash, dochash common.Hash, url string) (txh string, err error) { | 
					
						
							| 
									
										
										
										
											2015-03-31 01:57:17 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	_, err = self.RegisterContentHash(address, codehash, dochash) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return self.RegisterUrl(address, dochash, url) | 
					
						
							| 
									
										
										
										
											2015-04-01 12:29:16 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | // resolution is costless non-transactional | 
					
						
							|  |  |  | // implemented as direct retrieval from  db | 
					
						
							| 
									
										
										
										
											2015-04-07 11:50:17 +02:00
										 |  |  | func (self *Resolver) KeyToContentHash(khash common.Hash) (chash common.Hash, err error) { | 
					
						
							|  |  |  | 	// look up in hashReg | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	at := common.Bytes2Hex(common.FromHex(HashRegContractAddress)) | 
					
						
							| 
									
										
										
										
											2015-04-17 13:46:38 +02:00
										 |  |  | 	key := storageAddress(storageMapping(storageIdx2Addr(1), khash[:])) | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	hash := self.backend.StorageAt(at, key) | 
					
						
							| 
									
										
										
										
											2015-04-07 11:50:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if hash == "0x0" || len(hash) < 3 { | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 		err = fmt.Errorf("content hash not found for '%v'", khash.Hex()) | 
					
						
							| 
									
										
										
										
											2015-04-07 11:50:17 +02:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	copy(chash[:], common.Hex2BytesFixed(hash[2:], 32)) | 
					
						
							| 
									
										
										
										
											2015-03-31 01:57:17 +01:00
										 |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | // retrieves the url-hint for the content hash - | 
					
						
							|  |  |  | // if we use content addressed storage, this step is no longer necessary | 
					
						
							| 
									
										
										
										
											2015-04-01 12:29:16 +01:00
										 |  |  | func (self *Resolver) ContentHashToUrl(chash common.Hash) (uri string, err error) { | 
					
						
							| 
									
										
										
										
											2015-04-07 11:50:17 +02:00
										 |  |  | 	// look up in URL reg | 
					
						
							| 
									
										
										
										
											2015-04-17 13:46:38 +02:00
										 |  |  | 	var str string = " " | 
					
						
							|  |  |  | 	var idx uint32 | 
					
						
							|  |  |  | 	for len(str) > 0 { | 
					
						
							|  |  |  | 		mapaddr := storageMapping(storageIdx2Addr(1), chash[:]) | 
					
						
							|  |  |  | 		key := storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(idx))) | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 		hex := self.backend.StorageAt(UrlHintContractAddress, key) | 
					
						
							| 
									
										
										
										
											2015-04-17 13:46:38 +02:00
										 |  |  | 		str = string(common.Hex2Bytes(hex[2:])) | 
					
						
							|  |  |  | 		l := len(str) | 
					
						
							|  |  |  | 		for (l > 0) && (str[l-1] == 0) { | 
					
						
							|  |  |  | 			l-- | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		str = str[:l] | 
					
						
							|  |  |  | 		uri = uri + str | 
					
						
							|  |  |  | 		idx++ | 
					
						
							| 
									
										
										
										
											2015-03-31 16:02:55 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-04-01 12:29:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-17 13:46:38 +02:00
										 |  |  | 	if len(uri) == 0 { | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 		err = fmt.Errorf("GetURLhint: URL hint not found for '%v'", chash.Hex()) | 
					
						
							| 
									
										
										
										
											2015-03-31 16:02:55 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							| 
									
										
										
										
											2015-03-31 01:57:17 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-07 11:50:17 +02:00
										 |  |  | func (self *Resolver) KeyToUrl(key common.Hash) (uri string, hash common.Hash, err error) { | 
					
						
							| 
									
										
										
										
											2015-03-31 01:57:17 +01:00
										 |  |  | 	// look up in urlHint | 
					
						
							| 
									
										
										
										
											2015-04-07 11:50:17 +02:00
										 |  |  | 	hash, err = self.KeyToContentHash(key) | 
					
						
							| 
									
										
										
										
											2015-03-31 01:57:17 +01:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-31 16:02:55 +01:00
										 |  |  | 	uri, err = self.ContentHashToUrl(hash) | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-17 13:46:38 +02:00
										 |  |  | func storageIdx2Addr(varidx uint32) []byte { | 
					
						
							|  |  |  | 	data := make([]byte, 32) | 
					
						
							|  |  |  | 	binary.BigEndian.PutUint32(data[28:32], varidx) | 
					
						
							|  |  |  | 	return data | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func storageMapping(addr, key []byte) []byte { | 
					
						
							| 
									
										
										
										
											2015-03-31 16:02:55 +01:00
										 |  |  | 	data := make([]byte, 64) | 
					
						
							| 
									
										
										
										
											2015-04-06 08:01:36 +02:00
										 |  |  | 	copy(data[0:32], key[0:32]) | 
					
						
							| 
									
										
										
										
											2015-04-17 13:46:38 +02:00
										 |  |  | 	copy(data[32:64], addr[0:32]) | 
					
						
							| 
									
										
										
										
											2015-04-22 23:11:11 +01:00
										 |  |  | 	sha := crypto.Sha3(data) | 
					
						
							|  |  |  | 	return sha | 
					
						
							| 
									
										
										
										
											2015-04-17 13:46:38 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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 { | 
					
						
							| 
									
										
										
										
											2015-04-19 19:24:46 +01:00
										 |  |  | 	return common.ToHex(addr) | 
					
						
							| 
									
										
										
										
											2015-03-31 01:57:17 +01:00
										 |  |  | } |