natspec_e2e_test.go 9.4 KB

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