natspec_e2e_test.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. package natspec
  2. import (
  3. "io/ioutil"
  4. "math/big"
  5. "os"
  6. "testing"
  7. "github.com/ethereum/go-ethereum/accounts"
  8. "github.com/ethereum/go-ethereum/common"
  9. "github.com/ethereum/go-ethereum/common/docserver"
  10. "github.com/ethereum/go-ethereum/common/resolver"
  11. "github.com/ethereum/go-ethereum/core"
  12. "github.com/ethereum/go-ethereum/core/state"
  13. "github.com/ethereum/go-ethereum/crypto"
  14. "github.com/ethereum/go-ethereum/eth"
  15. "github.com/ethereum/go-ethereum/rpc"
  16. xe "github.com/ethereum/go-ethereum/xeth"
  17. )
  18. type testFrontend struct {
  19. t *testing.T
  20. ethereum *eth.Ethereum
  21. xeth *xe.XEth
  22. api *rpc.EthereumApi
  23. coinbase string
  24. stateDb *state.StateDB
  25. txc uint64
  26. lastConfirm string
  27. makeNatSpec bool
  28. }
  29. const (
  30. testAccount = "e273f01c99144c438695e10f24926dc1f9fbf62d"
  31. testBalance = "1000000000000"
  32. )
  33. const testFileName = "long_file_name_for_testing_registration_of_URLs_longer_than_32_bytes.content"
  34. const testNotice = "Register key `utils.toHex(_key)` <- content `utils.toHex(_content)`"
  35. const testExpNotice = "Register key 0xadd1a7d961cff0242089674ec2ef6fca671ab15e1fe80e38859fc815b98d88ab <- content 0xc00d5bcc872e17813df6ec5c646bb281a6e2d3b454c2c400c78192adf3344af9"
  36. const testExpNotice2 = `About to submit transaction (NatSpec notice error "abi key does not match any method"): {"id":6,"jsonrpc":"2.0","method":"eth_transact","params":[{"from":"0xe273f01c99144c438695e10f24926dc1f9fbf62d","to":"0xb737b91f8e95cf756766fc7c62c9a8ff58470381","value":"100000000000","gas":"100000","gasPrice":"100000","data":"0x31e12c20"}]}`
  37. const testExpNotice3 = `About to submit transaction (no NatSpec info found for contract): {"id":6,"jsonrpc":"2.0","method":"eth_transact","params":[{"from":"0xe273f01c99144c438695e10f24926dc1f9fbf62d","to":"0x8b839ad85686967a4f418eccc81962eaee314ac3","value":"100000000000","gas":"100000","gasPrice":"100000","data":"0x300a3bbfc00d5bcc872e17813df6ec5c646bb281a6e2d3b454c2c400c78192adf3344af900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000"}]}`
  38. const testUserDoc = `
  39. {
  40. "source": "...",
  41. "language": "Solidity",
  42. "languageVersion": 1,
  43. "methods": {
  44. "register(uint256,uint256)": {
  45. "notice": "` + testNotice + `"
  46. }
  47. },
  48. "invariants": [
  49. { "notice": "" }
  50. ],
  51. "construction": [
  52. { "notice": "" }
  53. ]
  54. }
  55. `
  56. const testABI = `
  57. [{
  58. "name": "register",
  59. "constant": false,
  60. "type": "function",
  61. "inputs": [{
  62. "name": "_key",
  63. "type": "uint256"
  64. }, {
  65. "name": "_content",
  66. "type": "uint256"
  67. }],
  68. "outputs": []
  69. }]
  70. `
  71. const testDocs = `
  72. {
  73. "userdoc": ` + testUserDoc + `,
  74. "abi": ` + testABI + `
  75. }
  76. `
  77. func (f *testFrontend) UnlockAccount(acc []byte) bool {
  78. f.t.Logf("Unlocking account %v\n", common.Bytes2Hex(acc))
  79. f.ethereum.AccountManager().Unlock(acc, "password")
  80. return true
  81. }
  82. func (f *testFrontend) ConfirmTransaction(tx string) bool {
  83. //f.t.Logf("ConfirmTransaction called tx = %v", tx)
  84. if f.makeNatSpec {
  85. ds, err := docserver.New("/tmp/")
  86. if err != nil {
  87. f.t.Errorf("Error creating DocServer: %v", err)
  88. }
  89. f.lastConfirm = GetNotice(f.xeth, tx, ds)
  90. }
  91. return true
  92. }
  93. var port = 30300
  94. func testEth(t *testing.T) (ethereum *eth.Ethereum, err error) {
  95. os.RemoveAll("/tmp/eth-natspec/")
  96. err = os.MkdirAll("/tmp/eth-natspec/keys/e273f01c99144c438695e10f24926dc1f9fbf62d/", os.ModePerm)
  97. if err != nil {
  98. t.Errorf("%v", err)
  99. return
  100. }
  101. err = os.MkdirAll("/tmp/eth-natspec/data", os.ModePerm)
  102. if err != nil {
  103. t.Errorf("%v", err)
  104. return
  105. }
  106. ks := crypto.NewKeyStorePlain("/tmp/eth-natspec/keys")
  107. ioutil.WriteFile("/tmp/eth-natspec/keys/e273f01c99144c438695e10f24926dc1f9fbf62d/e273f01c99144c438695e10f24926dc1f9fbf62d",
  108. []byte(`{"Id":"RhRXD+fNRKS4jx+7ZfEsNA==","Address":"4nPwHJkUTEOGleEPJJJtwfn79i0=","PrivateKey":"h4ACVpe74uIvi5Cg/2tX/Yrm2xdr3J7QoMbMtNX2CNc="}`), os.ModePerm)
  109. port++
  110. ethereum, err = eth.New(&eth.Config{
  111. DataDir: "/tmp/eth-natspec",
  112. AccountManager: accounts.NewManager(ks),
  113. Name: "test",
  114. })
  115. if err != nil {
  116. t.Errorf("%v", err)
  117. return
  118. }
  119. return
  120. }
  121. func testInit(t *testing.T) (self *testFrontend) {
  122. core.GenesisData = []byte(`{
  123. "` + testAccount + `": {"balance": "` + testBalance + `"}
  124. }`)
  125. ethereum, err := testEth(t)
  126. if err != nil {
  127. t.Errorf("error creating ethereum: %v", err)
  128. return
  129. }
  130. err = ethereum.Start()
  131. if err != nil {
  132. t.Errorf("error starting ethereum: %v", err)
  133. return
  134. }
  135. self = &testFrontend{t: t, ethereum: ethereum}
  136. self.xeth = xe.New(ethereum, self)
  137. self.api = rpc.NewEthereumApi(self.xeth)
  138. addr := self.xeth.Coinbase()
  139. self.coinbase = addr
  140. if addr != "0x"+testAccount {
  141. t.Errorf("CoinBase %v does not match TestAccount 0x%v", addr, testAccount)
  142. }
  143. t.Logf("CoinBase is %v", addr)
  144. balance := self.xeth.BalanceAt(testAccount)
  145. /*if balance != core.TestBalance {
  146. t.Errorf("Balance %v does not match TestBalance %v", balance, core.TestBalance)
  147. }*/
  148. t.Logf("Balance is %v", balance)
  149. self.stateDb = self.ethereum.ChainManager().State().Copy()
  150. return
  151. }
  152. func (self *testFrontend) insertTx(addr, contract, fnsig string, args []string) {
  153. //cb := common.HexToAddress(self.coinbase)
  154. //coinbase := self.ethereum.ChainManager().State().GetStateObject(cb)
  155. hash := common.Bytes2Hex(crypto.Sha3([]byte(fnsig)))
  156. data := "0x" + hash[0:8]
  157. for _, arg := range args {
  158. data = data + common.Bytes2Hex(common.Hex2BytesFixed(arg, 32))
  159. }
  160. self.t.Logf("Tx data: %v", data)
  161. jsontx := `
  162. [{
  163. "from": "` + addr + `",
  164. "to": "` + contract + `",
  165. "value": "100000000000",
  166. "gas": "100000",
  167. "gasPrice": "100000",
  168. "data": "` + data + `"
  169. }]
  170. `
  171. req := &rpc.RpcRequest{
  172. Jsonrpc: "2.0",
  173. Method: "eth_transact",
  174. Params: []byte(jsontx),
  175. Id: 6,
  176. }
  177. var reply interface{}
  178. err0 := self.api.GetRequestReply(req, &reply)
  179. if err0 != nil {
  180. self.t.Errorf("GetRequestReply error: %v", err0)
  181. }
  182. //self.xeth.Transact(addr, contract, "100000000000", "100000", "100000", data)
  183. }
  184. func (self *testFrontend) applyTxs() {
  185. cb := common.HexToAddress(self.coinbase)
  186. block := self.ethereum.ChainManager().NewBlock(cb)
  187. coinbase := self.stateDb.GetStateObject(cb)
  188. coinbase.SetGasPool(big.NewInt(10000000))
  189. txs := self.ethereum.TxPool().GetQueuedTransactions()
  190. for i := 0; i < len(txs); i++ {
  191. for _, tx := range txs {
  192. //self.t.Logf("%v %v %v", i, tx.Nonce(), self.txc)
  193. if tx.Nonce() == self.txc {
  194. _, gas, err := core.ApplyMessage(core.NewEnv(self.stateDb, self.ethereum.ChainManager(), tx, block), tx, coinbase)
  195. //self.ethereum.TxPool().RemoveSet([]*types.Transaction{tx})
  196. self.t.Logf("ApplyMessage: gas %v err %v", gas, err)
  197. self.txc++
  198. }
  199. }
  200. }
  201. //self.ethereum.TxPool().RemoveSet(txs)
  202. self.xeth = self.xeth.WithState(self.stateDb)
  203. }
  204. func (self *testFrontend) registerURL(hash common.Hash, url string) {
  205. hashHex := common.Bytes2Hex(hash[:])
  206. urlBytes := []byte(url)
  207. var bb bool = true
  208. var cnt byte
  209. for bb {
  210. bb = len(urlBytes) > 0
  211. urlb := urlBytes
  212. if len(urlb) > 32 {
  213. urlb = urlb[:32]
  214. }
  215. urlHex := common.Bytes2Hex(urlb)
  216. self.insertTx(self.coinbase, resolver.URLHintContractAddress, "register(uint256,uint8,uint256)", []string{hashHex, common.Bytes2Hex([]byte{cnt}), urlHex})
  217. if len(urlBytes) > 32 {
  218. urlBytes = urlBytes[32:]
  219. } else {
  220. urlBytes = nil
  221. }
  222. cnt++
  223. }
  224. }
  225. func (self *testFrontend) setOwner() {
  226. self.insertTx(self.coinbase, resolver.HashRegContractAddress, "setowner()", []string{})
  227. /*owner := self.xeth.StorageAt("0x"+resolver.HashRegContractAddress, "0x0000000000000000000000000000000000000000000000000000000000000000")
  228. self.t.Logf("owner = %v", owner)
  229. if owner != self.coinbase {
  230. self.t.Errorf("setowner() unsuccessful, owner != coinbase")
  231. }*/
  232. }
  233. func (self *testFrontend) registerNatSpec(codehash, dochash common.Hash) {
  234. codeHex := common.Bytes2Hex(codehash[:])
  235. docHex := common.Bytes2Hex(dochash[:])
  236. self.insertTx(self.coinbase, resolver.HashRegContractAddress, "register(uint256,uint256)", []string{codeHex, docHex})
  237. }
  238. func (self *testFrontend) testResolver() *resolver.Resolver {
  239. return resolver.New(self.xeth, resolver.URLHintContractAddress, resolver.HashRegContractAddress)
  240. }
  241. func TestNatspecE2E(t *testing.T) {
  242. t.Skip()
  243. tf := testInit(t)
  244. defer tf.ethereum.Stop()
  245. resolver.CreateContracts(tf.xeth, testAccount)
  246. t.Logf("URLHint contract registered at %v", resolver.URLHintContractAddress)
  247. t.Logf("HashReg contract registered at %v", resolver.HashRegContractAddress)
  248. tf.applyTxs()
  249. ioutil.WriteFile("/tmp/"+testFileName, []byte(testDocs), os.ModePerm)
  250. dochash := common.BytesToHash(crypto.Sha3([]byte(testDocs)))
  251. codehex := tf.xeth.CodeAt(resolver.HashRegContractAddress)
  252. codehash := common.BytesToHash(crypto.Sha3(common.Hex2Bytes(codehex[2:])))
  253. tf.setOwner()
  254. tf.registerNatSpec(codehash, dochash)
  255. tf.registerURL(dochash, "file:///"+testFileName)
  256. tf.applyTxs()
  257. chash, err := tf.testResolver().KeyToContentHash(codehash)
  258. if err != nil {
  259. t.Errorf("Can't find content hash")
  260. }
  261. t.Logf("chash = %x err = %v", chash, err)
  262. url, err2 := tf.testResolver().ContentHashToUrl(dochash)
  263. if err2 != nil {
  264. t.Errorf("Can't find URL hint")
  265. }
  266. t.Logf("url = %v err = %v", url, err2)
  267. // NatSpec info for register method of HashReg contract installed
  268. // now using the same transactions to check confirm messages
  269. tf.makeNatSpec = true
  270. tf.registerNatSpec(codehash, dochash)
  271. t.Logf("Confirm message: %v\n", tf.lastConfirm)
  272. if tf.lastConfirm != testExpNotice {
  273. t.Errorf("Wrong confirm message, expected '%v', got '%v'", testExpNotice, tf.lastConfirm)
  274. }
  275. tf.setOwner()
  276. t.Logf("Confirm message for unknown method: %v\n", tf.lastConfirm)
  277. if tf.lastConfirm != testExpNotice2 {
  278. t.Errorf("Wrong confirm message, expected '%v', got '%v'", testExpNotice2, tf.lastConfirm)
  279. }
  280. tf.registerURL(dochash, "file:///test.content")
  281. t.Logf("Confirm message for unknown contract: %v\n", tf.lastConfirm)
  282. if tf.lastConfirm != testExpNotice3 {
  283. t.Errorf("Wrong confirm message, expected '%v', got '%v'", testExpNotice3, tf.lastConfirm)
  284. }
  285. }