natspec.go 4.6 KB

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