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
50
bzz/api.go
50
bzz/api.go
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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 (
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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