natspec_e2e_test.go 9.0 KB

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