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" "bytes"
"fmt" "fmt"
"io" "io"
"net" "math/big"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
@ -14,15 +14,16 @@ import (
"sync" "sync"
"github.com/ethereum/go-ethereum/common" "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/crypto"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discover"
) )
var ( var (
hashMatcher = regexp.MustCompile("^[0-9A-Fa-f]{64}") hashMatcher = regexp.MustCompile("^[0-9A-Fa-f]{64}")
slashes = regexp.MustCompile("/+") slashes = regexp.MustCompile("/+")
domainAndVersion = regexp.MustCompile("[@:;,]+")
) )
/* /*
@ -31,11 +32,11 @@ on top of the dpa
it is the public interface of the dpa which is included in the ethereum stack it is the public interface of the dpa which is included in the ethereum stack
*/ */
type Api struct { type Api struct {
Chunker *TreeChunker Chunker *TreeChunker
Port string Port string
Resolver *resolver.Resolver Registrar registrar.VersionedRegistrar
dpa *DPA dpa *DPA
netStore *netStore netStore *netStore
} }
/* /*
@ -366,12 +367,12 @@ func (self *Api) Upload(lpath, index string) (string, error) {
return hs, err2 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))) 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()) 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 { } else {
err = fmt.Errorf("no registry: %v", err) 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 type errResolve error
func (self *Api) Resolve(hostPort string) (contentHash Key, err error) { func (self *Api) Resolve(hostPort string) (contentHash Key, err error) {
var host string host := hostPort
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
}
}
if hashMatcher.MatchString(host) { if hashMatcher.MatchString(host) {
contentHash = Key(common.Hex2Bytes(host)) contentHash = Key(common.Hex2Bytes(host))
dpaLogger.Debugf("Swarm: host is a contentHash: '%064x'", contentHash) dpaLogger.Debugf("Swarm: host is a contentHash: '%064x'", contentHash)
} else { } else {
if self.Resolver != nil { if self.Registrar != nil {
hostHash := common.BytesToHash(crypto.Sha3([]byte(host)))
var hash common.Hash 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 { if err != nil {
err = fmt.Errorf("unable to resolve '%s': %v", hostPort, err) 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) fmt.Println(err)
return otto.UndefinedValue() return otto.UndefinedValue()
} }
contenthash, err = call.Argument(1).ToString() domain, err = call.Argument(1).ToString()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return otto.UndefinedValue() return otto.UndefinedValue()
} }
domain, err = call.Argument(2).ToString() contenthash, err = call.Argument(2).ToString()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return otto.UndefinedValue() return otto.UndefinedValue()
@ -58,7 +58,7 @@ func (self *JSApi) register(call otto.FunctionCall) otto.Value {
hash := common.HexToHash(contenthash) 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 { if err != nil {
fmt.Println(err) fmt.Println(err)
return otto.FalseValue() return otto.FalseValue()

View file

@ -14,7 +14,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/compiler" "github.com/ethereum/go-ethereum/common/compiler"
"github.com/ethereum/go-ethereum/common/natspec" "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/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
@ -50,6 +50,9 @@ func (js *jsre) adminBindings() {
admin.Set("verbosity", js.verbosity) admin.Set("verbosity", js.verbosity)
admin.Set("progress", js.downloadProgress) admin.Set("progress", js.downloadProgress)
admin.Set("setSolc", js.setSolc) admin.Set("setSolc", js.setSolc)
admin.Set("setGlobalRegistrar", js.setGlobalRegistrar)
admin.Set("setHashReg", js.setHashReg)
admin.Set("setUrlHint", js.setUrlHint)
admin.Set("contractInfo", struct{}{}) admin.Set("contractInfo", struct{}{})
t, _ = admin.Get("contractInfo") t, _ = admin.Get("contractInfo")
@ -57,7 +60,6 @@ func (js *jsre) adminBindings() {
// newRegistry officially not documented temporary option // newRegistry officially not documented temporary option
cinfo.Set("start", js.startNatSpec) cinfo.Set("start", js.startNatSpec)
cinfo.Set("stop", js.stopNatSpec) cinfo.Set("stop", js.stopNatSpec)
cinfo.Set("newRegistry", js.newRegistry)
cinfo.Set("get", js.getContractInfo) cinfo.Set("get", js.getContractInfo)
cinfo.Set("saveInfo", js.saveInfo) cinfo.Set("saveInfo", js.saveInfo)
cinfo.Set("register", js.register) cinfo.Set("register", js.register)
@ -173,6 +175,112 @@ func (js *jsre) pendingTransactions(call otto.FunctionCall) otto.Value {
return v 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 { func (js *jsre) resend(call otto.FunctionCall) otto.Value {
if len(call.ArgumentList) == 0 { if len(call.ArgumentList) == 0 {
fmt.Println("first argument must be a transaction") 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) codeb := js.xeth.CodeAtBytes(address)
codeHash := common.BytesToHash(crypto.Sha3(codeb)) codeHash := common.BytesToHash(crypto.Sha3(codeb))
contentHash := common.HexToHash(contentHashHex) 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 { if err != nil {
fmt.Println(err) fmt.Println(err)
return otto.FalseValue() return otto.FalseValue()
@ -832,8 +940,8 @@ func (js *jsre) registerUrl(call otto.FunctionCall) otto.Value {
return otto.FalseValue() return otto.FalseValue()
} }
registry := resolver.New(js.xeth) registry := registrar.New(js.xeth)
_, err = registry.RegisterUrl(common.HexToAddress(sender), common.HexToHash(contenthash), url) _, err = registry.SetUrlToHash(common.HexToAddress(sender), common.HexToHash(contenthash), url)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return otto.FalseValue() return otto.FalseValue()
@ -858,7 +966,7 @@ func (js *jsre) getContractInfo(call otto.FunctionCall) otto.Value {
fmt.Println(err) fmt.Println(err)
return otto.UndefinedValue() return otto.UndefinedValue()
} }
var info compiler.ContractInfo var info interface{}
err = json.Unmarshal(infoDoc, &info) err = json.Unmarshal(infoDoc, &info)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@ -878,29 +986,6 @@ func (js *jsre) stopNatSpec(call otto.FunctionCall) otto.Value {
return otto.TrueValue() 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` // internal transaction type which will allow us to resend transactions using `eth.resend`
type tx struct { type tx struct {
tx *types.Transaction tx *types.Transaction

View file

@ -31,7 +31,8 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/docserver" "github.com/ethereum/go-ethereum/common/docserver"
"github.com/ethereum/go-ethereum/common/natspec" "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" "github.com/ethereum/go-ethereum/eth"
re "github.com/ethereum/go-ethereum/jsre" re "github.com/ethereum/go-ethereum/jsre"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
@ -92,10 +93,10 @@ func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain string, bzzEnabled bool
js.adminBindings() js.adminBindings()
if bzzEnabled { if bzzEnabled {
// register the swarm rountripper with the bzz scheme on the docserver // 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 // swarm js bindings
bzz.NewJSApi(js.re, js.ethereum.Swarm) 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 { if !liner.TerminalSupported() || !interactive {
@ -155,7 +156,7 @@ var net = web3.net;
utils.Fatalf("Error setting namespaces: %v", err) 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 { func (self *jsre) ConfirmTransaction(tx string) bool {

View file

@ -1,14 +1,9 @@
package main package main
import ( import (
// "io/ioutil"
"os" "os"
// "path"
// "runtime"
"testing" "testing"
// "github.com/ethereum/go-ethereum/bzz"
// "github.com/ethereum/go-ethereum/common/resolver"
"github.com/ethereum/go-ethereum/eth" "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/compiler"
"github.com/ethereum/go-ethereum/common/docserver" "github.com/ethereum/go-ethereum/common/docserver"
"github.com/ethereum/go-ethereum/common/natspec" "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/core"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
@ -260,7 +260,19 @@ func TestContract(t *testing.T) {
defer os.RemoveAll(tmp) defer os.RemoveAll(tmp)
coinbase := common.HexToAddress(testAddress) 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` + source := `contract test {\n` +
" /// @notice Will multiply `a` by 7." + `\n` + " /// @notice Will multiply `a` by 7." + `\n` +
@ -313,13 +325,14 @@ func TestContract(t *testing.T) {
if checkEvalJSON( if checkEvalJSON(
t, repl, t, repl,
`contractaddress = eth.sendTransaction({from: primary, data: contract.code, gasPrice:"1000", gas:"100000", amount:100 })`, `contractaddress = eth.sendTransaction({from: primary, data: contract.code})`,
`"0x291293d57e0a0ab47effe97c02577f90d9211567"`, `"0x46d69d55c3c4b86a924a92c9fc4720bb7bce1d74"`,
// `"0x291293d57e0a0ab47effe97c02577f90d9211567"`,
) != nil { ) != nil {
return return
} }
if !processTxs(repl, t, 7) { if !processTxs(repl, t, 8) {
return return
} }
@ -350,7 +363,7 @@ multiply7 = Multiply7.at(contractaddress);
return 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 { if repl.lastConfirm != expNotice {
t.Errorf("incorrect confirmation message: expected\n%v, got\n%v", expNotice, repl.lastConfirm) t.Errorf("incorrect confirmation message: expected\n%v, got\n%v", expNotice, repl.lastConfirm)
return return

View file

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

View file

@ -10,7 +10,7 @@ import (
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/docserver" "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"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -73,8 +73,7 @@ const (
) )
type testFrontend struct { type testFrontend struct {
t *testing.T t *testing.T
// resolver *resolver.Resolver
ethereum *eth.Ethereum ethereum *eth.Ethereum
xeth *xe.XEth xeth *xe.XEth
coinbase common.Address coinbase common.Address
@ -156,11 +155,16 @@ func testInit(t *testing.T) (self *testFrontend) {
self.stateDb = self.ethereum.ChainManager().State().Copy() self.stateDb = self.ethereum.ChainManager().State().Copy()
// initialise the registry contracts // initialise the registry contracts
// self.resolver.CreateContracts(addr) reg := registrar.New(self.xeth)
resolver.New(self.xeth).CreateContracts(addr) 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() self.applyTxs()
// t.Logf("HashReg contract registered at %v", resolver.HashRegContractAddress)
// t.Logf("URLHint contract registered at %v", resolver.UrlHintContractAddress)
return return
@ -189,16 +193,20 @@ func TestNatspecE2E(t *testing.T) {
dochash := common.BytesToHash(crypto.Sha3([]byte(testContractInfo))) dochash := common.BytesToHash(crypto.Sha3([]byte(testContractInfo)))
// take the codehash for the contract we wanna test // take the codehash for the contract we wanna test
// codehex := tf.xeth.CodeAt(resolver.HashRegContractAddress) // codehex := tf.xeth.CodeAt(registar.HashRegAddr)
codeb := tf.xeth.CodeAtBytes(resolver.HashRegContractAddress) codeb := tf.xeth.CodeAtBytes(registrar.HashRegAddr)
codehash := common.BytesToHash(crypto.Sha3(codeb)) codehash := common.BytesToHash(crypto.Sha3(codeb))
// use resolver to register codehash->dochash->url // use resolver to register codehash->dochash->url
// test if globalregistry works // test if globalregistry works
// resolver.HashRefContractAddress = "0x0" // registrar.HashRefAddr = "0x0"
// resolver.UrlHintContractAddress = "0x0" // registrar.UrlHintAddr = "0x0"
registry := resolver.New(tf.xeth) reg := registrar.New(tf.xeth)
_, err := registry.RegisterAddrWithUrl(tf.coinbase, codehash, dochash, "file:///"+testFileName) _, 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 { if err != nil {
t.Errorf("error registering: %v", err) t.Errorf("error registering: %v", err)
} }
@ -209,18 +217,19 @@ func TestNatspecE2E(t *testing.T) {
// now using the same transactions to check confirm messages // now using the same transactions to check confirm messages
tf.wantNatSpec = true // this is set so now the backend uses natspec confirmation 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 { if err != nil {
t.Errorf("error calling contract registry: %v", err) 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 { if tf.lastConfirm != testExpNotice {
t.Errorf("Wrong confirm message. expected '%v', got '%v'", testExpNotice, tf.lastConfirm) t.Errorf("Wrong confirm message. expected '%v', got '%v'", testExpNotice, tf.lastConfirm)
} }
// test unknown method // test unknown method
exp := fmt.Sprintf(testExpNotice2, resolver.HashRegContractAddress) exp := fmt.Sprintf(testExpNotice2, registrar.HashRegAddr)
_, err = registry.SetOwner(tf.coinbase) _, err = reg.SetOwner(tf.coinbase)
if err != nil { if err != nil {
t.Errorf("error setting owner: %v", err) t.Errorf("error setting owner: %v", err)
} }
@ -230,9 +239,9 @@ func TestNatspecE2E(t *testing.T) {
} }
// test unknown contract // 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 { if err != nil {
t.Errorf("error registering: %v", err) 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 ( import (
"testing" "testing"
@ -20,22 +20,22 @@ var (
) )
func NewTestBackend() *testBackend { func NewTestBackend() *testBackend {
HashRegContractAddress = common.BigToAddress(common.Big0).Hex() //[2:] HashRegAddr = common.BigToAddress(common.Big0).Hex() //[2:]
UrlHintContractAddress = common.BigToAddress(common.Big1).Hex() //[2:] UrlHintAddr = common.BigToAddress(common.Big1).Hex() //[2:]
self := &testBackend{} self := &testBackend{}
self.contracts = make(map[string](map[string]string)) 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[:])) 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[:]) mapaddr := storageMapping(storageIdx2Addr(1), hash[:])
key = storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(0))) 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))) key = storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(1)))
self.contracts[UrlHintContractAddress[2:]][key] = "0x0" self.contracts[UrlHintAddr[2:]][key] = "0x0"
return self return self
} }
@ -56,21 +56,21 @@ func (self *testBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, cod
return "", "", nil return "", "", nil
} }
func TestCreateContractsExists(t *testing.T) { func TestSetGlobalRegistrar(t *testing.T) {
b := NewTestBackend() b := NewTestBackend()
res := New(b) res := New(b)
_, _, err := res.CreateContracts(common.BigToAddress(common.Big0)) err := res.SetGlobalRegistrar("addresshex", common.BigToAddress(common.Big1))
exp := "HashReg already exists at 0x0000000000000000000000000000000000000000" if err != nil {
if err == nil || err.Error() != exp { t.Errorf("unexpected error: %v'", err)
t.Errorf("expected error '%s', got '%v'", exp, err)
} }
} }
func TestKeyToContentHash(t *testing.T) { func TestHashToHash(t *testing.T) {
b := NewTestBackend() b := NewTestBackend()
res := New(b) res := New(b)
// res.SetHashReg()
got, err := res.KeyToContentHash(codehash) got, err := res.HashToHash(codehash)
if err != nil { if err != nil {
t.Errorf("expected no error, got %v", err) t.Errorf("expected no error, got %v", err)
} else { } else {
@ -80,10 +80,12 @@ func TestKeyToContentHash(t *testing.T) {
} }
} }
func TestContentHashToUrl(t *testing.T) { func TestHashToUrl(t *testing.T) {
b := NewTestBackend() b := NewTestBackend()
res := New(b) res := New(b)
got, err := res.ContentHashToUrl(hash) // res.SetUrlHint()
got, err := res.HashToUrl(hash)
if err != nil { if err != nil {
t.Errorf("expected error, got %v", err) t.Errorf("expected error, got %v", err)
} else { } 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
}