aboutsummaryrefslogtreecommitdiffstats
path: root/common/resolver
diff options
context:
space:
mode:
Diffstat (limited to 'common/resolver')
-rw-r--r--common/resolver/contracts.go36
-rw-r--r--common/resolver/resolver.go128
-rw-r--r--common/resolver/resolver_test.go88
3 files changed, 252 insertions, 0 deletions
diff --git a/common/resolver/contracts.go b/common/resolver/contracts.go
new file mode 100644
index 000000000..4aad95e43
--- /dev/null
+++ b/common/resolver/contracts.go
@@ -0,0 +1,36 @@
+package resolver
+
+const ( // built-in contracts address and code
+ ContractCodeURLhint = "0x60c180600c6000396000f30060003560e060020a90048063300a3bbf14601557005b6024600435602435604435602a565b60006000f35b6000600084815260200190815260200160002054600160a060020a0316600014806078575033600160a060020a03166000600085815260200190815260200160002054600160a060020a0316145b607f5760bc565b336000600085815260200190815260200160002081905550806001600085815260200190815260200160002083610100811060b657005b01819055505b50505056"
+ /*
+ 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;
+ mapping (uint256 => uint256[256]) url;
+ }
+ */
+
+ ContractCodeHashReg = "0x609880600c6000396000f30060003560e060020a9004806331e12c2014601f578063d66d6c1014602b57005b6025603d565b60006000f35b6037600435602435605d565b60006000f35b600054600160a060020a0316600014605357605b565b336000819055505b565b600054600160a060020a031633600160a060020a031614607b576094565b8060016000848152602001908152602001600020819055505b505056"
+ /*
+ 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;
+ }
+ */
+
+)
diff --git a/common/resolver/resolver.go b/common/resolver/resolver.go
new file mode 100644
index 000000000..1e6d03ffb
--- /dev/null
+++ b/common/resolver/resolver.go
@@ -0,0 +1,128 @@
+package resolver
+
+import (
+ "encoding/binary"
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+ xe "github.com/ethereum/go-ethereum/xeth"
+)
+
+/*
+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 string = "0000000000000000000000000000000000000000000000000000000000001234"
+var HashRegContractAddress string = "0000000000000000000000000000000000000000000000000000000000005678"
+
+func CreateContracts(xeth *xe.XEth, addr string) {
+ var err error
+ URLHintContractAddress, err = xeth.Transact(addr, "", "100000000000", "1000000", "100000", ContractCodeURLhint)
+ if err != nil {
+ panic(err)
+ }
+ HashRegContractAddress, err = xeth.Transact(addr, "", "100000000000", "1000000", "100000", ContractCodeHashReg)
+ if err != nil {
+ panic(err)
+ }
+}
+
+type Resolver struct {
+ backend Backend
+ urlHintContractAddress string
+ hashRegContractAddress string
+}
+
+type Backend interface {
+ StorageAt(string, string) string
+}
+
+func New(eth Backend, uhca, nrca string) *Resolver {
+ return &Resolver{eth, uhca, nrca}
+}
+
+func (self *Resolver) KeyToContentHash(khash common.Hash) (chash common.Hash, err error) {
+ // look up in hashReg
+ key := storageAddress(storageMapping(storageIdx2Addr(1), khash[:]))
+ hash := self.backend.StorageAt(self.hashRegContractAddress, key)
+
+ if hash == "0x0" || len(hash) < 3 {
+ err = fmt.Errorf("GetHashReg: content hash not found")
+ return
+ }
+
+ copy(chash[:], common.Hex2BytesFixed(hash[2:], 32))
+ return
+}
+
+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(self.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")
+ }
+ 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])
+ return crypto.Sha3(data)
+}
+
+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)
+}
diff --git a/common/resolver/resolver_test.go b/common/resolver/resolver_test.go
new file mode 100644
index 000000000..f5eb51437
--- /dev/null
+++ b/common/resolver/resolver_test.go
@@ -0,0 +1,88 @@
+package resolver
+
+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 {
+ self := &testBackend{}
+ self.contracts = make(map[string](map[string]string))
+
+ self.contracts[HashRegContractAddress] = make(map[string]string)
+ key := storageAddress(storageMapping(storageIdx2Addr(1), codehash[:]))
+ self.contracts[HashRegContractAddress][key] = hash.Hex()
+
+ self.contracts[URLHintContractAddress] = make(map[string]string)
+ mapaddr := storageMapping(storageIdx2Addr(1), hash[:])
+
+ key = storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(0)))
+ self.contracts[URLHintContractAddress][key] = common.ToHex([]byte(url))
+ key = storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(1)))
+ self.contracts[URLHintContractAddress][key] = "0x00"
+
+ return self
+}
+
+func (self *testBackend) StorageAt(ca, sa string) (res string) {
+ c := self.contracts[ca]
+ if c == nil {
+ return
+ }
+ res = c[sa]
+ return
+}
+
+func TestKeyToContentHash(t *testing.T) {
+ b := NewTestBackend()
+ res := New(b, URLHintContractAddress, HashRegContractAddress)
+
+ got, err := res.KeyToContentHash(codehash)
+ if err != nil {
+ t.Errorf("expected no error, got %v", err)
+ } else {
+ if got != hash {
+ t.Errorf("incorrect result, expected %x, got %x: ", hash.Hex(), got.Hex())
+ }
+ }
+}
+
+func TestContentHashToUrl(t *testing.T) {
+ b := NewTestBackend()
+ res := New(b, URLHintContractAddress, HashRegContractAddress)
+ got, err := res.ContentHashToUrl(hash)
+ if err != nil {
+ t.Errorf("expected no error, got %v", err)
+ } else {
+ if string(got) != url {
+ t.Errorf("incorrect result, expected %v, got %s: ", url, string(got))
+ }
+ }
+}
+
+func TestKeyToUrl(t *testing.T) {
+ b := NewTestBackend()
+ res := New(b, URLHintContractAddress, HashRegContractAddress)
+ got, _, err := res.KeyToUrl(codehash)
+ if err != nil {
+ t.Errorf("expected no error, got %v", err)
+ } else {
+ if string(got) != url {
+ t.Errorf("incorrect result, expected %v, got %s: ", url, string(got))
+ }
+ }
+}