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.

321 lines
8.3 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. return hezStringToEthAddr(addrStr, name)
  71. }
  72. func parseQueryBJJ(c querier) (*babyjub.PublicKey, error) {
  73. const name = "BJJ"
  74. bjjStr := c.Query(name)
  75. if bjjStr == "" {
  76. return nil, nil
  77. }
  78. return hezStringToBJJ(bjjStr, name)
  79. }
  80. func parseQueryTxType(c querier) (*common.TxType, error) {
  81. const name = "type"
  82. typeStr := c.Query(name)
  83. if typeStr == "" {
  84. return nil, nil
  85. }
  86. switch common.TxType(typeStr) {
  87. case common.TxTypeExit:
  88. ret := common.TxTypeExit
  89. return &ret, nil
  90. case common.TxTypeTransfer:
  91. ret := common.TxTypeTransfer
  92. return &ret, nil
  93. case common.TxTypeDeposit:
  94. ret := common.TxTypeDeposit
  95. return &ret, nil
  96. case common.TxTypeCreateAccountDeposit:
  97. ret := common.TxTypeCreateAccountDeposit
  98. return &ret, nil
  99. case common.TxTypeCreateAccountDepositTransfer:
  100. ret := common.TxTypeCreateAccountDepositTransfer
  101. return &ret, nil
  102. case common.TxTypeDepositTransfer:
  103. ret := common.TxTypeDepositTransfer
  104. return &ret, nil
  105. case common.TxTypeForceTransfer:
  106. ret := common.TxTypeForceTransfer
  107. return &ret, nil
  108. case common.TxTypeForceExit:
  109. ret := common.TxTypeForceExit
  110. return &ret, nil
  111. case common.TxTypeTransferToEthAddr:
  112. ret := common.TxTypeTransferToEthAddr
  113. return &ret, nil
  114. case common.TxTypeTransferToBJJ:
  115. ret := common.TxTypeTransferToBJJ
  116. return &ret, nil
  117. }
  118. return nil, fmt.Errorf(
  119. "invalid %s, %s is not a valid option. Check the valid options in the docmentation",
  120. name, typeStr,
  121. )
  122. }
  123. func parseIdx(c querier) (*common.Idx, error) {
  124. const name = "accountIndex"
  125. idxStr := c.Query(name)
  126. return stringToIdx(idxStr, name)
  127. }
  128. func parseAccountFilters(c querier) (*common.TokenID, *ethCommon.Address, *babyjub.PublicKey, *common.Idx, error) {
  129. // TokenID
  130. tid, err := parseQueryUint("tokenId", nil, 0, maxUint32, c)
  131. if err != nil {
  132. return nil, nil, nil, nil, err
  133. }
  134. var tokenID *common.TokenID
  135. if tid != nil {
  136. tokenID = new(common.TokenID)
  137. *tokenID = common.TokenID(*tid)
  138. }
  139. // Hez Eth addr
  140. addr, err := parseQueryHezEthAddr(c)
  141. if err != nil {
  142. return nil, nil, nil, nil, err
  143. }
  144. // BJJ
  145. bjj, err := parseQueryBJJ(c)
  146. if err != nil {
  147. return nil, nil, nil, nil, err
  148. }
  149. if addr != nil && bjj != nil {
  150. return nil, nil, nil, nil,
  151. errors.New("bjj and hermezEthereumAddress params are incompatible")
  152. }
  153. // Idx
  154. idx, err := parseIdx(c)
  155. if err != nil {
  156. return nil, nil, nil, nil, err
  157. }
  158. if idx != nil && (addr != nil || bjj != nil || tokenID != nil) {
  159. return nil, nil, nil, nil,
  160. errors.New("accountIndex is incompatible with BJJ, hermezEthereumAddress and tokenId")
  161. }
  162. return tokenID, addr, bjj, idx, nil
  163. }
  164. func parseTokenFilters(c querier) ([]common.TokenID, []string, string, error) {
  165. idsStr := c.Query("ids")
  166. symbolsStr := c.Query("symbols")
  167. nameStr := c.Query("name")
  168. var tokensIDs []common.TokenID
  169. if idsStr != "" {
  170. ids := strings.Split(idsStr, ",")
  171. for _, id := range ids {
  172. idUint, err := strconv.Atoi(id)
  173. if err != nil {
  174. return nil, nil, "", err
  175. }
  176. tokenID := common.TokenID(idUint)
  177. tokensIDs = append(tokensIDs, tokenID)
  178. }
  179. }
  180. var symbols []string
  181. if symbolsStr != "" {
  182. symbols = strings.Split(symbolsStr, ",")
  183. }
  184. return tokensIDs, symbols, nameStr, nil
  185. }
  186. // Param parsers
  187. type paramer interface {
  188. Param(string) string
  189. }
  190. func parseParamTxID(c paramer) (common.TxID, error) {
  191. const name = "id"
  192. txIDStr := c.Param(name)
  193. if txIDStr == "" {
  194. return common.TxID{}, fmt.Errorf("%s is required", name)
  195. }
  196. txID, err := common.NewTxIDFromString(txIDStr)
  197. if err != nil {
  198. return common.TxID{}, fmt.Errorf("invalid %s", name)
  199. }
  200. return txID, nil
  201. }
  202. func parseParamIdx(c paramer) (*common.Idx, error) {
  203. const name = "accountIndex"
  204. idxStr := c.Param(name)
  205. return stringToIdx(idxStr, name)
  206. }
  207. func parseParamUint(name string, dflt *uint, min, max uint, c paramer) (*uint, error) { //nolint:SA4009 res may be not overwriten
  208. str := c.Param(name)
  209. return stringToUint(str, name, dflt, min, max)
  210. }
  211. func stringToIdx(idxStr, name string) (*common.Idx, error) {
  212. if idxStr == "" {
  213. return nil, nil
  214. }
  215. splitted := strings.Split(idxStr, ":")
  216. const expectedLen = 3
  217. if len(splitted) != expectedLen || splitted[0] != "hez" {
  218. return nil, fmt.Errorf(
  219. "invalid %s, must follow this: hez:<tokenSymbol>:index", name)
  220. }
  221. // TODO: check that the tokenSymbol match the token related to the account index
  222. idxInt, err := strconv.Atoi(splitted[2])
  223. idx := common.Idx(idxInt)
  224. return &idx, err
  225. }
  226. func stringToUint(uintStr, name string, dflt *uint, min, max uint) (*uint, error) {
  227. if uintStr != "" {
  228. resInt, err := strconv.Atoi(uintStr)
  229. if err != nil || resInt < 0 || resInt < int(min) || resInt > int(max) {
  230. return nil, fmt.Errorf(
  231. "Inavlid %s. Must be an integer within the range [%d, %d]",
  232. name, min, max)
  233. }
  234. res := uint(resInt)
  235. return &res, nil
  236. }
  237. return dflt, nil
  238. }
  239. func hezStringToEthAddr(addrStr, name string) (*ethCommon.Address, error) {
  240. if addrStr == "" {
  241. return nil, nil
  242. }
  243. splitted := strings.Split(addrStr, "hez:")
  244. if len(splitted) != 2 || len(splitted[1]) != 42 {
  245. return nil, fmt.Errorf(
  246. "Invalid %s, must follow this regex: ^hez:0x[a-fA-F0-9]{40}$", name)
  247. }
  248. var addr ethCommon.Address
  249. err := addr.UnmarshalText([]byte(splitted[1]))
  250. return &addr, err
  251. }
  252. func hezStringToBJJ(bjjStr, name string) (*babyjub.PublicKey, error) {
  253. const decodedLen = 33
  254. splitted := strings.Split(bjjStr, "hez:")
  255. if len(splitted) != 2 || len(splitted[1]) != 44 {
  256. return nil, fmt.Errorf(
  257. "Invalid %s, must follow this regex: ^hez:[A-Za-z0-9+/=]{44}$",
  258. name)
  259. }
  260. decoded, err := base64.RawURLEncoding.DecodeString(splitted[1])
  261. if err != nil {
  262. return nil, fmt.Errorf(
  263. "Invalid %s, error decoding base64 string: %s",
  264. name, err.Error())
  265. }
  266. if len(decoded) != decodedLen {
  267. return nil, fmt.Errorf(
  268. "invalid %s, error decoding base64 string: unexpected byte array length",
  269. name)
  270. }
  271. bjjBytes := [decodedLen - 1]byte{}
  272. copy(bjjBytes[:decodedLen-1], decoded[:decodedLen-1])
  273. sum := bjjBytes[0]
  274. for i := 1; i < len(bjjBytes); i++ {
  275. sum += bjjBytes[i]
  276. }
  277. if decoded[decodedLen-1] != sum {
  278. return nil, fmt.Errorf("invalid %s, checksum failed",
  279. name)
  280. }
  281. bjjComp := babyjub.PublicKeyComp(bjjBytes)
  282. bjj, err := bjjComp.Decompress()
  283. if err != nil {
  284. return nil, fmt.Errorf(
  285. "invalid %s, error decompressing public key: %s",
  286. name, err.Error())
  287. }
  288. return bjj, nil
  289. }
  290. func parseEthAddr(c paramer, name string) (*ethCommon.Address, error) {
  291. addrStr := c.Param(name)
  292. if addrStr == "" {
  293. return nil, nil
  294. }
  295. var addr ethCommon.Address
  296. err := addr.UnmarshalText([]byte(addrStr))
  297. return &addr, err
  298. }