diff options
-rw-r--r-- | cmd/geth/js_test.go | 17 | ||||
-rw-r--r-- | common/natspec/natspec_e2e_test.go.orig | 253 | ||||
-rw-r--r-- | common/registrar/registrar.go | 20 | ||||
-rw-r--r-- | rpc/api/admin_args.go | 32 | ||||
-rw-r--r-- | rpc/api/admin_js.go | 4 |
5 files changed, 296 insertions, 30 deletions
diff --git a/cmd/geth/js_test.go b/cmd/geth/js_test.go index bde5d250f..791218997 100644 --- a/cmd/geth/js_test.go +++ b/cmd/geth/js_test.go @@ -255,7 +255,11 @@ func TestSignature(t *testing.T) { func TestContract(t *testing.T) { // 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) + coinbase := common.HexToAddress(testAddress) + tmp, repl, ethereum := testREPL(t, func(conf *eth.Config) { + conf.Etherbase = testAddress + conf.PowTest = true + }) if err := ethereum.Start(); err != nil { t.Errorf("error starting ethereum: %v", err) return @@ -263,7 +267,6 @@ func TestContract(t *testing.T) { defer ethereum.Stop() defer os.RemoveAll(tmp) - coinbase := common.HexToAddress(testAddress) reg := registrar.New(repl.xeth) err := reg.SetGlobalRegistrar("", coinbase) if err != nil { @@ -330,12 +333,12 @@ func TestContract(t *testing.T) { if checkEvalJSON( t, repl, `contractaddress = eth.sendTransaction({from: primary, data: contract.code})`, - `"0x291293d57e0a0ab47effe97c02577f90d9211567"`, + `"0x46d69d55c3c4b86a924a92c9fc4720bb7bce1d74"`, ) != nil { return } - if !processTxs(repl, t, 7) { + if !processTxs(repl, t, 8) { return } @@ -358,7 +361,7 @@ multiply7 = Multiply7.at(contractaddress); if checkEvalJSON(t, repl, `admin.startNatSpec()`, `true`) != nil { return } - if checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary })`, `"0xcb08355dff8f8cadb5dc3d72e652ef5c33792cb0d871229dd1aef5db1c4ba1f2"`) != nil { + if checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary })`, `"0x4ef9088431a8033e4580d00e4eb2487275e031ff4163c7529df0ef45af17857b"`) != nil { return } @@ -366,7 +369,7 @@ multiply7 = Multiply7.at(contractaddress); return } - expNotice = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x87e2802265838c7f14bb69eecd2112911af6767907a702eeaa445239fb20711b'): {"params":[{"to":"0x291293d57e0a0ab47effe97c02577f90d9211567","data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000006"}]}` + expNotice = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x87e2802265838c7f14bb69eecd2112911af6767907a702eeaa445239fb20711b'): {"params":[{"to":"0x46d69d55c3c4b86a924a92c9fc4720bb7bce1d74","data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000006"}]}` if repl.lastConfirm != expNotice { t.Errorf("incorrect confirmation message: expected\n%v, got\n%v", expNotice, repl.lastConfirm) return @@ -399,7 +402,7 @@ multiply7 = Multiply7.at(contractaddress); return } - if checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary })`, `"0xe773bf05b027e4485c8b28967c35c939a71c5f6c177db78b51db52e76760d903"`) != nil { + if checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary })`, `"0x66d7635c12ad0b231e66da2f987ca3dfdca58ffe49c6442aa55960858103fd0c"`) != nil { return } diff --git a/common/natspec/natspec_e2e_test.go.orig b/common/natspec/natspec_e2e_test.go.orig new file mode 100644 index 000000000..ae8e17ad9 --- /dev/null +++ b/common/natspec/natspec_e2e_test.go.orig @@ -0,0 +1,253 @@ +package natspec + +import ( + "fmt" + "io/ioutil" + "os" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/docserver" + "github.com/ethereum/go-ethereum/common/registrar" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth" + xe "github.com/ethereum/go-ethereum/xeth" +) + +const ( + testBalance = "10000000000000000000" + + testFileName = "long_file_name_for_testing_registration_of_URLs_longer_than_32_bytes.content" + + testNotice = "Register key `utils.toHex(_key)` <- content `utils.toHex(_content)`" + + testExpNotice = "Register key 0xadd1a7d961cff0242089674ec2ef6fca671ab15e1fe80e38859fc815b98d88ab <- content 0xb3a2dea218de5d8bbe6c4645aadbf67b5ab00ecb1a9ec95dbdad6a0eed3e41a7" + + testExpNotice2 = `About to submit transaction (NatSpec notice error: abi key does not match any method): {"params":[{"to":"%s","data": "0x31e12c20"}]}` + + testExpNotice3 = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x1392c62d05b2d149e22a339c531157ae06b44d39a674cce500064b12b9aeb019'): {"params":[{"to":"%s","data": "0x300a3bbfb3a2dea218de5d8bbe6c4645aadbf67b5ab00ecb1a9ec95dbdad6a0eed3e41a7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066696c653a2f2f2f746573742e636f6e74656e74"}]}` +) + +const ( + testUserDoc = ` +{ + "methods": { + "register(uint256,uint256)": { + "notice": "` + testNotice + `" + } + }, + "invariants": [ + { "notice": "" } + ], + "construction": [ + { "notice": "" } + ] +} +` + testAbiDefinition = ` +[{ + "name": "register", + "constant": false, + "type": "function", + "inputs": [{ + "name": "_key", + "type": "uint256" + }, { + "name": "_content", + "type": "uint256" + }], + "outputs": [] +}] +` + + testContractInfo = ` +{ + "userDoc": ` + testUserDoc + `, + "abiDefinition": ` + testAbiDefinition + ` +} +` +) + +type testFrontend struct { + t *testing.T + ethereum *eth.Ethereum + xeth *xe.XEth + coinbase common.Address + stateDb *state.StateDB + txc uint64 + lastConfirm string + wantNatSpec bool +} + +func (self *testFrontend) UnlockAccount(acc []byte) bool { + self.ethereum.AccountManager().Unlock(common.BytesToAddress(acc), "password") + return true +} + +func (self *testFrontend) ConfirmTransaction(tx string) bool { + if self.wantNatSpec { + ds := docserver.New("/tmp/") + self.lastConfirm = GetNotice(self.xeth, tx, ds) + } + return true +} + +func testEth(t *testing.T) (ethereum *eth.Ethereum, err error) { + + os.RemoveAll("/tmp/eth-natspec/") + + err = os.MkdirAll("/tmp/eth-natspec/keystore", os.ModePerm) + if err != nil { + panic(err) + } + + // create a testAddress + ks := crypto.NewKeyStorePassphrase("/tmp/eth-natspec/keystore") + am := accounts.NewManager(ks) + testAccount, err := am.NewAccount("password") + if err != nil { + panic(err) + } + testAddress := strings.TrimPrefix(testAccount.Address.Hex(), "0x") + + // set up mock genesis with balance on the testAddress + core.GenesisAccounts = []byte(`{ + "` + testAddress + `": {"balance": "` + testBalance + `"} + }`) + + // only use minimalistic stack with no networking + ethereum, err = eth.New(ð.Config{ + DataDir: "/tmp/eth-natspec", + AccountManager: am, + MaxPeers: 0, + }) + + if err != nil { + panic(err) + } + + return +} + +func testInit(t *testing.T) (self *testFrontend) { + // initialise and start minimal ethereum stack + ethereum, err := testEth(t) + if err != nil { + t.Errorf("error creating ethereum: %v", err) + return + } + err = ethereum.Start() + if err != nil { + t.Errorf("error starting ethereum: %v", err) + return + } + + // mock frontend + self = &testFrontend{t: t, ethereum: ethereum} + self.xeth = xe.New(ethereum, self) + + addr, _ := ethereum.Etherbase() + self.coinbase = addr + self.stateDb = self.ethereum.ChainManager().State().Copy() + + // initialise the registry contracts + reg := registrar.New(self.xeth) + err = reg.SetHashReg("", addr) + if err != nil { + t.Errorf("error creating HashReg: %v", err) + } + err = reg.SetUrlHint("", addr) + if err != nil { + t.Errorf("error creating UrlHint: %v", err) + } + self.applyTxs() + + return + +} + +// this is needed for transaction to be applied to the state in testing +// the heavy lifing is done in XEth.ApplyTestTxs +// this is fragile, +// and does process leaking since xeth loops cannot quit safely +// should be replaced by proper mining with testDAG for easy full integration tests +func (self *testFrontend) applyTxs() { + self.txc, self.xeth = self.xeth.ApplyTestTxs(self.stateDb, self.coinbase, self.txc) + return +} + +// end to end test +func TestNatspecE2E(t *testing.T) { + t.Skip() + + tf := testInit(t) + defer tf.ethereum.Stop() + + // create a contractInfo file (mock cloud-deployed contract metadocs) + // incidentally this is the info for the registry contract itself + ioutil.WriteFile("/tmp/"+testFileName, []byte(testContractInfo), os.ModePerm) + dochash := common.BytesToHash(crypto.Sha3([]byte(testContractInfo))) + + // take the codehash for the contract we wanna test + // codehex := tf.xeth.CodeAt(registar.HashRegAddr) + codeb := tf.xeth.CodeAtBytes(registrar.HashRegAddr) + codehash := common.BytesToHash(crypto.Sha3(codeb)) + + // use resolver to register codehash->dochash->url + // test if globalregistry works + // registrar.HashRefAddr = "0x0" + // registrar.UrlHintAddr = "0x0" + reg := registrar.New(tf.xeth) + _, err := reg.SetHashToHash(tf.coinbase, codehash, dochash) + if err != nil { + t.Errorf("error registering: %v", err) + } + _, err = reg.SetUrlToHash(tf.coinbase, dochash, "file:///"+testFileName) + if err != nil { + t.Errorf("error registering: %v", err) + } + // apply txs to the state + tf.applyTxs() + + // NatSpec info for register method of HashReg contract installed + // now using the same transactions to check confirm messages + + tf.wantNatSpec = true // this is set so now the backend uses natspec confirmation + _, err = reg.SetHashToHash(tf.coinbase, codehash, dochash) + if err != nil { + t.Errorf("error calling contract registry: %v", err) + } + + fmt.Printf("GlobalRegistrar: %v, HashReg: %v, UrlHint: %v\n", registrar.GlobalRegistrarAddr, registrar.HashRegAddr, registrar.UrlHintAddr) + if tf.lastConfirm != testExpNotice { + t.Errorf("Wrong confirm message. expected '%v', got '%v'", testExpNotice, tf.lastConfirm) + } + + // test unknown method + exp := fmt.Sprintf(testExpNotice2, registrar.HashRegAddr) + _, err = reg.SetOwner(tf.coinbase) + if err != nil { + t.Errorf("error setting owner: %v", err) + } + + if tf.lastConfirm != exp { + t.Errorf("Wrong confirm message, expected '%v', got '%v'", exp, tf.lastConfirm) + } + + // test unknown contract + exp = fmt.Sprintf(testExpNotice3, registrar.UrlHintAddr) + + _, err = reg.SetUrlToHash(tf.coinbase, dochash, "file:///test.content") + if err != nil { + t.Errorf("error registering: %v", err) + } + + if tf.lastConfirm != exp { + t.Errorf("Wrong confirm message, expected '%v', got '%v'", exp, tf.lastConfirm) + } + +} diff --git a/common/registrar/registrar.go b/common/registrar/registrar.go index c1731cef5..457dd6894 100644 --- a/common/registrar/registrar.go +++ b/common/registrar/registrar.go @@ -39,10 +39,10 @@ So the caller needs to make sure the relevant environment initialised the desire contracts */ var ( - UrlHintAddr = "0x0" - HashRegAddr = "0x0" - // GlobalRegistrarAddr = "0x0" - GlobalRegistrarAddr = "0xc6d9d2cd449a754c494264e1809c50e34d64562b" + UrlHintAddr = "0x0" + HashRegAddr = "0x0" + GlobalRegistrarAddr = "0x0" + // GlobalRegistrarAddr = "0xc6d9d2cd449a754c494264e1809c50e34d64562b" zero = regexp.MustCompile("^(0x)?0*$") ) @@ -122,7 +122,11 @@ func (self *Registrar) SetHashReg(hashreg string, addr common.Address) (err erro 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) + var res string + res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", hashRegAbi) + if len(res) >= 40 { + HashRegAddr = "0x" + res[len(res)-40:len(res)] + } if err != nil || zero.MatchString(HashRegAddr) { if (addr == common.Address{}) { err = fmt.Errorf("HashReg address not found and sender for creation not given") @@ -157,7 +161,11 @@ func (self *Registrar) SetUrlHint(urlhint string, addr common.Address) (err erro 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) + var res string + res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", urlHintAbi) + if len(res) >= 40 { + UrlHintAddr = "0x" + res[len(res)-40:len(res)] + } if err != nil || zero.MatchString(UrlHintAddr) { if (addr == common.Address{}) { err = fmt.Errorf("UrlHint address not found and sender for creation not given") diff --git a/rpc/api/admin_args.go b/rpc/api/admin_args.go index e7548c7be..532907905 100644 --- a/rpc/api/admin_args.go +++ b/rpc/api/admin_args.go @@ -114,7 +114,7 @@ func (args *StartRPCArgs) UnmarshalJSON(b []byte) (err error) { args.ListenPort = 8545 args.Apis = "net,eth,web3" - if len(obj) >= 1 { + if len(obj) >= 1 && obj[0] != nil { if addr, ok := obj[0].(string); ok { args.ListenAddress = addr } else { @@ -122,7 +122,7 @@ func (args *StartRPCArgs) UnmarshalJSON(b []byte) (err error) { } } - if len(obj) >= 2 { + if len(obj) >= 2 && obj[1] != nil { if port, ok := obj[1].(float64); ok && port >= 0 && port <= 64*1024 { args.ListenPort = uint(port) } else { @@ -130,7 +130,7 @@ func (args *StartRPCArgs) UnmarshalJSON(b []byte) (err error) { } } - if len(obj) >= 3 { + if len(obj) >= 3 && obj[2] != nil { if corsDomain, ok := obj[2].(string); ok { args.CorsDomain = corsDomain } else { @@ -138,7 +138,7 @@ func (args *StartRPCArgs) UnmarshalJSON(b []byte) (err error) { } } - if len(obj) >= 4 { + if len(obj) >= 4 && obj[3] != nil { if apis, ok := obj[3].(string); ok { args.Apis = apis } else { @@ -229,7 +229,7 @@ func (args *SetGlobalRegistrarArgs) UnmarshalJSON(b []byte) (err error) { } } - if len(obj) >= 2 { + if len(obj) >= 2 && obj[1] != nil { if addr, ok := obj[1].(string); ok { args.ContractAddress = addr } else { @@ -251,7 +251,7 @@ func (args *SetHashRegArgs) UnmarshalJSON(b []byte) (err error) { return shared.NewDecodeParamError(err.Error()) } - if len(obj) >= 1 { + if len(obj) >= 1 && obj[0] != nil { if hashreg, ok := obj[0].(string); ok { args.HashReg = hashreg } else { @@ -259,7 +259,7 @@ func (args *SetHashRegArgs) UnmarshalJSON(b []byte) (err error) { } } - if len(obj) >= 2 { + if len(obj) >= 2 && obj[1] != nil { if sender, ok := obj[1].(string); ok { args.Sender = sender } else { @@ -281,7 +281,7 @@ func (args *SetUrlHintArgs) UnmarshalJSON(b []byte) (err error) { return shared.NewDecodeParamError(err.Error()) } - if len(obj) >= 1 { + if len(obj) >= 1 && obj[0] != nil { if urlhint, ok := obj[0].(string); ok { args.UrlHint = urlhint } else { @@ -289,7 +289,7 @@ func (args *SetUrlHintArgs) UnmarshalJSON(b []byte) (err error) { } } - if len(obj) >= 2 { + if len(obj) >= 2 && obj[1] != nil { if sender, ok := obj[1].(string); ok { args.Sender = sender } else { @@ -388,17 +388,19 @@ func (args *RegisterUrlArgs) UnmarshalJSON(b []byte) (err error) { } if len(obj) >= 1 { - if sender, ok := obj[1].(string); ok { + if sender, ok := obj[0].(string); ok { args.Sender = sender } else { return shared.NewInvalidTypeError("Sender", "not a string") } } - if sender, ok := obj[1].(string); ok { - args.ContentHash = sender - } else { - return shared.NewInvalidTypeError("ContentHash", "not a string") + if len(obj) >= 2 { + if sender, ok := obj[1].(string); ok { + args.ContentHash = sender + } else { + return shared.NewInvalidTypeError("ContentHash", "not a string") + } } if len(obj) >= 3 { @@ -460,7 +462,7 @@ func (args *HttpGetArgs) UnmarshalJSON(b []byte) (err error) { } } - if len(obj) >= 2 { + if len(obj) >= 2 && obj[1] != nil { if path, ok := obj[1].(string); ok { args.Path = path } else { diff --git a/rpc/api/admin_js.go b/rpc/api/admin_js.go index 2a9197da7..40c029fd1 100644 --- a/rpc/api/admin_js.go +++ b/rpc/api/admin_js.go @@ -92,8 +92,8 @@ web3._extend({ new web3._extend.Method({ name: 'register', call: 'admin_register', - params: 2, - inputFormatter: [web3._extend.utils.formatInputString,web3._extend.utils.formatInputString], + params: 3, + inputFormatter: [web3._extend.utils.formatInputString,web3._extend.utils.formatInputString,web3._extend.utils.formatInputString], outputFormatter: web3._extend.formatters.formatOutputBool }), new web3._extend.Method({ |