github.com/jeffallen/go-ethereum@v1.1.4-0.20150910155051-571d3236c49c/common/natspec/natspec_e2e_test.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser 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 // The go-ethereum library 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 Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package natspec 18 19 import ( 20 "fmt" 21 "io/ioutil" 22 "math/big" 23 "os" 24 "runtime" 25 "strings" 26 "testing" 27 "time" 28 29 "github.com/ethereum/go-ethereum/accounts" 30 "github.com/ethereum/go-ethereum/common" 31 "github.com/ethereum/go-ethereum/common/docserver" 32 "github.com/ethereum/go-ethereum/common/registrar" 33 "github.com/ethereum/go-ethereum/core" 34 "github.com/ethereum/go-ethereum/crypto" 35 "github.com/ethereum/go-ethereum/eth" 36 "github.com/ethereum/go-ethereum/ethdb" 37 xe "github.com/ethereum/go-ethereum/xeth" 38 ) 39 40 const ( 41 testBalance = "10000000000000000000" 42 43 testFileName = "long_file_name_for_testing_registration_of_URLs_longer_than_32_bytes.content" 44 45 testNotice = "Register key `utils.toHex(_key)` <- content `utils.toHex(_content)`" 46 47 testExpNotice = "Register key 0xadd1a7d961cff0242089674ec2ef6fca671ab15e1fe80e38859fc815b98d88ab <- content 0xb3a2dea218de5d8bbe6c4645aadbf67b5ab00ecb1a9ec95dbdad6a0eed3e41a7" 48 49 testExpNotice2 = `About to submit transaction (NatSpec notice error: abi key does not match any method): {"params":[{"to":"%s","data": "0x31e12c20"}]}` 50 51 testExpNotice3 = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x1392c62d05b2d149e22a339c531157ae06b44d39a674cce500064b12b9aeb019'): {"params":[{"to":"%s","data": "0x300a3bbfb3a2dea218de5d8bbe6c4645aadbf67b5ab00ecb1a9ec95dbdad6a0eed3e41a7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066696c653a2f2f2f746573742e636f6e74656e74"}]}` 52 ) 53 54 const ( 55 testUserDoc = ` 56 { 57 "methods": { 58 "register(uint256,uint256)": { 59 "notice": "` + testNotice + `" 60 } 61 }, 62 "invariants": [ 63 { "notice": "" } 64 ], 65 "construction": [ 66 { "notice": "" } 67 ] 68 } 69 ` 70 testAbiDefinition = ` 71 [{ 72 "name": "register", 73 "constant": false, 74 "type": "function", 75 "inputs": [{ 76 "name": "_key", 77 "type": "uint256" 78 }, { 79 "name": "_content", 80 "type": "uint256" 81 }], 82 "outputs": [] 83 }] 84 ` 85 86 testContractInfo = ` 87 { 88 "userDoc": ` + testUserDoc + `, 89 "abiDefinition": ` + testAbiDefinition + ` 90 } 91 ` 92 ) 93 94 type testFrontend struct { 95 t *testing.T 96 ethereum *eth.Ethereum 97 xeth *xe.XEth 98 wait chan *big.Int 99 lastConfirm string 100 wantNatSpec bool 101 } 102 103 func (self *testFrontend) UnlockAccount(acc []byte) bool { 104 self.ethereum.AccountManager().Unlock(common.BytesToAddress(acc), "password") 105 return true 106 } 107 108 func (self *testFrontend) ConfirmTransaction(tx string) bool { 109 if self.wantNatSpec { 110 ds := docserver.New("/tmp/") 111 self.lastConfirm = GetNotice(self.xeth, tx, ds) 112 } 113 return true 114 } 115 116 func testEth(t *testing.T) (ethereum *eth.Ethereum, err error) { 117 118 os.RemoveAll("/tmp/eth-natspec/") 119 120 err = os.MkdirAll("/tmp/eth-natspec/keystore", os.ModePerm) 121 if err != nil { 122 panic(err) 123 } 124 125 // create a testAddress 126 ks := crypto.NewKeyStorePassphrase("/tmp/eth-natspec/keystore") 127 am := accounts.NewManager(ks) 128 testAccount, err := am.NewAccount("password") 129 if err != nil { 130 panic(err) 131 } 132 133 testAddress := strings.TrimPrefix(testAccount.Address.Hex(), "0x") 134 135 db, _ := ethdb.NewMemDatabase() 136 // set up mock genesis with balance on the testAddress 137 core.WriteGenesisBlockForTesting(db, common.HexToAddress(testAddress), common.String2Big(testBalance)) 138 139 // only use minimalistic stack with no networking 140 ethereum, err = eth.New(ð.Config{ 141 DataDir: "/tmp/eth-natspec", 142 AccountManager: am, 143 MaxPeers: 0, 144 PowTest: true, 145 Etherbase: common.HexToAddress(testAddress), 146 NewDB: func(path string) (common.Database, error) { return db, nil }, 147 }) 148 149 if err != nil { 150 panic(err) 151 } 152 153 return 154 } 155 156 func testInit(t *testing.T) (self *testFrontend) { 157 // initialise and start minimal ethereum stack 158 ethereum, err := testEth(t) 159 if err != nil { 160 t.Errorf("error creating ethereum: %v", err) 161 return 162 } 163 err = ethereum.Start() 164 if err != nil { 165 t.Errorf("error starting ethereum: %v", err) 166 return 167 } 168 169 // mock frontend 170 self = &testFrontend{t: t, ethereum: ethereum} 171 self.xeth = xe.New(ethereum, self) 172 self.wait = self.xeth.UpdateState() 173 addr, _ := self.ethereum.Etherbase() 174 175 // initialise the registry contracts 176 reg := registrar.New(self.xeth) 177 var registrarTxhash, hashRegTxhash, urlHintTxhash string 178 registrarTxhash, err = reg.SetGlobalRegistrar("", addr) 179 if err != nil { 180 t.Errorf("error creating GlobalRegistrar: %v", err) 181 } 182 183 hashRegTxhash, err = reg.SetHashReg("", addr) 184 if err != nil { 185 t.Errorf("error creating HashReg: %v", err) 186 } 187 urlHintTxhash, err = reg.SetUrlHint("", addr) 188 if err != nil { 189 t.Errorf("error creating UrlHint: %v", err) 190 } 191 if !processTxs(self, t, 3) { 192 t.Errorf("error mining txs") 193 } 194 _ = registrarTxhash 195 _ = hashRegTxhash 196 _ = urlHintTxhash 197 198 /* TODO: 199 * lookup receipt and contract addresses by tx hash 200 * name registration for HashReg and UrlHint addresses 201 * mine those transactions 202 * then set once more SetHashReg SetUrlHint 203 */ 204 205 return 206 207 } 208 209 // end to end test 210 func TestNatspecE2E(t *testing.T) { 211 t.Skip() 212 213 tf := testInit(t) 214 defer tf.ethereum.Stop() 215 addr, _ := tf.ethereum.Etherbase() 216 217 // create a contractInfo file (mock cloud-deployed contract metadocs) 218 // incidentally this is the info for the registry contract itself 219 ioutil.WriteFile("/tmp/"+testFileName, []byte(testContractInfo), os.ModePerm) 220 dochash := crypto.Sha3Hash([]byte(testContractInfo)) 221 222 // take the codehash for the contract we wanna test 223 codeb := tf.xeth.CodeAtBytes(registrar.HashRegAddr) 224 codehash := crypto.Sha3Hash(codeb) 225 226 // use resolver to register codehash->dochash->url 227 // test if globalregistry works 228 // registrar.HashRefAddr = "0x0" 229 // registrar.UrlHintAddr = "0x0" 230 reg := registrar.New(tf.xeth) 231 _, err := reg.SetHashToHash(addr, codehash, dochash) 232 if err != nil { 233 t.Errorf("error registering: %v", err) 234 } 235 _, err = reg.SetUrlToHash(addr, dochash, "file:///"+testFileName) 236 if err != nil { 237 t.Errorf("error registering: %v", err) 238 } 239 if !processTxs(tf, t, 5) { 240 return 241 } 242 243 // NatSpec info for register method of HashReg contract installed 244 // now using the same transactions to check confirm messages 245 246 tf.wantNatSpec = true // this is set so now the backend uses natspec confirmation 247 _, err = reg.SetHashToHash(addr, codehash, dochash) 248 if err != nil { 249 t.Errorf("error calling contract registry: %v", err) 250 } 251 252 fmt.Printf("GlobalRegistrar: %v, HashReg: %v, UrlHint: %v\n", registrar.GlobalRegistrarAddr, registrar.HashRegAddr, registrar.UrlHintAddr) 253 if tf.lastConfirm != testExpNotice { 254 t.Errorf("Wrong confirm message. expected\n'%v', got\n'%v'", testExpNotice, tf.lastConfirm) 255 } 256 257 // test unknown method 258 exp := fmt.Sprintf(testExpNotice2, registrar.HashRegAddr) 259 _, err = reg.SetOwner(addr) 260 if err != nil { 261 t.Errorf("error setting owner: %v", err) 262 } 263 264 if tf.lastConfirm != exp { 265 t.Errorf("Wrong confirm message, expected\n'%v', got\n'%v'", exp, tf.lastConfirm) 266 } 267 268 // test unknown contract 269 exp = fmt.Sprintf(testExpNotice3, registrar.UrlHintAddr) 270 271 _, err = reg.SetUrlToHash(addr, dochash, "file:///test.content") 272 if err != nil { 273 t.Errorf("error registering: %v", err) 274 } 275 276 if tf.lastConfirm != exp { 277 t.Errorf("Wrong confirm message, expected '%v', got '%v'", exp, tf.lastConfirm) 278 } 279 280 } 281 282 func pendingTransactions(repl *testFrontend, t *testing.T) (txc int64, err error) { 283 txs := repl.ethereum.TxPool().GetTransactions() 284 return int64(len(txs)), nil 285 } 286 287 func processTxs(repl *testFrontend, t *testing.T, expTxc int) bool { 288 var txc int64 289 var err error 290 for i := 0; i < 50; i++ { 291 txc, err = pendingTransactions(repl, t) 292 if err != nil { 293 t.Errorf("unexpected error checking pending transactions: %v", err) 294 return false 295 } 296 if expTxc < int(txc) { 297 t.Errorf("too many pending transactions: expected %v, got %v", expTxc, txc) 298 return false 299 } else if expTxc == int(txc) { 300 break 301 } 302 time.Sleep(100 * time.Millisecond) 303 } 304 if int(txc) != expTxc { 305 t.Errorf("incorrect number of pending transactions, expected %v, got %v", expTxc, txc) 306 return false 307 } 308 309 err = repl.ethereum.StartMining(runtime.NumCPU()) 310 if err != nil { 311 t.Errorf("unexpected error mining: %v", err) 312 return false 313 } 314 defer repl.ethereum.StopMining() 315 316 timer := time.NewTimer(100 * time.Second) 317 height := new(big.Int).Add(repl.xeth.CurrentBlock().Number(), big.NewInt(1)) 318 repl.wait <- height 319 select { 320 case <-timer.C: 321 // if times out make sure the xeth loop does not block 322 go func() { 323 select { 324 case repl.wait <- nil: 325 case <-repl.wait: 326 } 327 }() 328 case <-repl.wait: 329 } 330 txc, err = pendingTransactions(repl, t) 331 if err != nil { 332 t.Errorf("unexpected error checking pending transactions: %v", err) 333 return false 334 } 335 if txc != 0 { 336 t.Errorf("%d trasactions were not mined", txc) 337 return false 338 } 339 return true 340 }