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.

352 lines
9.1 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. func parseBidFilters(c querier) (*uint, *ethCommon.Address, error) {
  187. slotNum, err := parseQueryUint("slotNum", nil, 0, maxUint32, c)
  188. if err != nil {
  189. return nil, nil, err
  190. }
  191. bidderAddr, err := parseQueryEthAddr("bidderAddr", c)
  192. if err != nil {
  193. return nil, nil, err
  194. }
  195. return slotNum, bidderAddr, nil
  196. }
  197. // Param parsers
  198. type paramer interface {
  199. Param(string) string
  200. }
  201. func parseParamTxID(c paramer) (common.TxID, error) {
  202. const name = "id"
  203. txIDStr := c.Param(name)
  204. if txIDStr == "" {
  205. return common.TxID{}, fmt.Errorf("%s is required", name)
  206. }
  207. txID, err := common.NewTxIDFromString(txIDStr)
  208. if err != nil {
  209. return common.TxID{}, fmt.Errorf("invalid %s", name)
  210. }
  211. return txID, nil
  212. }
  213. func parseParamIdx(c paramer) (*common.Idx, error) {
  214. const name = "accountIndex"
  215. idxStr := c.Param(name)
  216. return stringToIdx(idxStr, name)
  217. }
  218. func parseParamUint(name string, dflt *uint, min, max uint, c paramer) (*uint, error) { //nolint:SA4009 res may be not overwriten
  219. str := c.Param(name)
  220. return stringToUint(str, name, dflt, min, max)
  221. }
  222. func stringToIdx(idxStr, name string) (*common.Idx, error) {
  223. if idxStr == "" {
  224. return nil, nil
  225. }
  226. splitted := strings.Split(idxStr, ":")
  227. const expectedLen = 3
  228. if len(splitted) != expectedLen || splitted[0] != "hez" {
  229. return nil, fmt.Errorf(
  230. "invalid %s, must follow this: hez:<tokenSymbol>:index", name)
  231. }
  232. // TODO: check that the tokenSymbol match the token related to the account index
  233. idxInt, err := strconv.Atoi(splitted[2])
  234. idx := common.Idx(idxInt)
  235. return &idx, err
  236. }
  237. func stringToUint(uintStr, name string, dflt *uint, min, max uint) (*uint, error) {
  238. if uintStr != "" {
  239. resInt, err := strconv.Atoi(uintStr)
  240. if err != nil || resInt < 0 || resInt < int(min) || resInt > int(max) {
  241. return nil, fmt.Errorf(
  242. "Inavlid %s. Must be an integer within the range [%d, %d]",
  243. name, min, max)
  244. }
  245. res := uint(resInt)
  246. return &res, nil
  247. }
  248. return dflt, nil
  249. }
  250. func hezStringToEthAddr(addrStr, name string) (*ethCommon.Address, error) {
  251. if addrStr == "" {
  252. return nil, nil
  253. }
  254. splitted := strings.Split(addrStr, "hez:")
  255. if len(splitted) != 2 || len(splitted[1]) != 42 {
  256. return nil, fmt.Errorf(
  257. "Invalid %s, must follow this regex: ^hez:0x[a-fA-F0-9]{40}$", name)
  258. }
  259. var addr ethCommon.Address
  260. err := addr.UnmarshalText([]byte(splitted[1]))
  261. return &addr, err
  262. }
  263. func hezStringToBJJ(bjjStr, name string) (*babyjub.PublicKey, error) {
  264. const decodedLen = 33
  265. splitted := strings.Split(bjjStr, "hez:")
  266. if len(splitted) != 2 || len(splitted[1]) != 44 {
  267. return nil, fmt.Errorf(
  268. "Invalid %s, must follow this regex: ^hez:[A-Za-z0-9+/=]{44}$",
  269. name)
  270. }
  271. decoded, err := base64.RawURLEncoding.DecodeString(splitted[1])
  272. if err != nil {
  273. return nil, fmt.Errorf(
  274. "Invalid %s, error decoding base64 string: %s",
  275. name, err.Error())
  276. }
  277. if len(decoded) != decodedLen {
  278. return nil, fmt.Errorf(
  279. "invalid %s, error decoding base64 string: unexpected byte array length",
  280. name)
  281. }
  282. bjjBytes := [decodedLen - 1]byte{}
  283. copy(bjjBytes[:decodedLen-1], decoded[:decodedLen-1])
  284. sum := bjjBytes[0]
  285. for i := 1; i < len(bjjBytes); i++ {
  286. sum += bjjBytes[i]
  287. }
  288. if decoded[decodedLen-1] != sum {
  289. return nil, fmt.Errorf("invalid %s, checksum failed",
  290. name)
  291. }
  292. bjjComp := babyjub.PublicKeyComp(bjjBytes)
  293. bjj, err := bjjComp.Decompress()
  294. if err != nil {
  295. return nil, fmt.Errorf(
  296. "invalid %s, error decompressing public key: %s",
  297. name, err.Error())
  298. }
  299. return bjj, nil
  300. }
  301. func parseQueryEthAddr(name string, c querier) (*ethCommon.Address, error) {
  302. addrStr := c.Query(name)
  303. if addrStr == "" {
  304. return nil, nil
  305. }
  306. return parseEthAddr(addrStr)
  307. }
  308. func parseParamEthAddr(name string, c paramer) (*ethCommon.Address, error) {
  309. addrStr := c.Param(name)
  310. if addrStr == "" {
  311. return nil, nil
  312. }
  313. return parseEthAddr(addrStr)
  314. }
  315. func parseEthAddr(ethAddrStr string) (*ethCommon.Address, error) {
  316. var addr ethCommon.Address
  317. err := addr.UnmarshalText([]byte(ethAddrStr))
  318. return &addr, err
  319. }
  320. func parseParamHezEthAddr(c paramer) (*ethCommon.Address, error) {
  321. const name = "hermezEthereumAddress"
  322. addrStr := c.Param(name)
  323. return hezStringToEthAddr(addrStr, name)
  324. }