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 {
fmt.Println("requires 1 argument: admin.contractInfo.newRegistry(adminaddress)")
return otto.FalseValue()
return otto.UndefinedValue()
}
addr, err := call.Argument(0).ToString()
if err != nil {
fmt.Println(err)
return otto.FalseValue()
return otto.UndefinedValue()
}
var hashReg, urlHint string
registry := resolver.New(js.xeth)
err = registry.CreateContracts(common.HexToAddress(addr))
hashReg, urlHint, err = registry.CreateContracts(common.HexToAddress(addr))
if err != nil {
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`

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/docserver"
"github.com/ethereum/go-ethereum/common/natspec"
"github.com/ethereum/go-ethereum/common/resolver"
"github.com/ethereum/go-ethereum/eth"
re "github.com/ethereum/go-ethereum/jsre"
"github.com/ethereum/go-ethereum/rpc"
@ -141,7 +142,7 @@ var net = web3.net;
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("/")

View file

@ -305,7 +305,7 @@ func TestContract(t *testing.T) {
checkEvalJSON(
t, repl,
`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"}]');
@ -331,7 +331,7 @@ multiply7 = Multiply7.at(contractaddress);
checkEvalJSON(t, repl, `admin.contractInfo.start()`, `true`)
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 {
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))
// use resolver to register codehash->dochash->url
// test if globalregistry works
// resolver.HashRefContractAddress = "0x0"
// resolver.UrlHintContractAddress = "0x0"
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 {
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
var UrlHintContractAddress, HashRegContractAddress string
var (
UrlHintContractAddress = "0x0"
HashRegContractAddress = "0x0"
)
const (
txValue = "0"
@ -28,42 +31,167 @@ const (
txGasPrice = "1000000000000"
)
func abi(s string) string {
func abiSignature(s string) string {
return common.ToHex(crypto.Sha3([]byte(s))[:4])
}
var (
registerContentHashAbi = abi("register(uint256,uint256)")
registerUrlAbi = abi("register(uint256,uint8,uint256)")
setOwnerAbi = abi("setowner()")
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))
)
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) *Resolver {
return &Resolver{eth}
func New(eth Backend) (res *Resolver) {
res = &Resolver{eth}
res.setContracts()
return
}
// for testing and play temporarily
// ideally the HashReg and UrlHint contracts should be in the genesis block
// if we got build-in support for natspec/contract info
// there should be only one of these officially endorsed
// addresses as constants
// TODO: could get around this with namereg, check
func (self *Resolver) CreateContracts(addr common.Address) (err error) {
HashRegContractAddress, err = self.backend.Transact(addr.Hex(), "", "", txValue, txGas, txGasPrice, ContractCodeHashReg)
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)
}
// 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 {
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)
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
}
@ -138,7 +266,7 @@ func (self *Resolver) RegisterUrl(address common.Address, hash common.Hash, url
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)
if err != nil {
@ -151,7 +279,7 @@ func (self *Resolver) Register(address common.Address, codehash, dochash common.
// implemented as direct retrieval from db
func (self *Resolver) KeyToContentHash(khash common.Hash) (chash common.Hash, err error) {
// look up in hashReg
at := common.Bytes2Hex(common.FromHex(HashRegContractAddress))
at := HashRegContractAddress[2:]
key := storageAddress(storageMapping(storageIdx2Addr(1), khash[:]))
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 {
mapaddr := storageMapping(storageIdx2Addr(1), chash[:])
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:]))
l := len(str)
for (l > 0) && (str[l-1] == 0) {
@ -230,3 +358,18 @@ func storageFixedArray(addr, idx []byte) []byte {
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
}

View file

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