You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

304 lines
7.9 KiB

  1. package api
  2. import (
  3. "encoding/base64"
  4. "errors"
  5. "fmt"
  6. "strconv"
  7. "strings"
  8. ethCommon "github.com/ethereum/go-ethereum/common"
  9. "github.com/hermeznetwork/hermez-node/common"
  10. "github.com/hermeznetwork/hermez-node/db/historydb"
  11. "github.com/iden3/go-iden3-crypto/babyjub"
  12. )
  13. // Query parsers
  14. type querier interface {
  15. Query(string) string
  16. }
  17. func parsePagination(c querier) (fromItem *uint, order string, limit *uint, err error) {
  18. // FromItem
  19. fromItem, err = parseQueryUint("fromItem", nil, 0, maxUint32, c)
  20. if err != nil {
  21. return nil, "", nil, err
  22. }
  23. // Order
  24. order = dfltOrder
  25. const orderName = "order"
  26. orderStr := c.Query(orderName)
  27. if orderStr != "" && !(orderStr == historydb.OrderAsc || historydb.OrderDesc == orderStr) {
  28. return nil, "", nil, errors.New(
  29. "order must have the value " + historydb.OrderAsc + " or " + historydb.OrderDesc,
  30. )
  31. }
  32. if orderStr == historydb.OrderAsc {
  33. order = historydb.OrderAsc
  34. } else if orderStr == historydb.OrderDesc {
  35. order = historydb.OrderDesc
  36. }
  37. // Limit
  38. limit = new(uint)
  39. *limit = dfltLimit
  40. limit, err = parseQueryUint("limit", limit, 1, maxLimit, c)
  41. if err != nil {
  42. return nil, "", nil, err
  43. }
  44. return fromItem, order, limit, nil
  45. }
  46. func parseQueryUint(name string, dflt *uint, min, max uint, c querier) (*uint, error) { //nolint:SA4009 res may be not overwriten
  47. str := c.Query(name)
  48. return stringToUint(str, name, dflt, min, max)
  49. }
  50. func parseQueryBool(name string, dflt *bool, c querier) (*bool, error) { //nolint:SA4009 res may be not overwriten
  51. str := c.Query(name)
  52. if str == "" {
  53. return dflt, nil
  54. }
  55. if str == "true" {
  56. res := new(bool)
  57. *res = true
  58. return res, nil
  59. }
  60. if str == "false" {
  61. res := new(bool)
  62. *res = false
  63. return res, nil
  64. }
  65. return nil, fmt.Errorf("Inavlid %s. Must be eithe true or false", name)
  66. }
  67. func parseQueryHezEthAddr(c querier) (*ethCommon.Address, error) {
  68. const name = "hermezEthereumAddress"
  69. addrStr := c.Query(name)
  70. if addrStr == "" {
  71. return nil, nil
  72. }
  73. splitted := strings.Split(addrStr, "hez:")
  74. if len(splitted) != 2 || len(splitted[1]) != 42 {
  75. return nil, fmt.Errorf(
  76. "Invalid %s, must follow this regex: ^hez:0x[a-fA-F0-9]{40}$", name)
  77. }
  78. var addr ethCommon.Address
  79. err := addr.UnmarshalText([]byte(splitted[1]))
  80. return &addr, err
  81. }
  82. func parseQueryBJJ(c querier) (*babyjub.PublicKey, error) {
  83. const name = "BJJ"
  84. const decodedLen = 33
  85. bjjStr := c.Query(name)
  86. if bjjStr == "" {
  87. return nil, nil
  88. }
  89. splitted := strings.Split(bjjStr, "hez:")
  90. if len(splitted) != 2 || len(splitted[1]) != 44 {
  91. return nil, fmt.Errorf(
  92. "Invalid %s, must follow this regex: ^hez:[A-Za-z0-9+/=]{44}$",
  93. name)
  94. }
  95. decoded, err := base64.RawURLEncoding.DecodeString(splitted[1])
  96. if err != nil {
  97. return nil, fmt.Errorf(
  98. "Invalid %s, error decoding base64 string: %s",
  99. name, err.Error())
  100. }
  101. if len(decoded) != decodedLen {
  102. return nil, fmt.Errorf(
  103. "invalid %s, error decoding base64 string: unexpected byte array length",
  104. name)
  105. }
  106. bjjBytes := [decodedLen - 1]byte{}
  107. copy(bjjBytes[:decodedLen-1], decoded[:decodedLen-1])
  108. sum := bjjBytes[0]
  109. for i := 1; i < len(bjjBytes); i++ {
  110. sum += bjjBytes[i]
  111. }
  112. if decoded[decodedLen-1] != sum {
  113. return nil, fmt.Errorf("invalid %s, checksum failed",
  114. name)
  115. }
  116. bjjComp := babyjub.PublicKeyComp(bjjBytes)
  117. bjj, err := bjjComp.Decompress()
  118. if err != nil {
  119. return nil, fmt.Errorf(
  120. "invalid %s, error decompressing public key: %s",
  121. name, err.Error())
  122. }
  123. return bjj, nil
  124. }
  125. func parseQueryTxType(c querier) (*common.TxType, error) {
  126. const name = "type"
  127. typeStr := c.Query(name)
  128. if typeStr == "" {
  129. return nil, nil
  130. }
  131. switch common.TxType(typeStr) {
  132. case common.TxTypeExit:
  133. ret := common.TxTypeExit
  134. return &ret, nil
  135. case common.TxTypeTransfer:
  136. ret := common.TxTypeTransfer
  137. return &ret, nil
  138. case common.TxTypeDeposit:
  139. ret := common.TxTypeDeposit
  140. return &ret, nil
  141. case common.TxTypeCreateAccountDeposit:
  142. ret := common.TxTypeCreateAccountDeposit
  143. return &ret, nil
  144. case common.TxTypeCreateAccountDepositTransfer:
  145. ret := common.TxTypeCreateAccountDepositTransfer
  146. return &ret, nil
  147. case common.TxTypeDepositTransfer:
  148. ret := common.TxTypeDepositTransfer
  149. return &ret, nil
  150. case common.TxTypeForceTransfer:
  151. ret := common.TxTypeForceTransfer
  152. return &ret, nil
  153. case common.TxTypeForceExit:
  154. ret := common.TxTypeForceExit
  155. return &ret, nil
  156. case common.TxTypeTransferToEthAddr:
  157. ret := common.TxTypeTransferToEthAddr
  158. return &ret, nil
  159. case common.TxTypeTransferToBJJ:
  160. ret := common.TxTypeTransferToBJJ
  161. return &ret, nil
  162. }
  163. return nil, fmt.Errorf(
  164. "invalid %s, %s is not a valid option. Check the valid options in the docmentation",
  165. name, typeStr,
  166. )
  167. }
  168. func parseIdx(c querier) (*common.Idx, error) {
  169. const name = "accountIndex"
  170. idxStr := c.Query(name)
  171. return stringToIdx(idxStr, name)
  172. }
  173. func parseAccountFilters(c querier) (*common.TokenID, *ethCommon.Address, *babyjub.PublicKey, *common.Idx, error) {
  174. // TokenID
  175. tid, err := parseQueryUint("tokenId", nil, 0, maxUint32, c)
  176. if err != nil {
  177. return nil, nil, nil, nil, err
  178. }
  179. var tokenID *common.TokenID
  180. if tid != nil {
  181. tokenID = new(common.TokenID)
  182. *tokenID = common.TokenID(*tid)
  183. }
  184. // Hez Eth addr
  185. addr, err := parseQueryHezEthAddr(c)
  186. if err != nil {
  187. return nil, nil, nil, nil, err
  188. }
  189. // BJJ
  190. bjj, err := parseQueryBJJ(c)
  191. if err != nil {
  192. return nil, nil, nil, nil, err
  193. }
  194. if addr != nil && bjj != nil {
  195. return nil, nil, nil, nil,
  196. errors.New("bjj and hermezEthereumAddress params are incompatible")
  197. }
  198. // Idx
  199. idx, err := parseIdx(c)
  200. if err != nil {
  201. return nil, nil, nil, nil, err
  202. }
  203. if idx != nil && (addr != nil || bjj != nil || tokenID != nil) {
  204. return nil, nil, nil, nil,
  205. errors.New("accountIndex is incompatible with BJJ, hermezEthereumAddress and tokenId")
  206. }
  207. return tokenID, addr, bjj, idx, nil
  208. }
  209. func parseTokenFilters(c querier) ([]common.TokenID, []string, string, error) {
  210. idsStr := c.Query("ids")
  211. symbolsStr := c.Query("symbols")
  212. nameStr := c.Query("name")
  213. var tokensIDs []common.TokenID
  214. if idsStr != "" {
  215. ids := strings.Split(idsStr, ",")
  216. for _, id := range ids {
  217. idUint, err := strconv.Atoi(id)
  218. if err != nil {
  219. return nil, nil, "", err
  220. }
  221. tokenID := common.TokenID(idUint)
  222. tokensIDs = append(tokensIDs, tokenID)
  223. }
  224. }
  225. var symbols []string
  226. if symbolsStr != "" {
  227. symbols = strings.Split(symbolsStr, ",")
  228. }
  229. return tokensIDs, symbols, nameStr, nil
  230. }
  231. // Param parsers
  232. type paramer interface {
  233. Param(string) string
  234. }
  235. func parseParamTxID(c paramer) (common.TxID, error) {
  236. const name = "id"
  237. txIDStr := c.Param(name)
  238. if txIDStr == "" {
  239. return common.TxID{}, fmt.Errorf("%s is required", name)
  240. }
  241. txID, err := common.NewTxIDFromString(txIDStr)
  242. if err != nil {
  243. return common.TxID{}, fmt.Errorf("invalid %s", name)
  244. }
  245. return txID, nil
  246. }
  247. func parseParamIdx(c paramer) (*common.Idx, error) {
  248. const name = "accountIndex"
  249. idxStr := c.Param(name)
  250. return stringToIdx(idxStr, name)
  251. }
  252. func parseParamUint(name string, dflt *uint, min, max uint, c paramer) (*uint, error) { //nolint:SA4009 res may be not overwriten
  253. str := c.Param(name)
  254. return stringToUint(str, name, dflt, min, max)
  255. }
  256. func stringToIdx(idxStr, name string) (*common.Idx, error) {
  257. if idxStr == "" {
  258. return nil, nil
  259. }
  260. splitted := strings.Split(idxStr, ":")
  261. const expectedLen = 3
  262. if len(splitted) != expectedLen || splitted[0] != "hez" {
  263. return nil, fmt.Errorf(
  264. "invalid %s, must follow this: hez:<tokenSymbol>:index", name)
  265. }
  266. // TODO: check that the tokenSymbol match the token related to the account index
  267. idxInt, err := strconv.Atoi(splitted[2])
  268. idx := common.Idx(idxInt)
  269. return &idx, err
  270. }
  271. func stringToUint(uintStr, name string, dflt *uint, min, max uint) (*uint, error) {
  272. if uintStr != "" {
  273. resInt, err := strconv.Atoi(uintStr)
  274. if err != nil || resInt < 0 || resInt < int(min) || resInt > int(max) {
  275. return nil, fmt.Errorf(
  276. "Inavlid %s. Must be an integer within the range [%d, %d]",
  277. name, min, max)
  278. }
  279. res := uint(resInt)
  280. return &res, nil
  281. }
  282. return dflt, nil
  283. }