forked from mirror/go-ethereum
commit
916d155467
@ -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" |
||||
) |
@ -0,0 +1,253 @@ |
||||
package natspec |
||||
|
||||
import ( |
||||
"fmt" |
||||
"io/ioutil" |
||||
"os" |
||||
"strings" |
||||
"testing" |
||||
|
||||
"github.com/ethereum/go-ethereum/accounts" |
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/common/docserver" |
||||
"github.com/ethereum/go-ethereum/common/registrar" |
||||
"github.com/ethereum/go-ethereum/core" |
||||
"github.com/ethereum/go-ethereum/core/state" |
||||
"github.com/ethereum/go-ethereum/crypto" |
||||
"github.com/ethereum/go-ethereum/eth" |
||||
xe "github.com/ethereum/go-ethereum/xeth" |
||||
) |
||||
|
||||
const ( |
||||
testBalance = "10000000000000000000" |
||||
|
||||
testFileName = "long_file_name_for_testing_registration_of_URLs_longer_than_32_bytes.content" |
||||
|
||||
testNotice = "Register key `utils.toHex(_key)` <- content `utils.toHex(_content)`" |
||||
|
||||
testExpNotice = "Register key 0xadd1a7d961cff0242089674ec2ef6fca671ab15e1fe80e38859fc815b98d88ab <- content 0xb3a2dea218de5d8bbe6c4645aadbf67b5ab00ecb1a9ec95dbdad6a0eed3e41a7" |
||||
|
||||
testExpNotice2 = `About to submit transaction (NatSpec notice error: abi key does not match any method): {"params":[{"to":"%s","data": "0x31e12c20"}]}` |
||||
|
||||
testExpNotice3 = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x1392c62d05b2d149e22a339c531157ae06b44d39a674cce500064b12b9aeb019'): {"params":[{"to":"%s","data": "0x300a3bbfb3a2dea218de5d8bbe6c4645aadbf67b5ab00ecb1a9ec95dbdad6a0eed3e41a7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066696c653a2f2f2f746573742e636f6e74656e74"}]}` |
||||
) |
||||
|
||||
const ( |
||||
testUserDoc = ` |
||||
{ |
||||
"methods": { |
||||
"register(uint256,uint256)": { |
||||
"notice": "` + testNotice + `" |
||||
} |
||||
}, |
||||
"invariants": [ |
||||
{ "notice": "" } |
||||
], |
||||
"construction": [ |
||||
{ "notice": "" } |
||||
] |
||||
} |
||||
` |
||||
testAbiDefinition = ` |
||||
[{ |
||||
"name": "register", |
||||
"constant": false, |
||||
"type": "function", |
||||
"inputs": [{ |
||||
"name": "_key", |
||||
"type": "uint256" |
||||
}, { |
||||
"name": "_content", |
||||
"type": "uint256" |
||||
}], |
||||
"outputs": [] |
||||
}] |
||||
` |
||||
|
||||
testContractInfo = ` |
||||
{ |
||||
"userDoc": ` + testUserDoc + `, |
||||
"abiDefinition": ` + testAbiDefinition + ` |
||||
} |
||||
` |
||||
) |
||||
|
||||
type testFrontend struct { |
||||
t *testing.T |
||||
ethereum *eth.Ethereum |
||||
xeth *xe.XEth |
||||
coinbase common.Address |
||||
stateDb *state.StateDB |
||||
txc uint64 |
||||
lastConfirm string |
||||
wantNatSpec bool |
||||
} |
||||
|
||||
func (self *testFrontend) UnlockAccount(acc []byte) bool { |
||||
self.ethereum.AccountManager().Unlock(common.BytesToAddress(acc), "password") |
||||
return true |
||||
} |
||||
|
||||
func (self *testFrontend) ConfirmTransaction(tx string) bool { |
||||
if self.wantNatSpec { |
||||
ds := docserver.New("/tmp/") |
||||
self.lastConfirm = GetNotice(self.xeth, tx, ds) |
||||
} |
||||
return true |
||||
} |
||||
|
||||
func testEth(t *testing.T) (ethereum *eth.Ethereum, err error) { |
||||
|
||||
os.RemoveAll("/tmp/eth-natspec/") |
||||
|
||||
err = os.MkdirAll("/tmp/eth-natspec/keystore", os.ModePerm) |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
|
||||
// create a testAddress
|
||||
ks := crypto.NewKeyStorePassphrase("/tmp/eth-natspec/keystore") |
||||
am := accounts.NewManager(ks) |
||||
testAccount, err := am.NewAccount("password") |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
testAddress := strings.TrimPrefix(testAccount.Address.Hex(), "0x") |
||||
|
||||
// set up mock genesis with balance on the testAddress
|
||||
core.GenesisAccounts = []byte(`{ |
||||
"` + testAddress + `": {"balance": "` + testBalance + `"} |
||||
}`) |
||||
|
||||
// only use minimalistic stack with no networking
|
||||
ethereum, err = eth.New(ð.Config{ |
||||
DataDir: "/tmp/eth-natspec", |
||||
AccountManager: am, |
||||
MaxPeers: 0, |
||||
}) |
||||
|
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
|
||||
return |
||||
} |
||||
|
||||
func testInit(t *testing.T) (self *testFrontend) { |
||||
// initialise and start minimal ethereum stack
|
||||
ethereum, err := testEth(t) |
||||
if err != nil { |
||||
t.Errorf("error creating ethereum: %v", err) |
||||
return |
||||
} |
||||
err = ethereum.Start() |
||||
if err != nil { |
||||
t.Errorf("error starting ethereum: %v", err) |
||||
return |
||||
} |
||||
|
||||
// mock frontend
|
||||
self = &testFrontend{t: t, ethereum: ethereum} |
||||
self.xeth = xe.New(ethereum, self) |
||||
|
||||
addr, _ := ethereum.Etherbase() |
||||
self.coinbase = addr |
||||
self.stateDb = self.ethereum.ChainManager().State().Copy() |
||||
|
||||
// initialise the registry contracts
|
||||
reg := registrar.New(self.xeth) |
||||
err = reg.SetHashReg("", addr) |
||||
if err != nil { |
||||
t.Errorf("error creating HashReg: %v", err) |
||||
} |
||||
err = reg.SetUrlHint("", addr) |
||||
if err != nil { |
||||
t.Errorf("error creating UrlHint: %v", err) |
||||
} |
||||
self.applyTxs() |
||||
|
||||
return |
||||
|
||||
} |
||||
|
||||
// this is needed for transaction to be applied to the state in testing
|
||||
// the heavy lifing is done in XEth.ApplyTestTxs
|
||||
// this is fragile,
|
||||
// and does process leaking since xeth loops cannot quit safely
|
||||
// should be replaced by proper mining with testDAG for easy full integration tests
|
||||
func (self *testFrontend) applyTxs() { |
||||
self.txc, self.xeth = self.xeth.ApplyTestTxs(self.stateDb, self.coinbase, self.txc) |
||||
return |
||||
} |
||||
|
||||
// end to end test
|
||||
func TestNatspecE2E(t *testing.T) { |
||||
t.Skip() |
||||
|
||||
tf := testInit(t) |
||||
defer tf.ethereum.Stop() |
||||
|
||||
// create a contractInfo file (mock cloud-deployed contract metadocs)
|
||||
// incidentally this is the info for the registry contract itself
|
||||
ioutil.WriteFile("/tmp/"+testFileName, []byte(testContractInfo), os.ModePerm) |
||||
dochash := common.BytesToHash(crypto.Sha3([]byte(testContractInfo))) |
||||
|
||||
// take the codehash for the contract we wanna test
|
||||
// codehex := tf.xeth.CodeAt(registar.HashRegAddr)
|
||||
codeb := tf.xeth.CodeAtBytes(registrar.HashRegAddr) |
||||
codehash := common.BytesToHash(crypto.Sha3(codeb)) |
||||
|
||||
// use resolver to register codehash->dochash->url
|
||||
// test if globalregistry works
|
||||
// registrar.HashRefAddr = "0x0"
|
||||
// registrar.UrlHintAddr = "0x0"
|
||||
reg := registrar.New(tf.xeth) |
||||
_, err := reg.SetHashToHash(tf.coinbase, codehash, dochash) |
||||
if err != nil { |
||||
t.Errorf("error registering: %v", err) |
||||
} |
||||
_, err = reg.SetUrlToHash(tf.coinbase, dochash, "file:///"+testFileName) |
||||
if err != nil { |
||||
t.Errorf("error registering: %v", err) |
||||
} |
||||
// apply txs to the state
|
||||
tf.applyTxs() |
||||
|
||||
// NatSpec info for register method of HashReg contract installed
|
||||
// now using the same transactions to check confirm messages
|
||||
|
||||
tf.wantNatSpec = true // this is set so now the backend uses natspec confirmation
|
||||
_, err = reg.SetHashToHash(tf.coinbase, codehash, dochash) |
||||
if err != nil { |
||||
t.Errorf("error calling contract registry: %v", err) |
||||
} |
||||
|
||||
fmt.Printf("GlobalRegistrar: %v, HashReg: %v, UrlHint: %v\n", registrar.GlobalRegistrarAddr, registrar.HashRegAddr, registrar.UrlHintAddr) |
||||
if tf.lastConfirm != testExpNotice { |
||||
t.Errorf("Wrong confirm message. expected '%v', got '%v'", testExpNotice, tf.lastConfirm) |
||||
} |
||||
|
||||
// test unknown method
|
||||
exp := fmt.Sprintf(testExpNotice2, registrar.HashRegAddr) |
||||
_, err = reg.SetOwner(tf.coinbase) |
||||
if err != nil { |
||||
t.Errorf("error setting owner: %v", err) |
||||
} |
||||
|
||||
if tf.lastConfirm != exp { |
||||
t.Errorf("Wrong confirm message, expected '%v', got '%v'", exp, tf.lastConfirm) |
||||
} |
||||
|
||||
// test unknown contract
|
||||
exp = fmt.Sprintf(testExpNotice3, registrar.UrlHintAddr) |
||||
|
||||
_, err = reg.SetUrlToHash(tf.coinbase, dochash, "file:///test.content") |
||||
if err != nil { |
||||
t.Errorf("error registering: %v", err) |
||||
} |
||||
|
||||
if tf.lastConfirm != exp { |
||||
t.Errorf("Wrong confirm message, expected '%v', got '%v'", exp, tf.lastConfirm) |
||||
} |
||||
|
||||
} |
File diff suppressed because one or more lines are too long
@ -0,0 +1,32 @@ |
||||
package ethreg |
||||
|
||||
import ( |
||||
"math/big" |
||||
|
||||
"github.com/ethereum/go-ethereum/common/registrar" |
||||
"github.com/ethereum/go-ethereum/xeth" |
||||
) |
||||
|
||||
// implements a versioned Registrar on an archiving full node
|
||||
type EthReg struct { |
||||
backend *xeth.XEth |
||||
registry *registrar.Registrar |
||||
} |
||||
|
||||
func New(xe *xeth.XEth) (self *EthReg) { |
||||
self = &EthReg{backend: xe} |
||||
self.registry = registrar.New(xe) |
||||
return |
||||
} |
||||
|
||||
func (self *EthReg) Registry() *registrar.Registrar { |
||||
return self.registry |
||||
} |
||||
|
||||
func (self *EthReg) Resolver(n *big.Int) *registrar.Registrar { |
||||
xe := self.backend |
||||
if n != nil { |
||||
xe = self.backend.AtStateNum(n.Int64()) |
||||
} |
||||
return registrar.New(xe) |
||||
} |
@ -0,0 +1,392 @@ |
||||
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) (txhash string, 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 { |
||||
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) { |
||||
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 || (hash == common.Hash{}.Hex()) { |
||||
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 := 0 |
||||
for (l < len(str)) && (str[l] == 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,36 +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; |
||||
} |
||||
*/ |
||||
|
||||
) |
@ -1,232 +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 |
||||
HashReg : Key Hash (hash of domain name or contract code) -> Content Hash |
||||
UrlHint : Content Hash -> Url Hint |
||||
|
||||
The resolver is meant to be called by the roundtripper transport implementation |
||||
of a url scheme |
||||
*/ |
||||
|
||||
// // contract addresses will be hardcoded after they're created
|
||||
var UrlHintContractAddress, HashRegContractAddress string |
||||
|
||||
const ( |
||||
txValue = "0" |
||||
txGas = "100000" |
||||
txGasPrice = "1000000000000" |
||||
) |
||||
|
||||
func abi(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()") |
||||
) |
||||
|
||||
type Backend interface { |
||||
StorageAt(string, string) string |
||||
Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) |
||||
} |
||||
|
||||
type Resolver struct { |
||||
backend Backend |
||||
} |
||||
|
||||
func New(eth Backend) *Resolver { |
||||
return &Resolver{eth} |
||||
} |
||||
|
||||
// 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) |
||||
if err != nil { |
||||
return |
||||
} |
||||
UrlHintContractAddress, err = self.backend.Transact(addr.Hex(), "", "", txValue, txGas, txGasPrice, ContractCodeURLhint) |
||||
glog.V(logger.Detail).Infof("HashReg @ %v\nUrlHint @ %v\n", HashRegContractAddress, UrlHintContractAddress) |
||||
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
|
||||
// kept
|
||||
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
|
||||
// it could be purely
|
||||
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 |
||||
} |
||||
|
||||
func (self *Resolver) Register(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) |
||||
} |
||||
|
||||
// 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 := common.Bytes2Hex(common.FromHex(HashRegContractAddress)) |
||||
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 |
||||
} |
||||
|
||||
// retrieves the url-hint for the content hash -
|
||||
// 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, 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 (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) |
||||
} |
Loading…
Reference in new issue