forked from cerc-io/plugeth
8b81ad1fc4
* lines with leading space are ommitted from history * exit processed even with whitespace around * all whitespace lines (not only empty ones) are ignored add 7 missing commands to admin api autocomplete registrar: methods now return proper error if reg addresses are not set. fixes #1457 rpc/console: fix personal.newAccount() regression. Now all comms accept interactive password registrar: add registrar tests for errors crypto: catch AES decryption error on presale wallet import + fix error msg format. fixes #1580 CLI: improve error message when starting a second instance of geth. fixes #1564 cli/accounts: unlock multiple accounts. fixes #1785 * make unlocking multiple accounts work with inline <() fd * passwdfile now correctly read only once * improve logs * fix CLI help text for unlocking fix regression with docRoot / admin API * docRoot/jspath passed to rpc/api ParseApis, which passes onto adminApi * docRoot field for JS console in order to pass when RPC is (re)started * improve flag desc for jspath common/docserver: catch http errors from response fix rpc/api tests common/natspec: fix end to end test (skipped because takes 8s) registrar: fix major regression: * deploy registrars on frontier * register HashsReg and UrlHint in GlobalRegistrar. * set all 3 contract addresses in code * zero out addresses first in tests
437 lines
13 KiB
Go
437 lines
13 KiB
Go
// Copyright 2015 The go-ethereum Authors
|
|
// This file is part of the go-ethereum library.
|
|
//
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
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 the first time any Registrar method is called
|
|
in a client session.
|
|
This is done for frontier by default, otherwise the caller needs to make sure
|
|
the relevant environment initialised the desired contracts
|
|
*/
|
|
var (
|
|
// GlobalRegistrarAddr = "0xc6d9d2cd449a754c494264e1809c50e34d64562b" // olympic
|
|
GlobalRegistrarAddr = "0x33990122638b9132ca29c723bdf037f1a891a70c" // frontier
|
|
HashRegAddr = "0x23bf622b5a65f6060d855fca401133ded3520620" // frontier
|
|
UrlHintAddr = "0x73ed5ef6c010727dfd2671dbb70faac19ec18626" // frontier
|
|
|
|
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) (txhash string, err error) {
|
|
if namereg != "" {
|
|
GlobalRegistrarAddr = namereg
|
|
return
|
|
}
|
|
if zero.MatchString(GlobalRegistrarAddr) {
|
|
if (addr == common.Address{}) {
|
|
err = fmt.Errorf("GlobalRegistrar address not found and sender for creation not given")
|
|
return
|
|
} else {
|
|
txhash, 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) (txhash string, 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)
|
|
var res string
|
|
res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", hashRegAbi)
|
|
if len(res) >= 40 {
|
|
HashRegAddr = "0x" + res[len(res)-40:len(res)]
|
|
}
|
|
if err != nil || zero.MatchString(HashRegAddr) {
|
|
if (addr == common.Address{}) {
|
|
err = fmt.Errorf("HashReg address not found and sender for creation not given")
|
|
return
|
|
}
|
|
|
|
txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "", "", 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 @ txhash %v\n", txhash)
|
|
} else {
|
|
glog.V(logger.Detail).Infof("HashRegAddr found at @ %v\n", HashRegAddr)
|
|
return
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (self *Registrar) SetUrlHint(urlhint string, addr common.Address) (txhash string, 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)
|
|
var res string
|
|
res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", urlHintAbi)
|
|
if len(res) >= 40 {
|
|
UrlHintAddr = "0x" + res[len(res)-40:len(res)]
|
|
}
|
|
if err != nil || zero.MatchString(UrlHintAddr) {
|
|
if (addr == common.Address{}) {
|
|
err = fmt.Errorf("UrlHint address not found and sender for creation not given")
|
|
return
|
|
}
|
|
txhash, 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 @ txhash %v\n", txhash)
|
|
} else {
|
|
glog.V(logger.Detail).Infof("UrlHint found @ %v\n", HashRegAddr)
|
|
return
|
|
}
|
|
}
|
|
|
|
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) {
|
|
if zero.MatchString(GlobalRegistrarAddr) {
|
|
return "", fmt.Errorf("GlobalRegistrar address is not set")
|
|
}
|
|
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) {
|
|
if zero.MatchString(GlobalRegistrarAddr) {
|
|
return "", fmt.Errorf("GlobalRegistrar address is not set")
|
|
}
|
|
|
|
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) {
|
|
if zero.MatchString(GlobalRegistrarAddr) {
|
|
return address, fmt.Errorf("GlobalRegistrar address is not set")
|
|
}
|
|
|
|
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) {
|
|
if zero.MatchString(HashRegAddr) {
|
|
return "", fmt.Errorf("HashReg address is not set")
|
|
}
|
|
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) {
|
|
if zero.MatchString(HashRegAddr) {
|
|
return "", fmt.Errorf("HashReg address is not set")
|
|
}
|
|
|
|
_, 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) {
|
|
if zero.MatchString(UrlHintAddr) {
|
|
return "", fmt.Errorf("UrlHint address is not set")
|
|
}
|
|
|
|
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) {
|
|
if zero.MatchString(HashRegAddr) {
|
|
return common.Hash{}, fmt.Errorf("HashReg address is not set")
|
|
}
|
|
|
|
// 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 || (hash == common.Hash{}.Hex()) {
|
|
err = fmt.Errorf("HashToHash: 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) {
|
|
if zero.MatchString(UrlHintAddr) {
|
|
return "", fmt.Errorf("UrlHint address is not set")
|
|
}
|
|
// 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 := 0
|
|
for (l < len(str)) && (str[l] == 0) {
|
|
l++
|
|
}
|
|
|
|
str = str[l:]
|
|
uri = uri + str
|
|
idx++
|
|
}
|
|
|
|
if len(uri) == 0 {
|
|
err = fmt.Errorf("HashToUrl: 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):], ""
|
|
}
|