github.com/jeffallen/go-ethereum@v1.1.4-0.20150910155051-571d3236c49c/cmd/geth/js_test.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // go-ethereum is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 17 package main 18 19 import ( 20 "fmt" 21 "io/ioutil" 22 "math/big" 23 "os" 24 "path/filepath" 25 "regexp" 26 "runtime" 27 "strconv" 28 "testing" 29 "time" 30 31 "github.com/ethereum/go-ethereum/accounts" 32 "github.com/ethereum/go-ethereum/common" 33 "github.com/ethereum/go-ethereum/common/compiler" 34 "github.com/ethereum/go-ethereum/common/docserver" 35 "github.com/ethereum/go-ethereum/common/natspec" 36 "github.com/ethereum/go-ethereum/common/registrar" 37 "github.com/ethereum/go-ethereum/core" 38 "github.com/ethereum/go-ethereum/crypto" 39 "github.com/ethereum/go-ethereum/eth" 40 "github.com/ethereum/go-ethereum/ethdb" 41 "github.com/ethereum/go-ethereum/rpc/codec" 42 "github.com/ethereum/go-ethereum/rpc/comms" 43 ) 44 45 const ( 46 testSolcPath = "" 47 solcVersion = "0.9.23" 48 49 testKey = "e6fab74a43941f82d89cb7faa408e227cdad3153c4720e540e855c19b15e6674" 50 testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" 51 testBalance = "10000000000000000000" 52 // of empty string 53 testHash = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" 54 ) 55 56 var ( 57 versionRE = regexp.MustCompile(strconv.Quote(`"compilerVersion":"` + solcVersion + `"`)) 58 testNodeKey = crypto.ToECDSA(common.Hex2Bytes("4b50fa71f5c3eeb8fdc452224b2395af2fcc3d125e06c32c82e048c0559db03f")) 59 testGenesis = `{"` + testAddress[2:] + `": {"balance": "` + testBalance + `"}}` 60 ) 61 62 type testjethre struct { 63 *jsre 64 lastConfirm string 65 ds *docserver.DocServer 66 } 67 68 func (self *testjethre) UnlockAccount(acc []byte) bool { 69 err := self.ethereum.AccountManager().Unlock(common.BytesToAddress(acc), "") 70 if err != nil { 71 panic("unable to unlock") 72 } 73 return true 74 } 75 76 func (self *testjethre) ConfirmTransaction(tx string) bool { 77 if self.ethereum.NatSpec { 78 self.lastConfirm = natspec.GetNotice(self.xeth, tx, self.ds) 79 } 80 return true 81 } 82 83 func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) { 84 return testREPL(t, nil) 85 } 86 87 func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *eth.Ethereum) { 88 tmp, err := ioutil.TempDir("", "geth-test") 89 if err != nil { 90 t.Fatal(err) 91 } 92 93 db, _ := ethdb.NewMemDatabase() 94 95 core.WriteGenesisBlockForTesting(db, common.HexToAddress(testAddress), common.String2Big(testBalance)) 96 ks := crypto.NewKeyStorePlain(filepath.Join(tmp, "keystore")) 97 am := accounts.NewManager(ks) 98 conf := ð.Config{ 99 NodeKey: testNodeKey, 100 DataDir: tmp, 101 AccountManager: am, 102 MaxPeers: 0, 103 Name: "test", 104 SolcPath: testSolcPath, 105 PowTest: true, 106 NewDB: func(path string) (common.Database, error) { return db, nil }, 107 } 108 if config != nil { 109 config(conf) 110 } 111 ethereum, err := eth.New(conf) 112 if err != nil { 113 t.Fatal("%v", err) 114 } 115 116 keyb, err := crypto.HexToECDSA(testKey) 117 if err != nil { 118 t.Fatal(err) 119 } 120 key := crypto.NewKeyFromECDSA(keyb) 121 err = ks.StoreKey(key, "") 122 if err != nil { 123 t.Fatal(err) 124 } 125 126 err = am.Unlock(key.Address, "") 127 if err != nil { 128 t.Fatal(err) 129 } 130 131 assetPath := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext") 132 client := comms.NewInProcClient(codec.JSON) 133 ds := docserver.New("/") 134 tf := &testjethre{ds: ds} 135 repl := newJSRE(ethereum, assetPath, "", client, false, tf) 136 tf.jsre = repl 137 return tmp, tf, ethereum 138 } 139 140 func TestNodeInfo(t *testing.T) { 141 t.Skip("broken after p2p update") 142 tmp, repl, ethereum := testJEthRE(t) 143 if err := ethereum.Start(); err != nil { 144 t.Fatalf("error starting ethereum: %v", err) 145 } 146 defer ethereum.Stop() 147 defer os.RemoveAll(tmp) 148 149 want := `{"DiscPort":0,"IP":"0.0.0.0","ListenAddr":"","Name":"test","NodeID":"4cb2fc32924e94277bf94b5e4c983beedb2eabd5a0bc941db32202735c6625d020ca14a5963d1738af43b6ac0a711d61b1a06de931a499fe2aa0b1a132a902b5","NodeUrl":"enode://4cb2fc32924e94277bf94b5e4c983beedb2eabd5a0bc941db32202735c6625d020ca14a5963d1738af43b6ac0a711d61b1a06de931a499fe2aa0b1a132a902b5@0.0.0.0:0","TCPPort":0,"Td":"131072"}` 150 checkEvalJSON(t, repl, `admin.nodeInfo`, want) 151 } 152 153 func TestAccounts(t *testing.T) { 154 tmp, repl, ethereum := testJEthRE(t) 155 if err := ethereum.Start(); err != nil { 156 t.Fatalf("error starting ethereum: %v", err) 157 } 158 defer ethereum.Stop() 159 defer os.RemoveAll(tmp) 160 161 checkEvalJSON(t, repl, `eth.accounts`, `["`+testAddress+`"]`) 162 checkEvalJSON(t, repl, `eth.coinbase`, `"`+testAddress+`"`) 163 val, err := repl.re.Run(`personal.newAccount("password")`) 164 if err != nil { 165 t.Errorf("expected no error, got %v", err) 166 } 167 addr := val.String() 168 if !regexp.MustCompile(`0x[0-9a-f]{40}`).MatchString(addr) { 169 t.Errorf("address not hex: %q", addr) 170 } 171 172 checkEvalJSON(t, repl, `eth.accounts`, `["`+testAddress+`","`+addr+`"]`) 173 174 } 175 176 func TestBlockChain(t *testing.T) { 177 tmp, repl, ethereum := testJEthRE(t) 178 if err := ethereum.Start(); err != nil { 179 t.Fatalf("error starting ethereum: %v", err) 180 } 181 defer ethereum.Stop() 182 defer os.RemoveAll(tmp) 183 // get current block dump before export/import. 184 val, err := repl.re.Run("JSON.stringify(debug.dumpBlock(eth.blockNumber))") 185 if err != nil { 186 t.Errorf("expected no error, got %v", err) 187 } 188 beforeExport := val.String() 189 190 // do the export 191 extmp, err := ioutil.TempDir("", "geth-test-export") 192 if err != nil { 193 t.Fatal(err) 194 } 195 defer os.RemoveAll(extmp) 196 tmpfile := filepath.Join(extmp, "export.chain") 197 tmpfileq := strconv.Quote(tmpfile) 198 199 ethereum.ChainManager().Reset() 200 201 checkEvalJSON(t, repl, `admin.exportChain(`+tmpfileq+`)`, `true`) 202 if _, err := os.Stat(tmpfile); err != nil { 203 t.Fatal(err) 204 } 205 206 // check import, verify that dumpBlock gives the same result. 207 checkEvalJSON(t, repl, `admin.importChain(`+tmpfileq+`)`, `true`) 208 checkEvalJSON(t, repl, `debug.dumpBlock(eth.blockNumber)`, beforeExport) 209 } 210 211 func TestMining(t *testing.T) { 212 tmp, repl, ethereum := testJEthRE(t) 213 if err := ethereum.Start(); err != nil { 214 t.Fatalf("error starting ethereum: %v", err) 215 } 216 defer ethereum.Stop() 217 defer os.RemoveAll(tmp) 218 checkEvalJSON(t, repl, `eth.mining`, `false`) 219 } 220 221 func TestRPC(t *testing.T) { 222 tmp, repl, ethereum := testJEthRE(t) 223 if err := ethereum.Start(); err != nil { 224 t.Errorf("error starting ethereum: %v", err) 225 return 226 } 227 defer ethereum.Stop() 228 defer os.RemoveAll(tmp) 229 230 checkEvalJSON(t, repl, `admin.startRPC("127.0.0.1", 5004, "*", "web3,eth,net")`, `true`) 231 } 232 233 func TestCheckTestAccountBalance(t *testing.T) { 234 t.Skip() // i don't think it tests the correct behaviour here. it's actually testing 235 // internals which shouldn't be tested. This now fails because of a change in the core 236 // and i have no means to fix this, sorry - @obscuren 237 tmp, repl, ethereum := testJEthRE(t) 238 if err := ethereum.Start(); err != nil { 239 t.Errorf("error starting ethereum: %v", err) 240 return 241 } 242 defer ethereum.Stop() 243 defer os.RemoveAll(tmp) 244 245 repl.re.Run(`primary = "` + testAddress + `"`) 246 checkEvalJSON(t, repl, `eth.getBalance(primary)`, `"`+testBalance+`"`) 247 } 248 249 func TestSignature(t *testing.T) { 250 tmp, repl, ethereum := testJEthRE(t) 251 if err := ethereum.Start(); err != nil { 252 t.Errorf("error starting ethereum: %v", err) 253 return 254 } 255 defer ethereum.Stop() 256 defer os.RemoveAll(tmp) 257 258 val, err := repl.re.Run(`eth.sign("` + testAddress + `", "` + testHash + `")`) 259 260 // This is a very preliminary test, lacking actual signature verification 261 if err != nil { 262 t.Errorf("Error running js: %v", err) 263 return 264 } 265 output := val.String() 266 t.Logf("Output: %v", output) 267 268 regex := regexp.MustCompile(`^0x[0-9a-f]{130}$`) 269 if !regex.MatchString(output) { 270 t.Errorf("Signature is not 65 bytes represented in hexadecimal.") 271 return 272 } 273 } 274 275 func TestContract(t *testing.T) { 276 t.Skip("contract testing is implemented with mining in ethash test mode. This takes about 7seconds to run. Unskip and run on demand") 277 coinbase := common.HexToAddress(testAddress) 278 tmp, repl, ethereum := testREPL(t, func(conf *eth.Config) { 279 conf.Etherbase = coinbase 280 conf.PowTest = true 281 }) 282 if err := ethereum.Start(); err != nil { 283 t.Errorf("error starting ethereum: %v", err) 284 return 285 } 286 defer ethereum.Stop() 287 defer os.RemoveAll(tmp) 288 289 reg := registrar.New(repl.xeth) 290 _, err := reg.SetGlobalRegistrar("", coinbase) 291 if err != nil { 292 t.Errorf("error setting HashReg: %v", err) 293 } 294 _, err = reg.SetHashReg("", coinbase) 295 if err != nil { 296 t.Errorf("error setting HashReg: %v", err) 297 } 298 _, err = reg.SetUrlHint("", coinbase) 299 if err != nil { 300 t.Errorf("error setting HashReg: %v", err) 301 } 302 /* TODO: 303 * lookup receipt and contract addresses by tx hash 304 * name registration for HashReg and UrlHint addresses 305 * mine those transactions 306 * then set once more SetHashReg SetUrlHint 307 */ 308 309 source := `contract test {\n` + 310 " /// @notice Will multiply `a` by 7." + `\n` + 311 ` function multiply(uint a) returns(uint d) {\n` + 312 ` return a * 7;\n` + 313 ` }\n` + 314 `}\n` 315 316 if checkEvalJSON(t, repl, `admin.stopNatSpec()`, `true`) != nil { 317 return 318 } 319 320 contractInfo, err := ioutil.ReadFile("info_test.json") 321 if err != nil { 322 t.Fatalf("%v", err) 323 } 324 if checkEvalJSON(t, repl, `primary = eth.accounts[0]`, `"`+testAddress+`"`) != nil { 325 return 326 } 327 if checkEvalJSON(t, repl, `source = "`+source+`"`, `"`+source+`"`) != nil { 328 return 329 } 330 331 // if solc is found with right version, test it, otherwise read from file 332 sol, err := compiler.New("") 333 if err != nil { 334 t.Logf("solc not found: mocking contract compilation step") 335 } else if sol.Version() != solcVersion { 336 t.Logf("WARNING: solc different version found (%v, test written for %v, may need to update)", sol.Version(), solcVersion) 337 } 338 339 if err != nil { 340 info, err := ioutil.ReadFile("info_test.json") 341 if err != nil { 342 t.Fatalf("%v", err) 343 } 344 _, err = repl.re.Run(`contract = JSON.parse(` + strconv.Quote(string(info)) + `)`) 345 if err != nil { 346 t.Errorf("%v", err) 347 } 348 } else { 349 if checkEvalJSON(t, repl, `contract = eth.compile.solidity(source).test`, string(contractInfo)) != nil { 350 return 351 } 352 } 353 354 if checkEvalJSON(t, repl, `contract.code`, `"0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056"`) != nil { 355 return 356 } 357 358 if checkEvalJSON( 359 t, repl, 360 `contractaddress = eth.sendTransaction({from: primary, data: contract.code})`, 361 `"0x46d69d55c3c4b86a924a92c9fc4720bb7bce1d74"`, 362 ) != nil { 363 return 364 } 365 366 if !processTxs(repl, t, 8) { 367 return 368 } 369 370 callSetup := `abiDef = JSON.parse('[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]'); 371 Multiply7 = eth.contract(abiDef); 372 multiply7 = Multiply7.at(contractaddress); 373 ` 374 _, err = repl.re.Run(callSetup) 375 if err != nil { 376 t.Errorf("unexpected error setting up contract, got %v", err) 377 return 378 } 379 380 expNotice := "" 381 if repl.lastConfirm != expNotice { 382 t.Errorf("incorrect confirmation message: expected %v, got %v", expNotice, repl.lastConfirm) 383 return 384 } 385 386 if checkEvalJSON(t, repl, `admin.startNatSpec()`, `true`) != nil { 387 return 388 } 389 if checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary })`, `"0x4ef9088431a8033e4580d00e4eb2487275e031ff4163c7529df0ef45af17857b"`) != nil { 390 return 391 } 392 393 if !processTxs(repl, t, 1) { 394 return 395 } 396 397 expNotice = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x87e2802265838c7f14bb69eecd2112911af6767907a702eeaa445239fb20711b'): {"params":[{"to":"0x46d69d55c3c4b86a924a92c9fc4720bb7bce1d74","data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000006"}]}` 398 if repl.lastConfirm != expNotice { 399 t.Errorf("incorrect confirmation message: expected\n%v, got\n%v", expNotice, repl.lastConfirm) 400 return 401 } 402 403 var contentHash = `"0x86d2b7cf1e72e9a7a3f8d96601f0151742a2f780f1526414304fbe413dc7f9bd"` 404 if sol != nil && solcVersion != sol.Version() { 405 modContractInfo := versionRE.ReplaceAll(contractInfo, []byte(`"compilerVersion":"`+sol.Version()+`"`)) 406 fmt.Printf("modified contractinfo:\n%s\n", modContractInfo) 407 contentHash = `"` + common.ToHex(crypto.Sha3([]byte(modContractInfo))) + `"` 408 } 409 if checkEvalJSON(t, repl, `filename = "/tmp/info.json"`, `"/tmp/info.json"`) != nil { 410 return 411 } 412 if checkEvalJSON(t, repl, `contentHash = admin.saveInfo(contract.info, filename)`, contentHash) != nil { 413 return 414 } 415 if checkEvalJSON(t, repl, `admin.register(primary, contractaddress, contentHash)`, `true`) != nil { 416 return 417 } 418 if checkEvalJSON(t, repl, `admin.registerUrl(primary, contentHash, "file://"+filename)`, `true`) != nil { 419 return 420 } 421 422 if checkEvalJSON(t, repl, `admin.startNatSpec()`, `true`) != nil { 423 return 424 } 425 426 if !processTxs(repl, t, 3) { 427 return 428 } 429 430 if checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary })`, `"0x66d7635c12ad0b231e66da2f987ca3dfdca58ffe49c6442aa55960858103fd0c"`) != nil { 431 return 432 } 433 434 if !processTxs(repl, t, 1) { 435 return 436 } 437 438 expNotice = "Will multiply 6 by 7." 439 if repl.lastConfirm != expNotice { 440 t.Errorf("incorrect confirmation message: expected\n%v, got\n%v", expNotice, repl.lastConfirm) 441 return 442 } 443 } 444 445 func pendingTransactions(repl *testjethre, t *testing.T) (txc int64, err error) { 446 txs := repl.ethereum.TxPool().GetTransactions() 447 return int64(len(txs)), nil 448 } 449 450 func processTxs(repl *testjethre, t *testing.T, expTxc int) bool { 451 var txc int64 452 var err error 453 for i := 0; i < 50; i++ { 454 txc, err = pendingTransactions(repl, t) 455 if err != nil { 456 t.Errorf("unexpected error checking pending transactions: %v", err) 457 return false 458 } 459 if expTxc < int(txc) { 460 t.Errorf("too many pending transactions: expected %v, got %v", expTxc, txc) 461 return false 462 } else if expTxc == int(txc) { 463 break 464 } 465 time.Sleep(100 * time.Millisecond) 466 } 467 if int(txc) != expTxc { 468 t.Errorf("incorrect number of pending transactions, expected %v, got %v", expTxc, txc) 469 return false 470 } 471 472 err = repl.ethereum.StartMining(runtime.NumCPU()) 473 if err != nil { 474 t.Errorf("unexpected error mining: %v", err) 475 return false 476 } 477 defer repl.ethereum.StopMining() 478 479 timer := time.NewTimer(100 * time.Second) 480 height := new(big.Int).Add(repl.xeth.CurrentBlock().Number(), big.NewInt(1)) 481 repl.wait <- height 482 select { 483 case <-timer.C: 484 // if times out make sure the xeth loop does not block 485 go func() { 486 select { 487 case repl.wait <- nil: 488 case <-repl.wait: 489 } 490 }() 491 case <-repl.wait: 492 } 493 txc, err = pendingTransactions(repl, t) 494 if err != nil { 495 t.Errorf("unexpected error checking pending transactions: %v", err) 496 return false 497 } 498 if txc != 0 { 499 t.Errorf("%d trasactions were not mined", txc) 500 return false 501 } 502 return true 503 } 504 505 func checkEvalJSON(t *testing.T, re *testjethre, expr, want string) error { 506 val, err := re.re.Run("JSON.stringify(" + expr + ")") 507 if err == nil && val.String() != want { 508 err = fmt.Errorf("Output mismatch for `%s`:\ngot: %s\nwant: %s", expr, val.String(), want) 509 } 510 if err != nil { 511 _, file, line, _ := runtime.Caller(1) 512 file = filepath.Base(file) 513 fmt.Printf("\t%s:%d: %v\n", file, line, err) 514 t.Fail() 515 } 516 return err 517 }