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.

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