Commit 83ee3944 authored by zelig's avatar zelig Committed by Jeffrey Wilcke

Registrar and contractInfo handling

* resolver -> common/registrar
  * global registrar name registry interface
  * add Call to resolver backend interface
  * the hashReg and UrlHing contracts now initialised from global registry
  * initialization of contracts uniform
  * improve errors and more econsistent method names
* common/registrar/ethreg: versioned registrar
* integrate new naming and registrar in natspec
* js console api: setGlobalRegistrar, setHashReg, setUrlHint
* js test TestContract uses mining - tests fixed all pass
* eth/backend: allow PoW test mode (small ethash DAG)
* console jsre refers to resolver.abi/addr,
* cmd/geth/contracts.go moved to common/registrar
parent d764bd05
......@@ -86,7 +86,6 @@ func runBlockTest(ctx *cli.Context) {
}
func runOneBlockTest(ctx *cli.Context, test *tests.BlockTest) (*eth.Ethereum, error) {
// TODO remove in favor of logic contained in tests package
cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
cfg.NewDB = func(path string) (common.Database, error) { return ethdb.NewMemDatabase() }
cfg.MaxPeers = 0 // disable network
......
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"
)
......@@ -32,16 +32,17 @@ 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/registrar"
"github.com/ethereum/go-ethereum/eth"
re "github.com/ethereum/go-ethereum/jsre"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/rpc/api"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/comms"
"github.com/ethereum/go-ethereum/rpc/shared"
"github.com/ethereum/go-ethereum/xeth"
"github.com/peterh/liner"
"github.com/robertkrimen/otto"
"github.com/ethereum/go-ethereum/rpc/shared"
)
type prompter interface {
......@@ -69,6 +70,7 @@ func (r dumbterm) PasswordPrompt(p string) (string, error) {
func (r dumbterm) AppendHistory(string) {}
type jsre struct {
ds *docserver.DocServer
re *re.JSRE
ethereum *eth.Ethereum
xeth *xeth.XEth
......@@ -180,6 +182,7 @@ func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain string, client comms.Et
if f == nil {
f = js
}
js.ds = docserver.New("/")
js.xeth = xeth.New(ethereum, f)
js.wait = js.xeth.UpdateState()
js.client = client
......@@ -331,15 +334,14 @@ func (js *jsre) apiBindings(f xeth.Frontend) error {
utils.Fatalf("Error setting namespaces: %v", err)
}
js.re.Eval(globalRegistrar + "registrar = GlobalRegistrar.at(\"" + globalRegistrarAddr + "\");")
js.re.Eval(`var GlobalRegistrar = eth.contract(` + registrar.GlobalRegistrarAbi + `); registrar = GlobalRegistrar.at("` + registrar.GlobalRegistrarAddr + `");`)
return nil
}
var ds, _ = docserver.New("/")
func (self *jsre) ConfirmTransaction(tx string) bool {
if self.ethereum.NatSpec {
notice := natspec.GetNotice(self.xeth, tx, ds)
notice := natspec.GetNotice(self.xeth, tx, self.ds)
fmt.Println(notice)
answer, _ := self.Prompt("Confirm Transaction [y/n]")
return strings.HasPrefix(strings.Trim(answer, " "), "y")
......
This diff is collapsed.
......@@ -319,12 +319,6 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
utils.PProfPortFlag,
utils.MetricsEnabledFlag,
utils.SolcPathFlag,
utils.GpoMinGasPriceFlag,
utils.GpoMaxGasPriceFlag,
utils.GpoFullBlockRatioFlag,
utils.GpobaseStepDownFlag,
utils.GpobaseStepUpFlag,
utils.GpobaseCorrectionFactorFlag,
}
app.Before = func(ctx *cli.Context) error {
utils.SetupLogger(ctx)
......
......@@ -185,12 +185,12 @@ func (sol *Solidity) Compile(source string) (contracts map[string]*Contract, err
return
}
func ExtractInfo(contract *Contract, filename string) (contenthash common.Hash, err error) {
contractInfo, err := json.Marshal(contract.Info)
func SaveInfo(info *ContractInfo, filename string) (contenthash common.Hash, err error) {
infojson, err := json.Marshal(info)
if err != nil {
return
}
contenthash = common.BytesToHash(crypto.Sha3(contractInfo))
err = ioutil.WriteFile(filename, contractInfo, 0600)
contenthash = common.BytesToHash(crypto.Sha3(infojson))
err = ioutil.WriteFile(filename, infojson, 0600)
return
}
......@@ -72,19 +72,15 @@ func TestNoCompiler(t *testing.T) {
}
}
func TestExtractInfo(t *testing.T) {
func TestSaveInfo(t *testing.T) {
var cinfo ContractInfo
err := json.Unmarshal([]byte(info), &cinfo)
if err != nil {
t.Errorf("%v", err)
}
contract := &Contract{
Code: "",
Info: cinfo,
}
filename := "/tmp/solctest.info.json"
os.Remove(filename)
cinfohash, err := ExtractInfo(contract, filename)
cinfohash, err := SaveInfo(&cinfo, filename)
if err != nil {
t.Errorf("error extracting info: %v", err)
}
......
......@@ -4,34 +4,25 @@ import (
"fmt"
"io/ioutil"
"net/http"
"path/filepath"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
// http://golang.org/pkg/net/http/#RoundTripper
var (
schemes = map[string]func(*DocServer) http.RoundTripper{
// Simple File server from local disk file:///etc/passwd :)
"file": fileServerOnDocRoot,
}
)
func fileServerOnDocRoot(ds *DocServer) http.RoundTripper {
return http.NewFileTransport(http.Dir(ds.DocRoot))
}
type DocServer struct {
*http.Transport
DocRoot string
schemes []string
}
func New(docRoot string) (self *DocServer, err error) {
func New(docRoot string) (self *DocServer) {
self = &DocServer{
Transport: &http.Transport{},
DocRoot: docRoot,
schemes: []string{"file"},
}
err = self.RegisterProtocols(schemes)
self.RegisterProtocol("file", http.NewFileTransport(http.Dir(self.DocRoot)))
return
}
......@@ -45,16 +36,48 @@ func (self *DocServer) Client() *http.Client {
}
}
func (self *DocServer) RegisterProtocols(schemes map[string]func(*DocServer) http.RoundTripper) (err error) {
for scheme, rtf := range schemes {
self.RegisterProtocol(scheme, rtf(self))
func (self *DocServer) RegisterScheme(scheme string, rt http.RoundTripper) {
self.schemes = append(self.schemes, scheme)
self.RegisterProtocol(scheme, rt)
}
func (self *DocServer) HasScheme(scheme string) bool {
for _, s := range self.schemes {
if s == scheme {
return true
}
}
return
return false
}
func (self *DocServer) GetAuthContent(uri string, hash common.Hash) (content []byte, err error) {
// retrieve content
url := uri
fmt.Printf("uri: %v\n", url)
content, err = self.Get(url, "")
if err != nil {
return
}
// check hash to authenticate content
hashbytes := crypto.Sha3(content)
var chash common.Hash
copy(chash[:], hashbytes)
if chash != hash {
content = nil
err = fmt.Errorf("content hash mismatch")
}
return
}
// Get(uri, path) downloads the document at uri, if path is non-empty it
// is interpreted as a filepath to which the contents are saved
func (self *DocServer) Get(uri, path string) (content []byte, err error) {
// retrieve content
resp, err := self.Client().Get(uri)
defer func() {
if resp != nil {
resp.Body.Close()
......@@ -68,13 +91,10 @@ func (self *DocServer) GetAuthContent(uri string, hash common.Hash) (content []b
return
}
// check hash to authenticate content
hashbytes := crypto.Sha3(content)
var chash common.Hash
copy(chash[:], hashbytes)
if chash != hash {
content = nil
err = fmt.Errorf("content hash mismatch")
if path != "" {
var abspath string
abspath, err = filepath.Abs(path)
ioutil.WriteFile(abspath, content, 0700)
}
return
......
......@@ -2,6 +2,7 @@ package docserver
import (
"io/ioutil"
"net/http"
"os"
"testing"
......@@ -15,7 +16,7 @@ func TestGetAuthContent(t *testing.T) {
copy(hash[:], crypto.Sha3([]byte(text)))
ioutil.WriteFile("/tmp/test.content", []byte(text), os.ModePerm)
ds, err := New("/tmp/")
ds := New("/tmp/")
content, err := ds.GetAuthContent("file:///test.content", hash)
if err != nil {
t.Errorf("no error expected, got %v", err)
......@@ -36,3 +37,18 @@ func TestGetAuthContent(t *testing.T) {
}
}
type rt struct{}
func (rt) RoundTrip(req *http.Request) (resp *http.Response, err error) { return }
func TestRegisterScheme(t *testing.T) {
ds := New("/tmp/")
if ds.HasScheme("scheme") {
t.Errorf("expected scheme not to be registered")
}
ds.RegisterScheme("scheme", rt{})
if !ds.HasScheme("scheme") {
t.Errorf("expected scheme to be registered")
}
}
......@@ -9,7 +9,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"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/xeth"
)
......@@ -88,7 +88,7 @@ func New(xeth *xeth.XEth, jsontx string, http *docserver.DocServer) (self *NatSp
}
// also called by admin.contractInfo.get
func FetchDocsForContract(contractAddress string, xeth *xeth.XEth, http *docserver.DocServer) (content []byte, err error) {
func FetchDocsForContract(contractAddress string, xeth *xeth.XEth, ds *docserver.DocServer) (content []byte, err error) {
// retrieve contract hash from state
codehex := xeth.CodeAt(contractAddress)
codeb := xeth.CodeAtBytes(contractAddress)
......@@ -99,20 +99,32 @@ func FetchDocsForContract(contractAddress string, xeth *xeth.XEth, http *docserv
}
codehash := common.BytesToHash(crypto.Sha3(codeb))
// set up nameresolver with natspecreg + urlhint contract addresses
res := resolver.New(xeth)
reg := registrar.New(xeth)
// resolve host via HashReg/UrlHint Resolver
uri, hash, err := res.KeyToUrl(codehash)
hash, err := reg.HashToHash(codehash)
if err != nil {
return
}
if ds.HasScheme("bzz") {
content, err = ds.Get("bzz://"+hash.Hex()[2:], "")
if err == nil { // non-fatal
return
}
err = nil
//falling back to urlhint
}
// get content via http client and authenticate content using hash
content, err = http.GetAuthContent(uri, hash)
uri, err := reg.HashToUrl(hash)
if err != nil {
return
}
// get content via http client and authenticate content using hash
content, err = ds.GetAuthContent(uri, hash)
if err != nil {
return
}
return
}
......
......@@ -10,7 +10,7 @@ import (
"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/resolver"
"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"
......@@ -73,8 +73,7 @@ const (
)
type testFrontend struct {
t *testing.T
// resolver *resolver.Resolver
t *testing.T
ethereum *eth.Ethereum
xeth *xe.XEth
coinbase common.Address
......@@ -91,10 +90,7 @@ func (self *testFrontend) UnlockAccount(acc []byte) bool {
func (self *testFrontend) ConfirmTransaction(tx string) bool {
if self.wantNatSpec {
ds, err := docserver.New("/tmp/")
if err != nil {
self.t.Errorf("Error creating DocServer: %v", err)
}
ds := docserver.New("/tmp/")
self.lastConfirm = GetNotice(self.xeth, tx, ds)
}
return true
......@@ -159,11 +155,16 @@ func testInit(t *testing.T) (self *testFrontend) {
self.stateDb = self.ethereum.ChainManager().State().Copy()
// initialise the registry contracts
// self.resolver.CreateContracts(addr)
resolver.New(self.xeth).CreateContracts(addr)
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()
// t.Logf("HashReg contract registered at %v", resolver.HashRegContractAddress)
// t.Logf("URLHint contract registered at %v", resolver.UrlHintContractAddress)
return
......@@ -192,13 +193,20 @@ func TestNatspecE2E(t *testing.T) {
dochash := common.BytesToHash(crypto.Sha3([]byte(testContractInfo)))
// take the codehash for the contract we wanna test
// codehex := tf.xeth.CodeAt(resolver.HashRegContractAddress)
codeb := tf.xeth.CodeAtBytes(resolver.HashRegContractAddress)
// 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
registry := resolver.New(tf.xeth)
_, err := registry.Register(tf.coinbase, codehash, dochash, "file:///"+testFileName)
// 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)
}
......@@ -209,18 +217,19 @@ func TestNatspecE2E(t *testing.T) {
// now using the same transactions to check confirm messages
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 {
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, resolver.HashRegContractAddress)
_, err = registry.SetOwner(tf.coinbase)
exp := fmt.Sprintf(testExpNotice2, registrar.HashRegAddr)
_, err = reg.SetOwner(tf.coinbase)
if err != nil {
t.Errorf("error setting owner: %v", err)
}
......@@ -230,9 +239,9 @@ func TestNatspecE2E(t *testing.T) {
}
// 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 {
t.Errorf("error registering: %v", err)
}
......
This diff is collapsed.
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)
}
This diff is collapsed.
package resolver
package registrar
import (
"testing"
......@@ -20,22 +20,22 @@ var (
)
func NewTestBackend() *testBackend {
HashRegContractAddress = common.BigToAddress(common.Big0).Hex()[2:]
UrlHintContractAddress = common.BigToAddress(common.Big1).Hex()[2:]
HashRegAddr = common.BigToAddress(common.Big0).Hex() //[2:]
UrlHintAddr = 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[HashRegAddr[2:]] = make(map[string]string)
key := storageAddress(storageMapping(storageIdx2Addr(1), codehash[:]))
self.contracts[HashRegContractAddress][key] = hash.Hex()
self.contracts[HashRegAddr[2:]][key] = hash.Hex()
self.contracts[UrlHintContractAddress] = make(map[string]string)
self.contracts[UrlHintAddr[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[UrlHintAddr[2:]][key] = common.ToHex([]byte(url))
key = storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(1)))
self.contracts[UrlHintContractAddress][key] = "0x00"
self.contracts[UrlHintAddr[2:]][key] = "0x0"
return self
}
......@@ -52,42 +52,45 @@ func (self *testBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, ga
return "", nil
}
func TestKeyToContentHash(t *testing.T) {
func (self *testBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error) {
return "", "", nil
}
func TestSetGlobalRegistrar(t *testing.T) {
b := NewTestBackend()
res := New(b)
got, err := res.KeyToContentHash(codehash)
err := res.SetGlobalRegistrar("addresshex", common.BigToAddress(common.Big1))
if err != nil {
t.Errorf("expected no error, got %v", err)
} else {
if got != hash {
t.Errorf("incorrect result, expected '%v', got '%v'", hash.Hex(), got.Hex())
}
t.Errorf("unexpected error: %v'", err)
}
}
func TestContentHashToUrl(t *testing.T) {
func TestHashToHash(t *testing.T) {
b := NewTestBackend()
res := New(b)
got, err := res.ContentHashToUrl(hash)
// res.SetHashReg()
got, err := res.HashToHash(codehash)
if err != nil {
t.Errorf("expected no error, got %v", err)
} else {
if got != url {
t.Errorf("incorrect result, expected '%v', got '%s'", url, got)
if got != hash {
t.Errorf("incorrect result, expected '%v', got '%v'", hash.Hex(), got.Hex())
}
}
}
func TestKeyToUrl(t *testing.T) {
func TestHashToUrl(t *testing.T) {
b := NewTestBackend()
res := New(b)
got, _, err := res.KeyToUrl(codehash)
// res.SetUrlHint()
got, err := res.HashToUrl(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 \n'%s', got \n'%s'", url, got)
t.Errorf("incorrect result, expected '%v', got '%s'", url, got)
}
}
}
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;
}
*/
)
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)
}
......@@ -70,6 +70,7 @@ type Config struct {
VmDebug bool
NatSpec bool
AutoDAG bool
PowTest bool
MaxPeers int
MaxPendingPeers int
......@@ -221,6 +222,7 @@ type Ethereum struct {
NatSpec bool
DataDir string
AutoDAG bool
PowTest bool
autodagquit chan bool
etherbase common.Address
clientVersion string
......@@ -329,6 +331,7 @@ func New(config *Config) (*Ethereum, error) {
MinerThreads: config.MinerThreads,
SolcPath: config.SolcPath,
AutoDAG: config.AutoDAG,
PowTest: config.PowTest,
GpoMinGasPrice: config.GpoMinGasPrice,
GpoMaxGasPrice: config.GpoMaxGasPrice,
GpoFullBlockRatio: config.GpoFullBlockRatio,
......@@ -337,7 +340,15 @@ func New(config *Config) (*Ethereum, error) {
GpobaseCorrectionFactor: config.GpobaseCorrectionFactor,
}
eth.pow = ethash.New()
if config.PowTest {
glog.V(logger.Info).Infof("ethash used in test mode")
eth.pow, err = ethash.NewForTesting()
if err != nil {
return nil, err
}
} else {
eth.pow = ethash.New()
}
genesis := core.GenesisBlock(uint64(config.GenesisNonce), stateDb)
eth.chainManager, err = core.NewChainManager(genesis, blockDb, stateDb, extraDb, eth.pow, eth.EventMux())
if err != nil {
......
......@@ -3,7 +3,9 @@ package api
import (
"fmt"
"io"
"math/big"
"os"
"time"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
......@@ -244,3 +246,49 @@ func (self *adminApi) StopRPC(req *shared.Request) (interface{}, error) {
comms.StopHttp()
return true, nil
}
func (self *adminApi) SleepBlocks(req *shared.Request) (interface{}, error) {
args := new(SleepBlocksArgs)
if err := self.coder.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
}
var timer <-chan time.Time
var height *big.Int
var err error
if args.Timeout > 0 {
timer = time.NewTimer(time.Duration(args.Timeout) * time.Second).C
}
height = new(big.Int).Add(self.xeth.CurrentBlock().Number(), big.NewInt(args.N))
height, err = sleepBlocks(self.xeth.UpdateState(), height, timer)
if err != nil {
return nil, err
}
return height.Uint64(), nil
}
func sleepBlocks(wait chan *big.Int, height *big.Int, timer <-chan time.Time) (newHeight *big.Int, err error) {
wait <- height
select {
case <-timer:
// if times out make sure the xeth loop does not block
go func() {
select {
case wait <- nil:
case <-wait:
}
}()
return nil, fmt.Errorf("timeout")
case newHeight = <-wait:
}
return
}
// sec, err := call.Argument(0).ToInteger()
// if err != nil {
// fmt.Println(err)
// return otto.FalseValue()
// }
// time.Sleep(time.Duration(sec) * time.Second)
// return otto.UndefinedValue()
// }
......@@ -147,3 +147,34 @@ func (args *StartRPCArgs) UnmarshalJSON(b []byte) (err error) {
return nil
}
type SleepBlocksArgs struct {
N int64
Timeout int64
}
func (args *SleepBlocksArgs) UnmarshalJSON(b []byte) (err error) {
var obj []interface{}
if err := json.Unmarshal(b, &obj); err != nil {
return shared.NewDecodeParamError(err.Error())
}
args.N = 1
args.Timeout = 0
if len(obj) >= 1 {
if n, ok := obj[0].(int64); ok {
args.N = n
} else {
return shared.NewInvalidTypeError("N", "not an integer")
}
}
if len(obj) >= 2 {
if n, ok := obj[1].(int64); ok {
args.N = n
} else {
return shared.NewInvalidTypeError("Timeout", "not an integer")
}
}
return nil
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment