mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 05:06:43 +00:00
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:
parent
56ae10a0e6
commit
018bd46b7f
13 changed files with 754 additions and 568 deletions
38
bzz/api.go
38
bzz/api.go
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
147
common/registrar/contracts.go
Normal file
147
common/registrar/contracts.go
Normal file
File diff suppressed because one or more lines are too long
391
common/registrar/registrar.go
Normal file
391
common/registrar/registrar.go
Normal 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):], ""
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
)
|
||||
|
|
@ -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
|
||||
}
|
||||
Loading…
Reference in a new issue