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.

430 lines
11 KiB

4 years ago
4 years ago
  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 parseExitFilters(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. func parseAccountFilters(c querier) ([]common.TokenID, *ethCommon.Address, *babyjub.PublicKey, error) {
  221. // TokenID
  222. idsStr := c.Query("tokenIds")
  223. var tokenIDs []common.TokenID
  224. if idsStr != "" {
  225. ids := strings.Split(idsStr, ",")
  226. for _, id := range ids {
  227. idUint, err := strconv.Atoi(id)
  228. if err != nil {
  229. return nil, nil, nil, err
  230. }
  231. tokenID := common.TokenID(idUint)
  232. tokenIDs = append(tokenIDs, tokenID)
  233. }
  234. }
  235. // Hez Eth addr
  236. addr, err := parseQueryHezEthAddr(c)
  237. if err != nil {
  238. return nil, nil, nil, err
  239. }
  240. // BJJ
  241. bjj, err := parseQueryBJJ(c)
  242. if err != nil {
  243. return nil, nil, nil, err
  244. }
  245. if addr != nil && bjj != nil {
  246. return nil, nil, nil,
  247. errors.New("bjj and hermezEthereumAddress params are incompatible")
  248. }
  249. return tokenIDs, addr, bjj, nil
  250. }
  251. // Param parsers
  252. type paramer interface {
  253. Param(string) string
  254. }
  255. func parseParamTxID(c paramer) (common.TxID, error) {
  256. const name = "id"
  257. txIDStr := c.Param(name)
  258. if txIDStr == "" {
  259. return common.TxID{}, fmt.Errorf("%s is required", name)
  260. }
  261. txID, err := common.NewTxIDFromString(txIDStr)
  262. if err != nil {
  263. return common.TxID{}, fmt.Errorf("invalid %s", name)
  264. }
  265. return txID, nil
  266. }
  267. func parseParamIdx(c paramer) (*common.Idx, error) {
  268. const name = "accountIndex"
  269. idxStr := c.Param(name)
  270. return stringToIdx(idxStr, name)
  271. }
  272. func parseParamUint(name string, dflt *uint, min, max uint, c paramer) (*uint, error) { //nolint:SA4009 res may be not overwriten
  273. str := c.Param(name)
  274. return stringToUint(str, name, dflt, min, max)
  275. }
  276. func parseParamInt64(name string, dflt *int64, min, max int64, c paramer) (*int64, error) { //nolint:SA4009 res may be not overwriten
  277. str := c.Param(name)
  278. return stringToInt64(str, name, dflt, min, max)
  279. }
  280. func stringToIdx(idxStr, name string) (*common.Idx, error) {
  281. if idxStr == "" {
  282. return nil, nil
  283. }
  284. splitted := strings.Split(idxStr, ":")
  285. const expectedLen = 3
  286. if len(splitted) != expectedLen || splitted[0] != "hez" {
  287. return nil, fmt.Errorf(
  288. "invalid %s, must follow this: hez:<tokenSymbol>:index", name)
  289. }
  290. // TODO: check that the tokenSymbol match the token related to the account index
  291. idxInt, err := strconv.Atoi(splitted[2])
  292. idx := common.Idx(idxInt)
  293. return &idx, err
  294. }
  295. func stringToUint(uintStr, name string, dflt *uint, min, max uint) (*uint, error) {
  296. if uintStr != "" {
  297. resInt, err := strconv.Atoi(uintStr)
  298. if err != nil || resInt < 0 || resInt < int(min) || resInt > int(max) {
  299. return nil, fmt.Errorf(
  300. "Invalid %s. Must be an integer within the range [%d, %d]",
  301. name, min, max)
  302. }
  303. res := uint(resInt)
  304. return &res, nil
  305. }
  306. return dflt, nil
  307. }
  308. func stringToInt64(uintStr, name string, dflt *int64, min, max int64) (*int64, error) {
  309. if uintStr != "" {
  310. resInt, err := strconv.Atoi(uintStr)
  311. if err != nil || resInt < 0 || resInt < int(min) || resInt > int(max) {
  312. return nil, fmt.Errorf(
  313. "Invalid %s. Must be an integer within the range [%d, %d]",
  314. name, min, max)
  315. }
  316. res := int64(resInt)
  317. return &res, nil
  318. }
  319. return dflt, nil
  320. }
  321. func hezStringToEthAddr(addrStr, name string) (*ethCommon.Address, error) {
  322. if addrStr == "" {
  323. return nil, nil
  324. }
  325. splitted := strings.Split(addrStr, "hez:")
  326. if len(splitted) != 2 || len(splitted[1]) != 42 {
  327. return nil, fmt.Errorf(
  328. "Invalid %s, must follow this regex: ^hez:0x[a-fA-F0-9]{40}$", name)
  329. }
  330. var addr ethCommon.Address
  331. err := addr.UnmarshalText([]byte(splitted[1]))
  332. return &addr, err
  333. }
  334. func hezStringToBJJ(bjjStr, name string) (*babyjub.PublicKey, error) {
  335. const decodedLen = 33
  336. splitted := strings.Split(bjjStr, "hez:")
  337. if len(splitted) != 2 || len(splitted[1]) != 44 {
  338. return nil, fmt.Errorf(
  339. "Invalid %s, must follow this regex: ^hez:[A-Za-z0-9+/=]{44}$",
  340. name)
  341. }
  342. decoded, err := base64.RawURLEncoding.DecodeString(splitted[1])
  343. if err != nil {
  344. return nil, fmt.Errorf(
  345. "Invalid %s, error decoding base64 string: %s",
  346. name, err.Error())
  347. }
  348. if len(decoded) != decodedLen {
  349. return nil, fmt.Errorf(
  350. "invalid %s, error decoding base64 string: unexpected byte array length",
  351. name)
  352. }
  353. bjjBytes := [decodedLen - 1]byte{}
  354. copy(bjjBytes[:decodedLen-1], decoded[:decodedLen-1])
  355. sum := bjjBytes[0]
  356. for i := 1; i < len(bjjBytes); i++ {
  357. sum += bjjBytes[i]
  358. }
  359. if decoded[decodedLen-1] != sum {
  360. return nil, fmt.Errorf("invalid %s, checksum failed",
  361. name)
  362. }
  363. bjjComp := babyjub.PublicKeyComp(bjjBytes)
  364. bjj, err := bjjComp.Decompress()
  365. if err != nil {
  366. return nil, fmt.Errorf(
  367. "invalid %s, error decompressing public key: %s",
  368. name, err.Error())
  369. }
  370. return bjj, nil
  371. }
  372. func parseQueryEthAddr(name string, c querier) (*ethCommon.Address, error) {
  373. addrStr := c.Query(name)
  374. if addrStr == "" {
  375. return nil, nil
  376. }
  377. return parseEthAddr(addrStr)
  378. }
  379. func parseParamEthAddr(name string, c paramer) (*ethCommon.Address, error) {
  380. addrStr := c.Param(name)
  381. if addrStr == "" {
  382. return nil, nil
  383. }
  384. return parseEthAddr(addrStr)
  385. }
  386. func parseEthAddr(ethAddrStr string) (*ethCommon.Address, error) {
  387. var addr ethCommon.Address
  388. err := addr.UnmarshalText([]byte(ethAddrStr))
  389. return &addr, err
  390. }
  391. func parseParamHezEthAddr(c paramer) (*ethCommon.Address, error) {
  392. const name = "hermezEthereumAddress"
  393. addrStr := c.Param(name)
  394. return hezStringToEthAddr(addrStr, name)
  395. }