streamline regirstrar

* initialization of contracts uniform
* versioned registrar
* improve errors and more econsistent method names
* integrate new naming and registrar in natspec, bzz
* versioning support for bzz scheme by Api.Resolve() -> VersionedRegistrar
* full archive nodes can implement VersionedRegistrar  -> xeth.AtStateNum
* js console api: setGlobalRegistrar, setHashReg, setUrlHint
* test fixed all pass
This commit is contained in:
zelig 2015-06-04 15:30:29 +01:00
parent 56ae10a0e6
commit 018bd46b7f
13 changed files with 754 additions and 568 deletions

View file

@ -5,7 +5,7 @@ import (
"bytes"
"fmt"
"io"
"net"
"math/big"
"os"
"os/exec"
"path/filepath"
@ -14,7 +14,7 @@ import (
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/resolver"
"github.com/ethereum/go-ethereum/common/registrar"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover"
@ -23,6 +23,7 @@ import (
var (
hashMatcher = regexp.MustCompile("^[0-9A-Fa-f]{64}")
slashes = regexp.MustCompile("/+")
domainAndVersion = regexp.MustCompile("[@:;,]+")
)
/*
@ -33,7 +34,7 @@ it is the public interface of the dpa which is included in the ethereum stack
type Api struct {
Chunker *TreeChunker
Port string
Resolver *resolver.Resolver
Registrar registrar.VersionedRegistrar
dpa *DPA
netStore *netStore
}
@ -366,12 +367,12 @@ func (self *Api) Upload(lpath, index string) (string, error) {
return hs, err2
}
func (self *Api) Register(sender common.Address, hash common.Hash, domain string) (err error) {
func (self *Api) Register(sender common.Address, domain string, hash common.Hash) (err error) {
domainhash := common.BytesToHash(crypto.Sha3([]byte(domain)))
if self.Resolver != nil {
if self.Registrar != nil {
dpaLogger.Debugf("Swarm: host '%s' (hash: '%v') to be registered as '%v'", domain, domainhash.Hex(), hash.Hex())
_, err = self.Resolver.RegisterContentHash(sender, domainhash, hash)
_, err = self.Registrar.Registry().SetHashToHash(sender, domainhash, hash)
} else {
err = fmt.Errorf("no registry: %v", err)
}
@ -381,26 +382,21 @@ func (self *Api) Register(sender common.Address, hash common.Hash, domain string
type errResolve error
func (self *Api) Resolve(hostPort string) (contentHash Key, err error) {
var host string
host, _, err = net.SplitHostPort(hostPort)
if err != nil {
porterr := "missing port in address " + hostPort
if err.Error() == porterr {
host = hostPort
err = nil
} else {
err = fmt.Errorf("invalid host '%s': %v (<>'%s')", hostPort, err, porterr)
return
}
}
host := hostPort
if hashMatcher.MatchString(host) {
contentHash = Key(common.Hex2Bytes(host))
dpaLogger.Debugf("Swarm: host is a contentHash: '%064x'", contentHash)
} else {
if self.Resolver != nil {
hostHash := common.BytesToHash(crypto.Sha3([]byte(host)))
if self.Registrar != nil {
var hash common.Hash
hash, err = self.Resolver.KeyToContentHash(hostHash)
var version *big.Int
parts := domainAndVersion.Split(host, 3)
if len(parts) > 1 && parts[1] != "" {
host = parts[0]
version = common.Big(parts[1])
}
hostHash := common.BytesToHash(crypto.Sha3([]byte(host)))
hash, err = self.Registrar.Resolver(version).HashToHash(hostHash)
if err != nil {
err = fmt.Errorf("unable to resolve '%s': %v", hostPort, err)
}

View file

@ -45,12 +45,12 @@ func (self *JSApi) register(call otto.FunctionCall) otto.Value {
fmt.Println(err)
return otto.UndefinedValue()
}
contenthash, err = call.Argument(1).ToString()
domain, err = call.Argument(1).ToString()
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
domain, err = call.Argument(2).ToString()
contenthash, err = call.Argument(2).ToString()
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
@ -58,7 +58,7 @@ func (self *JSApi) register(call otto.FunctionCall) otto.Value {
hash := common.HexToHash(contenthash)
err = self.api.Register(common.HexToAddress(sender), hash, domain)
err = self.api.Register(common.HexToAddress(sender), domain, hash)
if err != nil {
fmt.Println(err)
return otto.FalseValue()

View file

@ -14,7 +14,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/compiler"
"github.com/ethereum/go-ethereum/common/natspec"
"github.com/ethereum/go-ethereum/common/resolver"
"github.com/ethereum/go-ethereum/common/registrar"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
@ -50,6 +50,9 @@ func (js *jsre) adminBindings() {
admin.Set("verbosity", js.verbosity)
admin.Set("progress", js.downloadProgress)
admin.Set("setSolc", js.setSolc)
admin.Set("setGlobalRegistrar", js.setGlobalRegistrar)
admin.Set("setHashReg", js.setHashReg)
admin.Set("setUrlHint", js.setUrlHint)
admin.Set("contractInfo", struct{}{})
t, _ = admin.Get("contractInfo")
@ -57,7 +60,6 @@ func (js *jsre) adminBindings() {
// newRegistry officially not documented temporary option
cinfo.Set("start", js.startNatSpec)
cinfo.Set("stop", js.stopNatSpec)
cinfo.Set("newRegistry", js.newRegistry)
cinfo.Set("get", js.getContractInfo)
cinfo.Set("saveInfo", js.saveInfo)
cinfo.Set("register", js.register)
@ -173,6 +175,112 @@ func (js *jsre) pendingTransactions(call otto.FunctionCall) otto.Value {
return v
}
func (js *jsre) setGlobalRegistrar(call otto.FunctionCall) otto.Value {
if len(call.ArgumentList) == 0 {
fmt.Println("requires 1 or 2 arguments: admin.setGlobalRegistrar(sender[, contractaddress])")
return otto.UndefinedValue()
}
var namereg, addr string
var sender common.Address
var err error
if len(call.ArgumentList) > 0 {
namereg, err = call.Argument(0).ToString()
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
}
if len(call.ArgumentList) > 1 {
addr, err = call.Argument(1).ToString()
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
sender = common.HexToAddress(addr)
}
reg := registrar.New(js.xeth)
err = reg.SetGlobalRegistrar(namereg, sender)
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
v, _ := call.Otto.ToValue(registrar.GlobalRegistrarAddr)
return v
}
func (js *jsre) setHashReg(call otto.FunctionCall) otto.Value {
if len(call.ArgumentList) > 2 {
fmt.Println("requires 0, 1 or 2 arguments: admin.setHashReg(sender[, contractaddress])")
return otto.UndefinedValue()
}
var hashreg, addr string
var sender common.Address
var err error
if len(call.ArgumentList) > 0 {
hashreg, err = call.Argument(0).ToString()
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
}
if len(call.ArgumentList) > 1 {
addr, err = call.Argument(1).ToString()
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
sender = common.HexToAddress(addr)
}
reg := registrar.New(js.xeth)
sender = common.HexToAddress(addr)
err = reg.SetHashReg(hashreg, sender)
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
v, _ := call.Otto.ToValue(registrar.HashRegAddr)
return v
}
func (js *jsre) setUrlHint(call otto.FunctionCall) otto.Value {
if len(call.ArgumentList) > 2 {
fmt.Println("requires 0, 1 or 2 arguments: admin.setHashReg(sender[, contractaddress])")
return otto.UndefinedValue()
}
var urlhint, addr string
var sender common.Address
var err error
if len(call.ArgumentList) > 0 {
urlhint, err = call.Argument(0).ToString()
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
}
if len(call.ArgumentList) > 1 {
addr, err = call.Argument(1).ToString()
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
sender = common.HexToAddress(addr)
}
reg := registrar.New(js.xeth)
sender = common.HexToAddress(addr)
err = reg.SetUrlHint(urlhint, sender)
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
v, _ := call.Otto.ToValue(registrar.UrlHintAddr)
return v
}
func (js *jsre) resend(call otto.FunctionCall) otto.Value {
if len(call.ArgumentList) == 0 {
fmt.Println("first argument must be a transaction")
@ -760,9 +868,9 @@ func (js *jsre) register(call otto.FunctionCall) otto.Value {
codeb := js.xeth.CodeAtBytes(address)
codeHash := common.BytesToHash(crypto.Sha3(codeb))
contentHash := common.HexToHash(contentHashHex)
registry := resolver.New(js.xeth)
registry := registrar.New(js.xeth)
_, err = registry.RegisterContentHash(common.HexToAddress(sender), codeHash, contentHash)
_, err = registry.SetHashToHash(common.HexToAddress(sender), codeHash, contentHash)
if err != nil {
fmt.Println(err)
return otto.FalseValue()
@ -832,8 +940,8 @@ func (js *jsre) registerUrl(call otto.FunctionCall) otto.Value {
return otto.FalseValue()
}
registry := resolver.New(js.xeth)
_, err = registry.RegisterUrl(common.HexToAddress(sender), common.HexToHash(contenthash), url)
registry := registrar.New(js.xeth)
_, err = registry.SetUrlToHash(common.HexToAddress(sender), common.HexToHash(contenthash), url)
if err != nil {
fmt.Println(err)
return otto.FalseValue()
@ -858,7 +966,7 @@ func (js *jsre) getContractInfo(call otto.FunctionCall) otto.Value {
fmt.Println(err)
return otto.UndefinedValue()
}
var info compiler.ContractInfo
var info interface{}
err = json.Unmarshal(infoDoc, &info)
if err != nil {
fmt.Println(err)
@ -878,29 +986,6 @@ func (js *jsre) stopNatSpec(call otto.FunctionCall) otto.Value {
return otto.TrueValue()
}
func (js *jsre) newRegistry(call otto.FunctionCall) otto.Value {
if len(call.ArgumentList) != 1 {
fmt.Println("requires 1 argument: admin.contractInfo.newRegistry(adminaddress)")
return otto.UndefinedValue()
}
addr, err := call.Argument(0).ToString()
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
var hashReg, urlHint string
registry := resolver.New(js.xeth)
hashReg, urlHint, err = registry.CreateContracts(common.HexToAddress(addr))
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
v, _ := call.Otto.ToValue([]string{hashReg, urlHint})
return v
}
// internal transaction type which will allow us to resend transactions using `eth.resend`
type tx struct {
tx *types.Transaction

View file

@ -31,7 +31,8 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/docserver"
"github.com/ethereum/go-ethereum/common/natspec"
"github.com/ethereum/go-ethereum/common/resolver"
"github.com/ethereum/go-ethereum/common/registrar"
"github.com/ethereum/go-ethereum/common/registrar/ethreg"
"github.com/ethereum/go-ethereum/eth"
re "github.com/ethereum/go-ethereum/jsre"
"github.com/ethereum/go-ethereum/rpc"
@ -92,10 +93,10 @@ func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain string, bzzEnabled bool
js.adminBindings()
if bzzEnabled {
// register the swarm rountripper with the bzz scheme on the docserver
js.ds.RegisterProtocol("bzz", &bzz.RoundTripper{Port: bzzPort})
js.ds.RegisterScheme("bzz", &bzz.RoundTripper{Port: bzzPort})
// swarm js bindings
bzz.NewJSApi(js.re, js.ethereum.Swarm)
js.ethereum.Swarm.Resolver = resolver.New(xeth.New(ethereum, f))
js.ethereum.Swarm.Registrar = ethreg.New(js.xeth)
}
if !liner.TerminalSupported() || !interactive {
@ -155,7 +156,7 @@ var net = web3.net;
utils.Fatalf("Error setting namespaces: %v", err)
}
js.re.Eval(resolver.GlobalRegistrar + "registrar = GlobalRegistrar.at(\"" + resolver.GlobalRegistrarAddr + "\");")
js.re.Eval(`var GlobalRegistrar = eth.contract(` + registrar.GlobalRegistrarAbi + `); registrar = GlobalRegistrar.at("` + registrar.GlobalRegistrarAddr + `");`)
}
func (self *jsre) ConfirmTransaction(tx string) bool {

View file

@ -1,14 +1,9 @@
package main
import (
// "io/ioutil"
"os"
// "path"
// "runtime"
"testing"
// "github.com/ethereum/go-ethereum/bzz"
// "github.com/ethereum/go-ethereum/common/resolver"
"github.com/ethereum/go-ethereum/eth"
)

View file

@ -17,7 +17,7 @@ import (
"github.com/ethereum/go-ethereum/common/compiler"
"github.com/ethereum/go-ethereum/common/docserver"
"github.com/ethereum/go-ethereum/common/natspec"
"github.com/ethereum/go-ethereum/common/resolver"
"github.com/ethereum/go-ethereum/common/registrar"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
@ -260,7 +260,19 @@ func TestContract(t *testing.T) {
defer os.RemoveAll(tmp)
coinbase := common.HexToAddress(testAddress)
resolver.New(repl.xeth).CreateContracts(coinbase)
reg := registrar.New(repl.xeth)
err := reg.SetGlobalRegistrar("", coinbase)
if err != nil {
t.Errorf("error setting HashReg: %v", err)
}
err = reg.SetHashReg("", coinbase)
if err != nil {
t.Errorf("error setting HashReg: %v", err)
}
err = reg.SetUrlHint("", coinbase)
if err != nil {
t.Errorf("error setting HashReg: %v", err)
}
source := `contract test {\n` +
" /// @notice Will multiply `a` by 7." + `\n` +
@ -313,13 +325,14 @@ func TestContract(t *testing.T) {
if checkEvalJSON(
t, repl,
`contractaddress = eth.sendTransaction({from: primary, data: contract.code, gasPrice:"1000", gas:"100000", amount:100 })`,
`"0x291293d57e0a0ab47effe97c02577f90d9211567"`,
`contractaddress = eth.sendTransaction({from: primary, data: contract.code})`,
`"0x46d69d55c3c4b86a924a92c9fc4720bb7bce1d74"`,
// `"0x291293d57e0a0ab47effe97c02577f90d9211567"`,
) != nil {
return
}
if !processTxs(repl, t, 7) {
if !processTxs(repl, t, 8) {
return
}
@ -350,7 +363,7 @@ multiply7 = Multiply7.at(contractaddress);
return
}
expNotice = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x87e2802265838c7f14bb69eecd2112911af6767907a702eeaa445239fb20711b'): {"params":[{"to":"0x291293d57e0a0ab47effe97c02577f90d9211567","data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000006"}]}`
expNotice = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x87e2802265838c7f14bb69eecd2112911af6767907a702eeaa445239fb20711b'): {"params":[{"to":"0x46d69d55c3c4b86a924a92c9fc4720bb7bce1d74","data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000006"}]}`
if repl.lastConfirm != expNotice {
t.Errorf("incorrect confirmation message: expected\n%v, got\n%v", expNotice, repl.lastConfirm)
return

View file

@ -9,7 +9,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/docserver"
"github.com/ethereum/go-ethereum/common/resolver"
"github.com/ethereum/go-ethereum/common/registrar"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/xeth"
)
@ -99,15 +99,15 @@ func FetchDocsForContract(contractAddress string, xeth *xeth.XEth, ds *docserver
}
codehash := common.BytesToHash(crypto.Sha3(codeb))
// set up nameresolver with natspecreg + urlhint contract addresses
res := resolver.New(xeth)
reg := registrar.New(xeth)
// resolve host via HashReg/UrlHint Resolver
hash, err := res.KeyToContentHash(codehash)
hash, err := reg.HashToHash(codehash)
if err != nil {
return
}
if ds.HasScheme("bzz") {
content, err = ds.Get("bzz://"+hash.Hex(), "")
content, err = ds.Get("bzz://"+hash.Hex()[2:], "")
if err == nil { // non-fatal
return
}
@ -115,7 +115,7 @@ func FetchDocsForContract(contractAddress string, xeth *xeth.XEth, ds *docserver
//falling back to urlhint
}
uri, err := res.ContentHashToUrl(hash)
uri, err := reg.HashToUrl(hash)
if err != nil {
return
}

View file

@ -10,7 +10,7 @@ import (
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/docserver"
"github.com/ethereum/go-ethereum/common/resolver"
"github.com/ethereum/go-ethereum/common/registrar"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/crypto"
@ -74,7 +74,6 @@ const (
type testFrontend struct {
t *testing.T
// resolver *resolver.Resolver
ethereum *eth.Ethereum
xeth *xe.XEth
coinbase common.Address
@ -156,11 +155,16 @@ func testInit(t *testing.T) (self *testFrontend) {
self.stateDb = self.ethereum.ChainManager().State().Copy()
// initialise the registry contracts
// self.resolver.CreateContracts(addr)
resolver.New(self.xeth).CreateContracts(addr)
reg := registrar.New(self.xeth)
err = reg.SetHashReg("", addr)
if err != nil {
t.Errorf("error creating HashReg: %v", err)
}
err = reg.SetUrlHint("", addr)
if err != nil {
t.Errorf("error creating UrlHint: %v", err)
}
self.applyTxs()
// t.Logf("HashReg contract registered at %v", resolver.HashRegContractAddress)
// t.Logf("URLHint contract registered at %v", resolver.UrlHintContractAddress)
return
@ -189,16 +193,20 @@ func TestNatspecE2E(t *testing.T) {
dochash := common.BytesToHash(crypto.Sha3([]byte(testContractInfo)))
// take the codehash for the contract we wanna test
// codehex := tf.xeth.CodeAt(resolver.HashRegContractAddress)
codeb := tf.xeth.CodeAtBytes(resolver.HashRegContractAddress)
// codehex := tf.xeth.CodeAt(registar.HashRegAddr)
codeb := tf.xeth.CodeAtBytes(registrar.HashRegAddr)
codehash := common.BytesToHash(crypto.Sha3(codeb))
// use resolver to register codehash->dochash->url
// test if globalregistry works
// resolver.HashRefContractAddress = "0x0"
// resolver.UrlHintContractAddress = "0x0"
registry := resolver.New(tf.xeth)
_, err := registry.RegisterAddrWithUrl(tf.coinbase, codehash, dochash, "file:///"+testFileName)
// registrar.HashRefAddr = "0x0"
// registrar.UrlHintAddr = "0x0"
reg := registrar.New(tf.xeth)
_, err := reg.SetHashToHash(tf.coinbase, codehash, dochash)
if err != nil {
t.Errorf("error registering: %v", err)
}
_, err = reg.SetUrlToHash(tf.coinbase, dochash, "file:///"+testFileName)
if err != nil {
t.Errorf("error registering: %v", err)
}
@ -209,18 +217,19 @@ func TestNatspecE2E(t *testing.T) {
// now using the same transactions to check confirm messages
tf.wantNatSpec = true // this is set so now the backend uses natspec confirmation
_, err = registry.RegisterContentHash(tf.coinbase, codehash, dochash)
_, err = reg.SetHashToHash(tf.coinbase, codehash, dochash)
if err != nil {
t.Errorf("error calling contract registry: %v", err)
}
fmt.Printf("GlobalRegistrar: %v, HashReg: %v, UrlHint: %v\n", registrar.GlobalRegistrarAddr, registrar.HashRegAddr, registrar.UrlHintAddr)
if tf.lastConfirm != testExpNotice {
t.Errorf("Wrong confirm message. expected '%v', got '%v'", testExpNotice, tf.lastConfirm)
}
// test unknown method
exp := fmt.Sprintf(testExpNotice2, resolver.HashRegContractAddress)
_, err = registry.SetOwner(tf.coinbase)
exp := fmt.Sprintf(testExpNotice2, registrar.HashRegAddr)
_, err = reg.SetOwner(tf.coinbase)
if err != nil {
t.Errorf("error setting owner: %v", err)
}
@ -230,9 +239,9 @@ func TestNatspecE2E(t *testing.T) {
}
// test unknown contract
exp = fmt.Sprintf(testExpNotice3, resolver.UrlHintContractAddress)
exp = fmt.Sprintf(testExpNotice3, registrar.UrlHintAddr)
_, err = registry.RegisterUrl(tf.coinbase, dochash, "file:///test.content")
_, err = reg.SetUrlToHash(tf.coinbase, dochash, "file:///test.content")
if err != nil {
t.Errorf("error registering: %v", err)
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,391 @@
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 {
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++
}
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):], ""
}

View file

@ -1,4 +1,4 @@
package resolver
package registrar
import (
"testing"
@ -20,22 +20,22 @@ var (
)
func NewTestBackend() *testBackend {
HashRegContractAddress = common.BigToAddress(common.Big0).Hex() //[2:]
UrlHintContractAddress = common.BigToAddress(common.Big1).Hex() //[2:]
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[HashRegContractAddress[2:]] = make(map[string]string)
self.contracts[HashRegAddr[2:]] = make(map[string]string)
key := storageAddress(storageMapping(storageIdx2Addr(1), codehash[:]))
self.contracts[HashRegContractAddress[2:]][key] = hash.Hex()
self.contracts[HashRegAddr[2:]][key] = hash.Hex()
self.contracts[UrlHintContractAddress[2:]] = make(map[string]string)
self.contracts[UrlHintAddr[2:]] = make(map[string]string)
mapaddr := storageMapping(storageIdx2Addr(1), hash[:])
key = storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(0)))
self.contracts[UrlHintContractAddress[2:]][key] = common.ToHex([]byte(url))
self.contracts[UrlHintAddr[2:]][key] = common.ToHex([]byte(url))
key = storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(1)))
self.contracts[UrlHintContractAddress[2:]][key] = "0x0"
self.contracts[UrlHintAddr[2:]][key] = "0x0"
return self
}
@ -56,21 +56,21 @@ func (self *testBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, cod
return "", "", nil
}
func TestCreateContractsExists(t *testing.T) {
func TestSetGlobalRegistrar(t *testing.T) {
b := NewTestBackend()
res := New(b)
_, _, err := res.CreateContracts(common.BigToAddress(common.Big0))
exp := "HashReg already exists at 0x0000000000000000000000000000000000000000"
if err == nil || err.Error() != exp {
t.Errorf("expected error '%s', got '%v'", exp, err)
err := res.SetGlobalRegistrar("addresshex", common.BigToAddress(common.Big1))
if err != nil {
t.Errorf("unexpected error: %v'", err)
}
}
func TestKeyToContentHash(t *testing.T) {
func TestHashToHash(t *testing.T) {
b := NewTestBackend()
res := New(b)
// res.SetHashReg()
got, err := res.KeyToContentHash(codehash)
got, err := res.HashToHash(codehash)
if err != nil {
t.Errorf("expected no error, got %v", err)
} else {
@ -80,10 +80,12 @@ func TestKeyToContentHash(t *testing.T) {
}
}
func TestContentHashToUrl(t *testing.T) {
func TestHashToUrl(t *testing.T) {
b := NewTestBackend()
res := New(b)
got, err := res.ContentHashToUrl(hash)
// res.SetUrlHint()
got, err := res.HashToUrl(hash)
if err != nil {
t.Errorf("expected error, got %v", err)
} else {
@ -92,16 +94,3 @@ func TestContentHashToUrl(t *testing.T) {
}
}
}
func TestKeyToUrl(t *testing.T) {
b := NewTestBackend()
res := New(b)
got, _, err := res.KeyToUrl(codehash)
if err != nil {
t.Errorf("expected no error, got %v", err)
} else {
if got != url {
t.Errorf("incorrect result, expected \n'%s', got \n'%s'", url, got)
}
}
}

View file

@ -1,38 +0,0 @@
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;
}
*/
GlobalRegistrar = `var GlobalRegistrar = web3.eth.contract([{"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"}]);`
GlobalRegistrarAddr = "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
)

View file

@ -1,402 +0,0 @@
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
The resolver is used by
* the roundtripper transport implementation of
url schemes to register and resolve domain names
* contract info retrieval (NatSpec)
The resolver uses 2 contracts on the blockchain:
* 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.
CreateContracts needs to be called once on each blockchain/network once.
Afterwards contract addresses are retrieved from the global registrar
the first time the Resolver constructor is called in a client session
(see setContracts)
*/
var (
UrlHintContractAddress = "0x0"
HashRegContractAddress = "0x0"
)
const (
txValue = "0"
txGas = ""
txGasPrice = ""
)
func abiSignature(s string) string {
return common.ToHex(crypto.Sha3([]byte(s))[:4])
}
var (
HashReg = "HashReg"
UrlHint = "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 = string(make([]byte, 24))
)
// resolver'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)
}
type Resolver struct {
backend Backend
}
func New(eth Backend) (res *Resolver) {
res = &Resolver{eth}
res.setContracts()
return
}
// setContracts retrieves contract addresses from the global registrar
// if they are unset
// the addresses are package level variables so this is done only once every
// client session
func (self *Resolver) setContracts() {
var err error
if HashRegContractAddress != "0x0" {
return
}
// reset iff error anywhere
defer func() {
if err != nil {
HashRegContractAddress = "0x0"
}
}()
hashRegAbi := registerAbi + string(common.Hex2BytesFixed(HashRegContractAddress[2:], 32))
HashRegContractAddress, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", hashRegAbi)
if err != nil {
err = fmt.Errorf("HashReg address not found: %v", err)
return
}
if UrlHintContractAddress != "0x0" {
return
}
// reset iff error anywhere
defer func() {
if err != nil {
UrlHintContractAddress = "0x0"
}
}()
urlHintAbi := registerAbi + string(common.Hex2BytesFixed(UrlHintContractAddress[2:], 32))
UrlHintContractAddress, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", urlHintAbi)
if err != nil {
err = fmt.Errorf("UrlHint address not found: %v", err)
return
}
glog.V(logger.Detail).Infof("HashReg @ %v\nUrlHint @ %v\n", HashRegContractAddress, UrlHintContractAddress)
}
// CreateContracts creates new HashReg and UrlHint contracts
// and registers their address on the global registrar.
// creates 6 transactions which need to be mined too take effect
// It does nothing if addresses are set
// needs to be called only once ever for a blockchain
func (self *Resolver) CreateContracts(addr common.Address) (hashReg, urlHint string, err error) {
if HashRegContractAddress != "0x0" {
err = fmt.Errorf("HashReg already exists at %v", HashRegContractAddress)
return
}
hashReg, err = self.backend.Transact(addr.Hex(), "", "", txValue, txGas, txGasPrice, ContractCodeHashReg)
if err != nil {
return
}
_, err = self.Reserve(addr, HashReg)
if err != nil {
return
}
_, err = self.RegisterAddress(addr, HashReg, common.HexToAddress(hashReg))
if err != nil {
return
}
if UrlHintContractAddress != "0x0" {
err = fmt.Errorf("UrlHint already exists at %v", UrlHintContractAddress)
return
}
urlHint, err = self.backend.Transact(addr.Hex(), "", "", txValue, txGas, txGasPrice, ContractCodeURLhint)
if err != nil {
return
}
_, err = self.Reserve(addr, UrlHint)
if err != nil {
return
}
_, err = self.RegisterAddress(addr, UrlHint, common.HexToAddress(urlHint))
if err != nil {
return
}
HashRegContractAddress = hashReg
UrlHintContractAddress = urlHint
glog.V(logger.Detail).Infof("HashReg @ %v\nUrlHint @ %v\n", HashRegContractAddress, UrlHintContractAddress)
return
}
// Reserve(from, name) reserves name for the sender address in the globalRegistrar
// the tx needs to be mined to take effect
func (self *Resolver) Reserve(address common.Address, name string) (txh string, err error) {
nameHex, extra := encodeName(name, 6)
abi := reserveAbi + nameHex + extra
return self.backend.Transact(
address.Hex(),
GlobalRegistrarAddr,
"", txValue, txGas, txGasPrice,
abi,
)
}
// RegisterAddress(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 *Resolver) RegisterAddress(from common.Address, name string, address common.Address) (txh string, err error) {
nameHex, extra := encodeName(name, 6)
addrHex := encodeAddress(address)
trueHex := make([]byte, 64)
trueHex[63] = 1
abi := registerAbi + nameHex + addrHex + extra
return self.backend.Transact(
from.Hex(),
GlobalRegistrarAddr,
"", txValue, txGas, txGasPrice,
abi,
)
}
// NameToAddr(from, name) queries the registrar for the address on name
func (self *Resolver) NameToAddr(from common.Address, name string) (address common.Address, err error) {
nameHex, extra := encodeName(name, 2)
abi := resolveAbi + nameHex + extra
res, _, err := self.backend.Call(
from.Hex(),
GlobalRegistrarAddr,
txValue, txGas, txGasPrice,
abi,
)
if err != nil {
return
}
address = common.HexToAddress(res)
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
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
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
}
// RegisterAddrWithUrl(address, key, contenthash, url) is a convenience method
// registers key -> conenthash in HashReg and
// registers contenthash -> url in UrlHint
// creates 3 transaction using address as sender
// transactions need to be mined to take effect
func (self *Resolver) RegisterAddrWithUrl(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)
}
// KeyToContentHash(key) resolves contenthash for key (a hash) using HashReg
// 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 := HashRegContractAddress[2:]
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
}
// ContentHashToUrl(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 *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[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++
}
if len(uri) == 0 {
err = fmt.Errorf("GetURLhint: URL hint not found for '%v'", chash.Hex())
}
return
}
// KeyToUrl(key) is a convenience method
// resolves contenthash for key (a hash) using HashReg, then
// resolves the url for contenthash using UrlHint
// resolution is costless non-transactional
// implemented as direct retrieval from db
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)
}
func encodeAddress(address common.Address) string {
return addressAbiPrefix + address.Hex()[2:]
}
func encodeName(name string, index uint8) (nameHex, extra string) {
nameHexBytes := make([]byte, 64)
if len(name) > 32 {
nameHexBytes[63] = byte(index)
extra = common.Bytes2Hex([]byte(name))
} else {
copy(nameHexBytes, []byte(name))
}
return string(nameHexBytes), extra
}