aboutsummaryrefslogblamecommitdiffstats
path: root/common/resolver/resolver.go
blob: 9016547e10cec644e11199f765c753ff8ffeaaf1 (plain) (tree)
1
2
3
4
5
6
7
8
9
10


                
                         
             

                                                
                                                

                                                     



                                            
                                                                         
                                  


                                                                               
  
 

                                                                
 




































                                                                                                                                
                       
                      
         




















                                                                                                                                
                       
                      
         









                                                         
 
 





































                                                                                                                 

 
                                                                                                                           
 




                                                                     

 

                                           

                                                                                          
                                                                      
                                                                           
                                               

                                           
                                                                                

                      
                                                           


              

                                                                        
                                                                                   
                             




                                                                                       
                                                                          







                                                       
         
 
                          
                                                                                        
         
              

 
                                                                                           
                             
                                              


                       



                                              






                                                       
                                
                                   
                                     

                                
















                                                     
                                 
 
package resolver

import (
    "encoding/binary"
    "fmt"

    "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"
)

/*
Resolver implements the Ethereum DNS mapping
HashReg : Key Hash (hash of domain name or contract code) -> Content Hash
UrlHint : Content Hash -> Url Hint

The resolver is meant to be called by the roundtripper transport implementation
of a url scheme
*/

// // contract addresses will be hardcoded after they're created
var UrlHintContractAddress, HashRegContractAddress string

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)
    if err != nil {
        return
    }
    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)
    if err != nil {
        return
    }
    codehex := common.Bytes2Hex(codehash[:])
    dochex := common.Bytes2Hex(dochash[:])

    data := registerContentHashAbi + codehex + dochex
    return self.backend.Transact(
        address.Hex(),
        HashRegContractAddress,
        "", txValue, txGas, txGasPrice,
        data,
    )
}

// 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
}

func (self *Resolver) Register(address common.Address, codehash, dochash common.Hash, url string) (txh string, err error) {

    _, err = self.RegisterContentHash(address, codehash, dochash)
    if err != nil {
        return
    }
    return self.RegisterUrl(address, dochash, url)
}

// resolution is costless non-transactional
// implemented as direct retrieval from  db
func (self *Resolver) KeyToContentHash(khash common.Hash) (chash common.Hash, err error) {
    // look up in hashReg
    at := common.Bytes2Hex(common.FromHex(HashRegContractAddress))
    key := storageAddress(storageMapping(storageIdx2Addr(1), khash[:]))
    hash := self.backend.StorageAt(at, key)

    if hash == "0x0" || len(hash) < 3 {
        err = fmt.Errorf("content hash not found for '%v'", khash.Hex())
        return
    }
    copy(chash[:], common.Hex2BytesFixed(hash[2:], 32))
    return
}

// retrieves the url-hint for the content hash -
// if we use content addressed storage, this step is no longer necessary
func (self *Resolver) ContentHashToUrl(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(UrlHintContractAddress, 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++
    }

    if len(uri) == 0 {
        err = fmt.Errorf("GetURLhint: URL hint not found for '%v'", chash.Hex())
    }
    return
}

func (self *Resolver) KeyToUrl(key common.Hash) (uri string, hash common.Hash, err error) {
    // look up in urlHint
    hash, err = self.KeyToContentHash(key)
    if err != nil {
        return
    }
    uri, err = self.ContentHashToUrl(hash)
    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)
}