aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/geth/blocktestcmd.go1
-rw-r--r--cmd/geth/contracts.go6
-rw-r--r--cmd/geth/js.go12
-rw-r--r--cmd/geth/js_test.go216
-rw-r--r--cmd/geth/main.go6
-rw-r--r--common/compiler/solidity.go8
-rw-r--r--common/compiler/solidity_test.go8
-rw-r--r--common/docserver/docserver.go70
-rw-r--r--common/docserver/docserver_test.go18
-rw-r--r--common/natspec/natspec.go24
-rw-r--r--common/natspec/natspec_e2e_test.go49
-rw-r--r--common/registrar/contracts.go147
-rw-r--r--common/registrar/ethreg/ethreg.go32
-rw-r--r--common/registrar/registrar.go398
-rw-r--r--common/registrar/registrar_test.go (renamed from common/resolver/resolver_test.go)51
-rw-r--r--common/resolver/contracts.go36
-rw-r--r--common/resolver/resolver.go232
-rw-r--r--eth/backend.go13
-rw-r--r--rpc/api/admin.go48
-rw-r--r--rpc/api/admin_args.go31
20 files changed, 978 insertions, 428 deletions
diff --git a/cmd/geth/blocktestcmd.go b/cmd/geth/blocktestcmd.go
index 116eec2b3..ffea4400e 100644
--- a/cmd/geth/blocktestcmd.go
+++ b/cmd/geth/blocktestcmd.go
@@ -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
diff --git a/cmd/geth/contracts.go b/cmd/geth/contracts.go
deleted file mode 100644
index 1f27838d1..000000000
--- a/cmd/geth/contracts.go
+++ /dev/null
@@ -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"
-)
diff --git a/cmd/geth/js.go b/cmd/geth/js.go
index 01840ebd9..5a5dd75f3 100644
--- a/cmd/geth/js.go
+++ b/cmd/geth/js.go
@@ -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")
diff --git a/cmd/geth/js_test.go b/cmd/geth/js_test.go
index 480f77c91..0b7045ff6 100644
--- a/cmd/geth/js_test.go
+++ b/cmd/geth/js_test.go
@@ -3,21 +3,22 @@ package main
import (
"fmt"
"io/ioutil"
+ "math/big"
"os"
"path/filepath"
"regexp"
"runtime"
"strconv"
"testing"
+ "time"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/compiler"
"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/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"
"github.com/ethereum/go-ethereum/rpc/codec"
@@ -43,7 +44,6 @@ var (
type testjethre struct {
*jsre
- stateDb *state.StateDB
lastConfirm string
ds *docserver.DocServer
}
@@ -64,6 +64,10 @@ func (self *testjethre) ConfirmTransaction(tx string) bool {
}
func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) {
+ return testREPL(t, nil)
+}
+
+func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *eth.Ethereum) {
tmp, err := ioutil.TempDir("", "geth-test")
if err != nil {
t.Fatal(err)
@@ -74,14 +78,19 @@ func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) {
ks := crypto.NewKeyStorePlain(filepath.Join(tmp, "keystore"))
am := accounts.NewManager(ks)
- ethereum, err := eth.New(&eth.Config{
+ conf := &eth.Config{
NodeKey: testNodeKey,
DataDir: tmp,
AccountManager: am,
MaxPeers: 0,
Name: "test",
SolcPath: testSolcPath,
- })
+ PowTest: true,
+ }
+ if config != nil {
+ config(conf)
+ }
+ ethereum, err := eth.New(conf)
if err != nil {
t.Fatal("%v", err)
}
@@ -102,25 +111,16 @@ func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) {
}
assetPath := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext")
- ds, err := docserver.New("/")
- if err != nil {
- t.Errorf("Error creating DocServer: %v", err)
- }
- tf := &testjethre{ds: ds, stateDb: ethereum.ChainManager().State().Copy()}
client := comms.NewInProcClient(codec.JSON)
+ ds := docserver.New("/")
+ tf := &testjethre{ds: ds}
repl := newJSRE(ethereum, assetPath, "", client, false, tf)
tf.jsre = repl
return tmp, tf, ethereum
}
-// this line below is needed for transaction to be applied to the state in testing
-// the heavy lifing is done in XEth.ApplyTestTxs
-// this is fragile, overwriting xeth will result in
-// process leaking since xeth loops cannot quit safely
-// should be replaced by proper mining with testDAG for easy full integration tests
-// txc, self.xeth = self.xeth.ApplyTestTxs(self.xeth.repl.stateDb, coinbase, txc)
-
func TestNodeInfo(t *testing.T) {
+ t.Skip("broken after p2p update")
tmp, repl, ethereum := testJEthRE(t)
if err := ethereum.Start(); err != nil {
t.Fatalf("error starting ethereum: %v", err)
@@ -254,7 +254,7 @@ func TestSignature(t *testing.T) {
}
func TestContract(t *testing.T) {
- t.Skip()
+ // t.Skip("contract testing is implemented with mining in ethash test mode. This takes about 7seconds to run. Unskip and run on demand")
tmp, repl, ethereum := testJEthRE(t)
if err := ethereum.Start(); err != nil {
t.Errorf("error starting ethereum: %v", err)
@@ -263,12 +263,21 @@ func TestContract(t *testing.T) {
defer ethereum.Stop()
defer os.RemoveAll(tmp)
- var txc uint64
coinbase := common.HexToAddress(testAddress)
- resolver.New(repl.xeth).CreateContracts(coinbase)
- // time.Sleep(1000 * time.Millisecond)
+ reg := registrar.New(repl.xeth)
+ err := reg.SetGlobalRegistrar("", coinbase)
+ if err != nil {
+ t.Errorf("error setting HashReg: %v", err)
+ }
+ err = reg.SetHashReg("", coinbase)
+ if err != nil {
+ t.Errorf("error setting HashReg: %v", err)
+ }
+ err = reg.SetUrlHint("", coinbase)
+ if err != nil {
+ t.Errorf("error setting HashReg: %v", err)
+ }
- // checkEvalJSON(t, repl, `eth.getBlock("pending", true).transactions.length`, `2`)
source := `contract test {\n` +
" /// @notice Will multiply `a` by 7." + `\n` +
` function multiply(uint a) returns(uint d) {\n` +
@@ -276,14 +285,20 @@ func TestContract(t *testing.T) {
` }\n` +
`}\n`
- checkEvalJSON(t, repl, `admin.contractInfo.stop()`, `true`)
+ if checkEvalJSON(t, repl, `admin.contractInfo.stop()`, `true`) != nil {
+ return
+ }
contractInfo, err := ioutil.ReadFile("info_test.json")
if err != nil {
t.Fatalf("%v", err)
}
- checkEvalJSON(t, repl, `primary = eth.accounts[0]`, `"`+testAddress+`"`)
- checkEvalJSON(t, repl, `source = "`+source+`"`, `"`+source+`"`)
+ if checkEvalJSON(t, repl, `primary = eth.accounts[0]`, `"`+testAddress+`"`) != nil {
+ return
+ }
+ if checkEvalJSON(t, repl, `source = "`+source+`"`, `"`+source+`"`) != nil {
+ return
+ }
// if solc is found with right version, test it, otherwise read from file
sol, err := compiler.New("")
@@ -303,69 +318,160 @@ func TestContract(t *testing.T) {
t.Errorf("%v", err)
}
} else {
- checkEvalJSON(t, repl, `contract = eth.compile.solidity(source).test`, string(contractInfo))
+ if checkEvalJSON(t, repl, `contract = eth.compile.solidity(source).test`, string(contractInfo)) != nil {
+ return
+ }
}
- checkEvalJSON(t, repl, `contract.code`, `"0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056"`)
+ if checkEvalJSON(t, repl, `contract.code`, `"0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056"`) != nil {
+ return
+ }
- checkEvalJSON(
+ if checkEvalJSON(
t, repl,
- `contractaddress = eth.sendTransaction({from: primary, data: contract.code })`,
- `"0x5dcaace5982778b409c524873b319667eba5d074"`,
- )
+ `contractaddress = eth.sendTransaction({from: primary, data: contract.code})`,
+ `"0x291293d57e0a0ab47effe97c02577f90d9211567"`,
+ ) != nil {
+ return
+ }
+
+ if !processTxs(repl, t, 7) {
+ return
+ }
callSetup := `abiDef = JSON.parse('[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]');
Multiply7 = eth.contract(abiDef);
multiply7 = Multiply7.at(contractaddress);
`
- // time.Sleep(1500 * time.Millisecond)
_, err = repl.re.Run(callSetup)
if err != nil {
t.Errorf("unexpected error setting up contract, got %v", err)
+ return
}
- // checkEvalJSON(t, repl, `eth.getBlock("pending", true).transactions.length`, `3`)
-
- // why is this sometimes failing?
- // checkEvalJSON(t, repl, `multiply7.multiply.call(6)`, `42`)
expNotice := ""
if repl.lastConfirm != expNotice {
t.Errorf("incorrect confirmation message: expected %v, got %v", expNotice, repl.lastConfirm)
+ return
}
- txc, repl.xeth = repl.xeth.ApplyTestTxs(repl.stateDb, coinbase, txc)
+ if checkEvalJSON(t, repl, `admin.contractInfo.start()`, `true`) != nil {
+ return
+ }
+ if checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary })`, `"0xcb08355dff8f8cadb5dc3d72e652ef5c33792cb0d871229dd1aef5db1c4ba1f2"`) != nil {
+ return
+ }
+
+ if !processTxs(repl, t, 1) {
+ return
+ }
- 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)
+ t.Errorf("incorrect confirmation message: expected\n%v, got\n%v", expNotice, repl.lastConfirm)
+ return
}
- var contenthash = `"0x86d2b7cf1e72e9a7a3f8d96601f0151742a2f780f1526414304fbe413dc7f9bd"`
- if sol != nil {
+ var contentHash = `"0x86d2b7cf1e72e9a7a3f8d96601f0151742a2f780f1526414304fbe413dc7f9bd"`
+ if sol != nil && solcVersion != sol.Version() {
modContractInfo := versionRE.ReplaceAll(contractInfo, []byte(`"compilerVersion":"`+sol.Version()+`"`))
- _ = modContractInfo
- // contenthash = crypto.Sha3(modContractInfo)
+ fmt.Printf("modified contractinfo:\n%s\n", modContractInfo)
+ contentHash = `"` + common.ToHex(crypto.Sha3([]byte(modContractInfo))) + `"`
}
- checkEvalJSON(t, repl, `filename = "/tmp/info.json"`, `"/tmp/info.json"`)
- checkEvalJSON(t, repl, `contenthash = admin.contractInfo.register(primary, contractaddress, contract, filename)`, contenthash)
- checkEvalJSON(t, repl, `admin.contractInfo.registerUrl(primary, contenthash, "file://"+filename)`, `true`)
- if err != nil {
- t.Errorf("unexpected error registering, got %v", err)
+ if checkEvalJSON(t, repl, `filename = "/tmp/info.json"`, `"/tmp/info.json"`) != nil {
+ return
+ }
+ if checkEvalJSON(t, repl, `contentHash = admin.contractInfo.saveInfo(contract.info, filename)`, contentHash) != nil {
+ return
+ }
+ if checkEvalJSON(t, repl, `admin.contractInfo.register(primary, contractaddress, contentHash)`, `true`) != nil {
+ return
+ }
+ if checkEvalJSON(t, repl, `admin.contractInfo.registerUrl(primary, contentHash, "file://"+filename)`, `true`) != nil {
+ return
}
- checkEvalJSON(t, repl, `admin.contractInfo.start()`, `true`)
+ if checkEvalJSON(t, repl, `admin.contractInfo.start()`, `true`) != nil {
+ return
+ }
+
+ if !processTxs(repl, t, 3) {
+ return
+ }
+
+ if checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary })`, `"0xe773bf05b027e4485c8b28967c35c939a71c5f6c177db78b51db52e76760d903"`) != nil {
+ return
+ }
- // update state
- txc, repl.xeth = repl.xeth.ApplyTestTxs(repl.stateDb, coinbase, txc)
+ if !processTxs(repl, t, 1) {
+ return
+ }
- checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary, gas: "1000000", gasPrice: "100000" })`, `undefined`)
expNotice = "Will multiply 6 by 7."
if repl.lastConfirm != expNotice {
- t.Errorf("incorrect confirmation message: expected %v, got %v", expNotice, repl.lastConfirm)
+ t.Errorf("incorrect confirmation message: expected\n%v, got\n%v", expNotice, repl.lastConfirm)
+ return
+ }
+}
+
+func pendingTransactions(repl *testjethre, t *testing.T) (txc int64, err error) {
+ txs := repl.ethereum.TxPool().GetTransactions()
+ return int64(len(txs)), nil
+}
+
+func processTxs(repl *testjethre, t *testing.T, expTxc int) bool {
+ var txc int64
+ var err error
+ for i := 0; i < 50; i++ {
+ txc, err = pendingTransactions(repl, t)
+ if err != nil {
+ t.Errorf("unexpected error checking pending transactions: %v", err)
+ return false
+ }
+ if expTxc < int(txc) {
+ t.Errorf("too many pending transactions: expected %v, got %v", expTxc, txc)
+ return false
+ } else if expTxc == int(txc) {
+ break
+ }
+ time.Sleep(100 * time.Millisecond)
+ }
+ if int(txc) != expTxc {
+ t.Errorf("incorrect number of pending transactions, expected %v, got %v", expTxc, txc)
+ return false
}
+ err = repl.ethereum.StartMining(runtime.NumCPU())
+ if err != nil {
+ t.Errorf("unexpected error mining: %v", err)
+ return false
+ }
+ defer repl.ethereum.StopMining()
+
+ timer := time.NewTimer(100 * time.Second)
+ height := new(big.Int).Add(repl.xeth.CurrentBlock().Number(), big.NewInt(1))
+ repl.wait <- height
+ select {
+ case <-timer.C:
+ // if times out make sure the xeth loop does not block
+ go func() {
+ select {
+ case repl.wait <- nil:
+ case <-repl.wait:
+ }
+ }()
+ case <-repl.wait:
+ }
+ txc, err = pendingTransactions(repl, t)
+ if err != nil {
+ t.Errorf("unexpected error checking pending transactions: %v", err)
+ return false
+ }
+ if txc != 0 {
+ t.Errorf("%d trasactions were not mined", txc)
+ return false
+ }
+ return true
}
func checkEvalJSON(t *testing.T, re *testjethre, expr, want string) error {
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index 3428bb4cf..7773ba93b 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -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)
diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go
index caf86974e..4d5ffb473 100644
--- a/common/compiler/solidity.go
+++ b/common/compiler/solidity.go
@@ -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
}
diff --git a/common/compiler/solidity_test.go b/common/compiler/solidity_test.go
index 59b6b6cd6..b493d6be3 100644
--- a/common/compiler/solidity_test.go
+++ b/common/compiler/solidity_test.go
@@ -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)
}
diff --git a/common/docserver/docserver.go b/common/docserver/docserver.go
index 5e076aa7e..c890cd3f5 100644
--- a/common/docserver/docserver.go
+++ b/common/docserver/docserver.go
@@ -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
diff --git a/common/docserver/docserver_test.go b/common/docserver/docserver_test.go
index 400d7447a..09b16864a 100644
--- a/common/docserver/docserver_test.go
+++ b/common/docserver/docserver_test.go
@@ -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")
+ }
+}
diff --git a/common/natspec/natspec.go b/common/natspec/natspec.go
index 7e5f053c7..9965a2227 100644
--- a/common/natspec/natspec.go
+++ b/common/natspec/natspec.go
@@ -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
}
diff --git a/common/natspec/natspec_e2e_test.go b/common/natspec/natspec_e2e_test.go
index 7e9172649..a941acbba 100644
--- a/common/natspec/natspec_e2e_test.go
+++ b/common/natspec/natspec_e2e_test.go
@@ -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)
}
diff --git a/common/registrar/contracts.go b/common/registrar/contracts.go
new file mode 100644
index 000000000..6c624030e
--- /dev/null
+++ b/common/registrar/contracts.go
@@ -0,0 +1,147 @@
+package registrar
+
+const ( // built-in contracts address source code and evm code
+ UrlHintCode = "0x60c180600c6000396000f30060003560e060020a90048063300a3bbf14601557005b6024600435602435604435602a565b60006000f35b6000600084815260200190815260200160002054600160a060020a0316600014806078575033600160a060020a03166000600085815260200190815260200160002054600160a060020a0316145b607f5760bc565b336000600085815260200190815260200160002081905550806001600085815260200190815260200160002083610100811060b657005b01819055505b50505056"
+
+ UrlHintSrc = `
+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;
+ (uint256 => uint256[256]) url;
+}
+ `
+
+ HashRegCode = "0x609880600c6000396000f30060003560e060020a9004806331e12c2014601f578063d66d6c1014602b57005b6025603d565b60006000f35b6037600435602435605d565b60006000f35b600054600160a060020a0316600014605357605b565b336000819055505b565b600054600160a060020a031633600160a060020a031614607b576094565b8060016000848152602001908152602001600020819055505b505056"
+
+ HashRegSrc = `
+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;
+}
+`
+
+ GlobalRegistrarSrc = `
+//sol
+
+import "owned";
+
+contract NameRegister {
+ function addr(bytes32 _name) constant returns (address o_owner) {}
+ function name(address _owner) constant returns (bytes32 o_name) {}
+}
+
+contract Registrar is NameRegister {
+ event Changed(bytes32 indexed name);
+ event PrimaryChanged(bytes32 indexed name, address indexed addr);
+
+ function owner(bytes32 _name) constant returns (address o_owner) {}
+ function addr(bytes32 _name) constant returns (address o_address) {}
+ function subRegistrar(bytes32 _name) constant returns (address o_subRegistrar) {}
+ function content(bytes32 _name) constant returns (bytes32 o_content) {}
+
+ function name(address _owner) constant returns (bytes32 o_name) {}
+}
+
+contract GlobalRegistrar is Registrar {
+ struct Record {
+ address owner;
+ address primary;
+ address subRegistrar;
+ bytes32 content;
+ uint value;
+ uint renewalDate;
+ }
+
+ function Registrar() {
+ // TODO: Populate with hall-of-fame.
+ }
+
+ function reserve(bytes32 _name) {
+ // Don't allow the same name to be overwritten.
+ // TODO: bidding mechanism
+ if (m_toRecord[_name].owner == 0) {
+ m_toRecord[_name].owner = msg.sender;
+ Changed(_name);
+ }
+ }
+
+ /*
+ TODO
+ > 12 chars: free
+ <= 12 chars: auction:
+ 1. new names are auctioned
+ - 7 day period to collect all bid bytes32es + deposits
+ - 1 day period to collect all bids to be considered (validity requires associated deposit to be >10% of bid)
+ - all valid bids are burnt except highest - difference between that and second highest is returned to winner
+ 2. remember when last auctioned/renewed
+ 3. anyone can force renewal process:
+ - 7 day period to collect all bid bytes32es + deposits
+ - 1 day period to collect all bids & full amounts - bids only uncovered if sufficiently high.
+ - 1% of winner burnt; original owner paid rest.
+ */
+
+ modifier onlyrecordowner(bytes32 _name) { if (m_toRecord[_name].owner == msg.sender) _ }
+
+ function transfer(bytes32 _name, address _newOwner) onlyrecordowner(_name) {
+ m_toRecord[_name].owner = _newOwner;
+ Changed(_name);
+ }
+
+ function disown(bytes32 _name) onlyrecordowner(_name) {
+ if (m_toName[m_toRecord[_name].primary] == _name)
+ {
+ PrimaryChanged(_name, m_toRecord[_name].primary);
+ m_toName[m_toRecord[_name].primary] = "";
+ }
+ delete m_toRecord[_name];
+ Changed(_name);
+ }
+
+ function setAddress(bytes32 _name, address _a, bool _primary) onlyrecordowner(_name) {
+ m_toRecord[_name].primary = _a;
+ if (_primary)
+ {
+ PrimaryChanged(_name, _a);
+ m_toName[_a] = _name;
+ }
+ Changed(_name);
+ }
+ function setSubRegistrar(bytes32 _name, address _registrar) onlyrecordowner(_name) {
+ m_toRecord[_name].subRegistrar = _registrar;
+ Changed(_name);
+ }
+ function setContent(bytes32 _name, bytes32 _content) onlyrecordowner(_name) {
+ m_toRecord[_name].content = _content;
+ Changed(_name);
+ }
+
+ function owner(bytes32 _name) constant returns (address) { return m_toRecord[_name].owner; }
+ function addr(bytes32 _name) constant returns (address) { return m_toRecord[_name].primary; }
+// function subRegistrar(bytes32 _name) constant returns (address) { return m_toRecord[_name].subRegistrar; } // TODO: bring in on next iteration.
+ function register(bytes32 _name) constant returns (address) { return m_toRecord[_name].subRegistrar; } // only possible for now
+ function content(bytes32 _name) constant returns (bytes32) { return m_toRecord[_name].content; }
+ function name(address _owner) constant returns (bytes32 o_name) { return m_toName[_owner]; }
+
+ mapping (address => bytes32) m_toName;
+ mapping (bytes32 => Record) m_toRecord;
+}
+`
+ GlobalRegistrarAbi = `[{"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"}]`
+
+ GlobalRegistrarCode = `0x610b408061000e6000396000f3006000357c01000000000000000000000000000000000000000000000000000000009004806301984892146100b357806302571be3146100ce5780632dff6941146100ff5780633b3b57de1461011a578063432ced041461014b5780635a3a05bd1461016257806379ce9fac1461019357806389a69c0e146101b0578063b9f37c86146101cd578063be99a980146101de578063c3d014d614610201578063d93e75731461021e578063e1fa8e841461023557005b6100c4600480359060200150610b02565b8060005260206000f35b6100df6004803590602001506109f3565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b610110600480359060200150610ad4565b8060005260206000f35b61012b600480359060200150610a3e565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b61015c600480359060200150610271565b60006000f35b610173600480359060200150610266565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b6101aa600480359060200180359060200150610341565b60006000f35b6101c7600480359060200180359060200150610844565b60006000f35b6101d860045061026e565b60006000f35b6101fb6004803590602001803590602001803590602001506106de565b60006000f35b61021860048035906020018035906020015061092c565b60006000f35b61022f600480359060200150610429565b60006000f35b610246600480359060200150610a89565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b60005b919050565b5b565b60006001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561033d57336001600050600083815260200190815260200160002060005060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550807fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b5b50565b813373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561042357816001600050600085815260200190815260200160002060005060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b5050565b803373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156106d95781600060005060006001600050600086815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000505414156105fd576001600050600083815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16827ff63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a85456040604090036040a36000600060005060006001600050600086815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050819055505b6001600050600083815260200190815260200160002060006000820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556002820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556003820160005060009055600482016000506000905560058201600050600090555050817fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b50565b823373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561083d57826001600050600086815260200190815260200160002060005060010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908302179055508115610811578273ffffffffffffffffffffffffffffffffffffffff16847ff63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a85456040604090036040a383600060005060008573ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050819055505b837fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b505050565b813373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561092657816001600050600085815260200190815260200160002060005060020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b5050565b813373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156109ed57816001600050600085815260200190815260200160002060005060030160005081905550827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b5050565b60006001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050610a39565b919050565b60006001600050600083815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050610a84565b919050565b60006001600050600083815260200190815260200160002060005060020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050610acf565b919050565b600060016000506000838152602001908152602001600020600050600301600050549050610afd565b919050565b6000600060005060008373ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050549050610b3b565b91905056`
+)
diff --git a/common/registrar/ethreg/ethreg.go b/common/registrar/ethreg/ethreg.go
new file mode 100644
index 000000000..f5ad3345b
--- /dev/null
+++ b/common/registrar/ethreg/ethreg.go
@@ -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)
+}
diff --git a/common/registrar/registrar.go b/common/registrar/registrar.go
new file mode 100644
index 000000000..061fe9c2c
--- /dev/null
+++ b/common/registrar/registrar.go
@@ -0,0 +1,398 @@
+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) (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 {
+ GlobalRegistrarAddr, 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) (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)
+ HashRegAddr, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", hashRegAbi)
+ if err != nil || zero.MatchString(HashRegAddr) {
+ if (addr == common.Address{}) {
+ err = fmt.Errorf("HashReg address not found and sender for creation not given")
+ return
+ }
+
+ HashRegAddr, err = self.backend.Transact(addr.Hex(), "", "", "", "200000", "", 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 @ %v\n", HashRegAddr)
+ } else {
+ glog.V(logger.Detail).Infof("HashRegAddr found at @ %v\n", HashRegAddr)
+ return
+ }
+ }
+
+ // register as HashReg
+ self.ReserveName(addr, HashRegName)
+ self.SetAddressToName(addr, HashRegName, common.HexToAddress(HashRegAddr))
+
+ return
+}
+
+func (self *Registrar) SetUrlHint(urlhint string, addr common.Address) (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)
+ UrlHintAddr, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", urlHintAbi)
+ if err != nil || zero.MatchString(UrlHintAddr) {
+ if (addr == common.Address{}) {
+ err = fmt.Errorf("UrlHint address not found and sender for creation not given")
+ return
+ }
+ UrlHintAddr, 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 @ %v\n", HashRegAddr)
+ } else {
+ glog.V(logger.Detail).Infof("UrlHint found @ %v\n", HashRegAddr)
+ return
+ }
+ }
+
+ // register as UrlHint
+ self.ReserveName(addr, UrlHintName)
+ self.SetAddressToName(addr, UrlHintName, common.HexToAddress(UrlHintAddr))
+
+ 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 := len(str)
+ for (l > 0) && (str[l-1] == 0) {
+ l--
+ }
+
+ str = str[:l]
+ uri = uri + str
+ idx++
+ }
+
+ l := 0
+ for (l < len(uri)) && (uri[l] == 0) {
+ l++
+ }
+ uri = uri[l:]
+
+ 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):], ""
+}
diff --git a/common/resolver/resolver_test.go b/common/registrar/registrar_test.go
index 02d12592e..5612e691c 100644
--- a/common/resolver/resolver_test.go
+++ b/common/registrar/registrar_test.go
@@ -1,4 +1,4 @@
-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)
}
}
}
diff --git a/common/resolver/contracts.go b/common/resolver/contracts.go
deleted file mode 100644
index 4aad95e43..000000000
--- a/common/resolver/contracts.go
+++ /dev/null
@@ -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;
- }
- */
-
-)
diff --git a/common/resolver/resolver.go b/common/resolver/resolver.go
deleted file mode 100644
index 9016547e1..000000000
--- a/common/resolver/resolver.go
+++ /dev/null
@@ -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)
-}
diff --git a/eth/backend.go b/eth/backend.go
index e62252b6c..38e06bcf8 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -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 {
diff --git a/rpc/api/admin.go b/rpc/api/admin.go
index b27482cfe..78c75a60b 100644
--- a/rpc/api/admin.go
+++ b/rpc/api/admin.go
@@ -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()
+// }
diff --git a/rpc/api/admin_args.go b/rpc/api/admin_args.go
index 5437971ca..7aee5d678 100644
--- a/rpc/api/admin_args.go
+++ b/rpc/api/admin_args.go
@@ -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
+}