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.

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