natspec.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. package natspec
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "github.com/robertkrimen/otto"
  7. "strings"
  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/crypto"
  12. "github.com/ethereum/go-ethereum/xeth"
  13. )
  14. type abi2method map[[8]byte]*method
  15. type NatSpec struct {
  16. jsvm *otto.Otto
  17. userDocJson, abiDocJson []byte
  18. userDoc userDoc
  19. tx, data string
  20. // abiDoc abiDoc
  21. }
  22. func New(xeth *xeth.XEth, tx string, http *docserver.DocServer) (self *NatSpec, err error) {
  23. // extract contract address from tx
  24. var obj map[string]json.RawMessage
  25. err = json.Unmarshal([]byte(tx), &obj)
  26. if err != nil {
  27. return
  28. }
  29. var tmp []map[string]string
  30. err = json.Unmarshal(obj["params"], &tmp)
  31. if err != nil {
  32. return
  33. }
  34. contractAddress := tmp[0]["to"]
  35. // retrieve contract hash from state
  36. if !xeth.IsContract(contractAddress) {
  37. err = fmt.Errorf("NatSpec error: contract not found")
  38. return
  39. }
  40. codeHash := xeth.CodeAt(contractAddress)
  41. // parse out host/domain
  42. // set up nameresolver with natspecreg + urlhint contract addresses
  43. stateReg := NewStateReg(xeth)
  44. res := resolver.New(
  45. xeth,
  46. stateReg.caNatSpec,
  47. stateReg.caURL,
  48. )
  49. // resolve host via nameReg/UrlHint Resolver
  50. uri, hash, err := res.NameToUrl(codeHash)
  51. if err != nil {
  52. return
  53. }
  54. // get content via http client and authenticate content using hash
  55. content, err := http.GetAuthContent(uri, hash)
  56. if err != nil {
  57. return
  58. }
  59. // get abi, userdoc
  60. var obj2 map[string]json.RawMessage
  61. err = json.Unmarshal(content, &obj2)
  62. if err != nil {
  63. return
  64. }
  65. abi := []byte(obj2["abi"])
  66. userdoc := []byte(obj2["userdoc"])
  67. self, err = NewWithDocs(abi, userdoc, tx)
  68. return
  69. }
  70. func NewWithDocs(abiDocJson, userDocJson []byte, tx string) (self *NatSpec, err error) {
  71. var obj map[string]json.RawMessage
  72. err = json.Unmarshal([]byte(tx), &obj)
  73. if err != nil {
  74. return
  75. }
  76. var tmp []map[string]string
  77. err = json.Unmarshal(obj["params"], &tmp)
  78. if err != nil {
  79. return
  80. }
  81. data := tmp[0]["data"]
  82. self = &NatSpec{
  83. jsvm: otto.New(),
  84. abiDocJson: abiDocJson,
  85. userDocJson: userDocJson,
  86. tx: tx,
  87. data: data,
  88. }
  89. _, err = self.jsvm.Run(natspecJS)
  90. if err != nil {
  91. return
  92. }
  93. _, err = self.jsvm.Run("var natspec = require('natspec');")
  94. if err != nil {
  95. return
  96. }
  97. err = json.Unmarshal(userDocJson, &self.userDoc)
  98. // err = parseAbiJson(abiDocJson, &self.abiDoc)
  99. return
  100. }
  101. // type abiDoc []method
  102. // type method struct {
  103. // Name string `json:name`
  104. // Inputs []input `json:inputs`
  105. // abiKey [8]byte
  106. // }
  107. // type input struct {
  108. // Name string `json:name`
  109. // Type string `json:type`
  110. // }
  111. type method struct {
  112. Notice string `json:notice`
  113. name string
  114. }
  115. type userDoc struct {
  116. Methods map[string]*method `json:methods`
  117. }
  118. func (self *NatSpec) makeAbi2method(abiKey [8]byte) (meth *method) {
  119. for signature, m := range self.userDoc.Methods {
  120. name := strings.Split(signature, "(")[0]
  121. hash := []byte(common.Bytes2Hex(crypto.Sha3([]byte(signature))))
  122. var key [8]byte
  123. copy(key[:], hash[:8])
  124. if bytes.Equal(key[:], abiKey[:]) {
  125. meth = m
  126. meth.name = name
  127. return
  128. }
  129. }
  130. return
  131. }
  132. func (self *NatSpec) Notice() (notice string, err error) {
  133. var abiKey [8]byte
  134. if len(self.data) < 10 {
  135. err = fmt.Errorf("Invalid transaction data")
  136. return
  137. }
  138. copy(abiKey[:], self.data[2:10])
  139. meth := self.makeAbi2method(abiKey)
  140. if meth == nil {
  141. err = fmt.Errorf("abi key %x does not match any method %v")
  142. return
  143. }
  144. notice, err = self.noticeForMethod(self.tx, meth.name, meth.Notice)
  145. return
  146. }
  147. func (self *NatSpec) noticeForMethod(tx string, name, expression string) (notice string, err error) {
  148. if _, err = self.jsvm.Run("var transaction = " + tx + ";"); err != nil {
  149. return "", fmt.Errorf("natspec.js error setting transaction: %v", err)
  150. }
  151. if _, err = self.jsvm.Run("var abi = " + string(self.abiDocJson) + ";"); err != nil {
  152. return "", fmt.Errorf("natspec.js error setting abi: %v", err)
  153. }
  154. if _, err = self.jsvm.Run("var method = '" + name + "';"); err != nil {
  155. return "", fmt.Errorf("natspec.js error setting method: %v", err)
  156. }
  157. if _, err = self.jsvm.Run("var expression = \"" + expression + "\";"); err != nil {
  158. return "", fmt.Errorf("natspec.js error setting expression: %v", err)
  159. }
  160. self.jsvm.Run("var call = {method: method,abi: abi,transaction: transaction};")
  161. value, err := self.jsvm.Run("natspec.evaluateExpression(expression, call);")
  162. if err != nil {
  163. return "", fmt.Errorf("natspec.js error evaluating expression: %v", err)
  164. }
  165. evalError := "Natspec evaluation failed, wrong input params"
  166. if value.String() == evalError {
  167. return "", fmt.Errorf("natspec.js error evaluating expression: wrong input params in expression '%s'", expression)
  168. }
  169. if len(value.String()) == 0 {
  170. return "", fmt.Errorf("natspec.js error evaluating expression")
  171. }
  172. return value.String(), nil
  173. }