Resolver: registries use global registrar

* global registrar now in resolver, console jsre refers to resolver.abi/addr, geth/contracts.go removed
* add Call to resolver backend interface
* global registrar name registry interface in resolver
* the hashReg and UrlHing contracts now initialised from global registry
* if not registered one can call the CreateContracts which creates them, reserved and registers them
* tests
* newRegistry returns the contract addresses for HashReg and UrlHint
This commit is contained in:
zelig 2015-05-22 04:30:51 +01:00
parent b2b9b3b567
commit 3aac38f60f
8 changed files with 200 additions and 43 deletions

View file

@ -781,22 +781,22 @@ func (js *jsre) newRegistry(call otto.FunctionCall) otto.Value {
if len(call.ArgumentList) != 1 { if len(call.ArgumentList) != 1 {
fmt.Println("requires 1 argument: admin.contractInfo.newRegistry(adminaddress)") fmt.Println("requires 1 argument: admin.contractInfo.newRegistry(adminaddress)")
return otto.FalseValue() return otto.UndefinedValue()
} }
addr, err := call.Argument(0).ToString() addr, err := call.Argument(0).ToString()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return otto.FalseValue() return otto.UndefinedValue()
} }
var hashReg, urlHint string
registry := resolver.New(js.xeth) registry := resolver.New(js.xeth)
err = registry.CreateContracts(common.HexToAddress(addr)) hashReg, urlHint, err = registry.CreateContracts(common.HexToAddress(addr))
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return otto.FalseValue() return otto.UndefinedValue()
} }
return otto.TrueValue() return js.re.ToVal([]string{hashReg, urlHint})
} }
// 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`

View file

@ -1,6 +0,0 @@
package main
var (
globalRegistrar = `var GlobalRegistrar = web3.eth.contract([{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"name","outputs":[{"name":"o_name","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"content","outputs":[{"name":"","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"addr","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserve","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"subRegistrar","outputs":[{"name":"o_subRegistrar","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_newOwner","type":"address"}],"name":"transfer","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_registrar","type":"address"}],"name":"setSubRegistrar","outputs":[],"type":"function"},{"constant":false,"inputs":[],"name":"Registrar","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_a","type":"address"},{"name":"_primary","type":"bool"}],"name":"setAddress","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_content","type":"bytes32"}],"name":"setContent","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"disown","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"register","outputs":[{"name":"","type":"address"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"}],"name":"Changed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"addr","type":"address"}],"name":"PrimaryChanged","type":"event"}]);`
globalRegistrarAddr = "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
)

View file

@ -29,6 +29,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/natspec" "github.com/ethereum/go-ethereum/common/natspec"
"github.com/ethereum/go-ethereum/common/resolver"
"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"
@ -141,7 +142,7 @@ var net = web3.net;
utils.Fatalf("Error setting namespaces: %v", err) utils.Fatalf("Error setting namespaces: %v", err)
} }
js.re.Eval(globalRegistrar + "registrar = GlobalRegistrar.at(\"" + globalRegistrarAddr + "\");") js.re.Eval(resolver.GlobalRegistrar + "registrar = GlobalRegistrar.at(\"" + resolver.GlobalRegistrarAddr + "\");")
} }
var ds, _ = docserver.New("/") var ds, _ = docserver.New("/")

View file

@ -305,7 +305,7 @@ func TestContract(t *testing.T) {
checkEvalJSON( checkEvalJSON(
t, repl, t, repl,
`contractaddress = eth.sendTransaction({from: primary, data: contract.code })`, `contractaddress = eth.sendTransaction({from: primary, data: contract.code })`,
`"0x5dcaace5982778b409c524873b319667eba5d074"`, `"0x291293d57e0a0ab47effe97c02577f90d9211567"`,
) )
callSetup := `abiDef = JSON.parse('[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]'); callSetup := `abiDef = JSON.parse('[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]');
@ -331,7 +331,7 @@ multiply7 = Multiply7.at(contractaddress);
checkEvalJSON(t, repl, `admin.contractInfo.start()`, `true`) checkEvalJSON(t, repl, `admin.contractInfo.start()`, `true`)
checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary, gas: "1000000", gasPrice: "100000" })`, `undefined`) checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary, gas: "1000000", gasPrice: "100000" })`, `undefined`)
expNotice = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x87e2802265838c7f14bb69eecd2112911af6767907a702eeaa445239fb20711b'): {"params":[{"to":"0x5dcaace5982778b409c524873b319667eba5d074","data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000006"}]}` expNotice = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x87e2802265838c7f14bb69eecd2112911af6767907a702eeaa445239fb20711b'): {"params":[{"to":"0x291293d57e0a0ab47effe97c02577f90d9211567","data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000006"}]}`
if repl.lastConfirm != expNotice { if repl.lastConfirm != expNotice {
t.Errorf("incorrect confirmation message: expected %v, got %v", expNotice, repl.lastConfirm) t.Errorf("incorrect confirmation message: expected %v, got %v", expNotice, repl.lastConfirm)
} }

View file

@ -197,8 +197,11 @@ func TestNatspecE2E(t *testing.T) {
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
// resolver.HashRefContractAddress = "0x0"
// resolver.UrlHintContractAddress = "0x0"
registry := resolver.New(tf.xeth) registry := resolver.New(tf.xeth)
_, err := registry.Register(tf.coinbase, codehash, dochash, "file:///"+testFileName) _, err := registry.RegisterAddrWithUrl(tf.coinbase, codehash, dochash, "file:///"+testFileName)
if err != nil { if err != nil {
t.Errorf("error registering: %v", err) t.Errorf("error registering: %v", err)
} }

View file

@ -33,4 +33,6 @@ const ( // built-in contracts address and code
} }
*/ */
GlobalRegistrar = `var GlobalRegistrar = web3.eth.contract([{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"name","outputs":[{"name":"o_name","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"content","outputs":[{"name":"","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"addr","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserve","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"subRegistrar","outputs":[{"name":"o_subRegistrar","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_newOwner","type":"address"}],"name":"transfer","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_registrar","type":"address"}],"name":"setSubRegistrar","outputs":[],"type":"function"},{"constant":false,"inputs":[],"name":"Registrar","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_a","type":"address"},{"name":"_primary","type":"bool"}],"name":"setAddress","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_content","type":"bytes32"}],"name":"setContent","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"disown","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"register","outputs":[{"name":"","type":"address"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"}],"name":"Changed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"addr","type":"address"}],"name":"PrimaryChanged","type":"event"}]);`
GlobalRegistrarAddr = "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
) )

View file

@ -20,7 +20,10 @@ of a url scheme
*/ */
// // contract addresses will be hardcoded after they're created // // contract addresses will be hardcoded after they're created
var UrlHintContractAddress, HashRegContractAddress string var (
UrlHintContractAddress = "0x0"
HashRegContractAddress = "0x0"
)
const ( const (
txValue = "0" txValue = "0"
@ -28,42 +31,167 @@ const (
txGasPrice = "1000000000000" txGasPrice = "1000000000000"
) )
func abi(s string) string { func abiSignature(s string) string {
return common.ToHex(crypto.Sha3([]byte(s))[:4]) return common.ToHex(crypto.Sha3([]byte(s))[:4])
} }
var ( var (
registerContentHashAbi = abi("register(uint256,uint256)") HashReg = "HashReg"
registerUrlAbi = abi("register(uint256,uint8,uint256)") UrlHint = "UrlHint"
setOwnerAbi = abi("setowner()")
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))
) )
type Backend interface { type Backend interface {
StorageAt(string, string) string StorageAt(string, string) string
Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) 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 { type Resolver struct {
backend Backend backend Backend
} }
func New(eth Backend) *Resolver { func New(eth Backend) (res *Resolver) {
return &Resolver{eth} res = &Resolver{eth}
res.setContracts()
return
} }
// for testing and play temporarily func (self *Resolver) setContracts() {
// ideally the HashReg and UrlHint contracts should be in the genesis block var err error
// if we got build-in support for natspec/contract info if HashRegContractAddress != "0x0" {
// there should be only one of these officially endorsed return
// addresses as constants }
// TODO: could get around this with namereg, check // reset iff error anywhere
func (self *Resolver) CreateContracts(addr common.Address) (err error) { defer func() {
HashRegContractAddress, err = self.backend.Transact(addr.Hex(), "", "", txValue, txGas, txGasPrice, ContractCodeHashReg) 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)
}
// This can be safely called from tests to or private chains to create
// new HashReg and UrlHint contracts (requires transaction)
// It does nothing if addresses are set
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 { if err != nil {
return return
} }
UrlHintContractAddress, err = self.backend.Transact(addr.Hex(), "", "", txValue, txGas, txGasPrice, ContractCodeURLhint) _, 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) 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
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 return
} }
@ -138,7 +266,7 @@ func (self *Resolver) RegisterUrl(address common.Address, hash common.Hash, url
return return
} }
func (self *Resolver) Register(address common.Address, codehash, dochash common.Hash, url string) (txh string, err error) { func (self *Resolver) RegisterAddrWithUrl(address common.Address, codehash, dochash common.Hash, url string) (txh string, err error) {
_, err = self.RegisterContentHash(address, codehash, dochash) _, err = self.RegisterContentHash(address, codehash, dochash)
if err != nil { if err != nil {
@ -151,7 +279,7 @@ func (self *Resolver) Register(address common.Address, codehash, dochash common.
// implemented as direct retrieval from db // implemented as direct retrieval from db
func (self *Resolver) KeyToContentHash(khash common.Hash) (chash common.Hash, err error) { func (self *Resolver) KeyToContentHash(khash common.Hash) (chash common.Hash, err error) {
// look up in hashReg // look up in hashReg
at := common.Bytes2Hex(common.FromHex(HashRegContractAddress)) at := HashRegContractAddress[2:]
key := storageAddress(storageMapping(storageIdx2Addr(1), khash[:])) key := storageAddress(storageMapping(storageIdx2Addr(1), khash[:]))
hash := self.backend.StorageAt(at, key) hash := self.backend.StorageAt(at, key)
@ -172,7 +300,7 @@ func (self *Resolver) ContentHashToUrl(chash common.Hash) (uri string, err error
for len(str) > 0 { for len(str) > 0 {
mapaddr := storageMapping(storageIdx2Addr(1), chash[:]) mapaddr := storageMapping(storageIdx2Addr(1), chash[:])
key := storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(idx))) key := storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(idx)))
hex := self.backend.StorageAt(UrlHintContractAddress, key) hex := self.backend.StorageAt(UrlHintContractAddress[2:], key)
str = string(common.Hex2Bytes(hex[2:])) str = string(common.Hex2Bytes(hex[2:]))
l := len(str) l := len(str)
for (l > 0) && (str[l-1] == 0) { for (l > 0) && (str[l-1] == 0) {
@ -230,3 +358,18 @@ func storageFixedArray(addr, idx []byte) []byte {
func storageAddress(addr []byte) string { func storageAddress(addr []byte) string {
return common.ToHex(addr) 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
}

View file

@ -20,22 +20,22 @@ var (
) )
func NewTestBackend() *testBackend { func NewTestBackend() *testBackend {
HashRegContractAddress = common.BigToAddress(common.Big0).Hex()[2:] HashRegContractAddress = common.BigToAddress(common.Big0).Hex() //[2:]
UrlHintContractAddress = common.BigToAddress(common.Big1).Hex()[2:] UrlHintContractAddress = 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] = make(map[string]string) self.contracts[HashRegContractAddress[2:]] = make(map[string]string)
key := storageAddress(storageMapping(storageIdx2Addr(1), codehash[:])) key := storageAddress(storageMapping(storageIdx2Addr(1), codehash[:]))
self.contracts[HashRegContractAddress][key] = hash.Hex() self.contracts[HashRegContractAddress[2:]][key] = hash.Hex()
self.contracts[UrlHintContractAddress] = make(map[string]string) self.contracts[UrlHintContractAddress[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][key] = common.ToHex([]byte(url)) self.contracts[UrlHintContractAddress[2:]][key] = common.ToHex([]byte(url))
key = storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(1))) key = storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(1)))
self.contracts[UrlHintContractAddress][key] = "0x00" self.contracts[UrlHintContractAddress[2:]][key] = "0x0"
return self return self
} }
@ -52,6 +52,20 @@ func (self *testBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, ga
return "", nil return "", nil
} }
func (self *testBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error) {
return "", "", nil
}
func TestCreateContractsExists(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)
}
}
func TestKeyToContentHash(t *testing.T) { func TestKeyToContentHash(t *testing.T) {
b := NewTestBackend() b := NewTestBackend()
res := New(b) res := New(b)
@ -71,7 +85,7 @@ func TestContentHashToUrl(t *testing.T) {
res := New(b) res := New(b)
got, err := res.ContentHashToUrl(hash) got, err := res.ContentHashToUrl(hash)
if err != nil { if err != nil {
t.Errorf("expected no error, got %v", err) t.Errorf("expected error, got %v", err)
} else { } else {
if got != url { if got != url {
t.Errorf("incorrect result, expected '%v', got '%s'", url, got) t.Errorf("incorrect result, expected '%v', got '%s'", url, got)