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.

520 lines
14 KiB

Redo coordinator structure, connect API to node - API: - Modify the constructor so that hardcoded rollup constants don't need to be passed (introduce a `Config` and use `configAPI` internally) - Common: - Update rollup constants with proper *big.Int when required - Add BidCoordinator and Slot structs used by the HistoryDB and Synchronizer. - Add helper methods to AuctionConstants - AuctionVariables: Add column `DefaultSlotSetBidSlotNum` (in the SQL table: `default_slot_set_bid_slot_num`), which indicates at which slotNum does the `DefaultSlotSetBid` specified starts applying. - Config: - Move coordinator exclusive configuration from the node config to the coordinator config - Coordinator: - Reorganize the code towards having the goroutines started and stopped from the coordinator itself instead of the node. - Remove all stop and stopped channels, and use context.Context and sync.WaitGroup instead. - Remove BatchInfo setters and assing variables directly - In ServerProof and ServerProofPool use context instead stop channel. - Use message passing to notify the coordinator about sync updates and reorgs - Introduce the Pipeline, which can be started and stopped by the Coordinator - Introduce the TxManager, which manages ethereum transactions (the TxManager is also in charge of making the forge call to the rollup smart contract). The TxManager keeps ethereum transactions and: 1. Waits for the transaction to be accepted 2. Waits for the transaction to be confirmed for N blocks - In forge logic, first prepare a batch and then wait for an available server proof to have all work ready once the proof server is ready. - Remove the `isForgeSequence` method which was querying the smart contract, and instead use notifications sent by the Synchronizer to figure out if it's forging time. - Update test (which is a minimal test to manually see if the coordinator starts) - HistoryDB: - Add method to get the number of batches in a slot (used to detect when a slot has passed the bid winner forging deadline) - Add method to get the best bid and associated coordinator of a slot (used to detect the forgerAddress that can forge the slot) - General: - Rename some instances of `currentBlock` to `lastBlock` to be more clear. - Node: - Connect the API to the node and call the methods to update cached state when the sync advances blocks. - Call methods to update Coordinator state when the sync advances blocks and finds reorgs. - Synchronizer: - Add Auction field in the Stats, which contain the current slot with info about highest bidder and other related info required to know who can forge in the current block. - Better organization of cached state: - On Sync, update the internal cached state - On Init or Reorg, load the state from HistoryDB into the internal cached state.
4 years ago
Redo coordinator structure, connect API to node - API: - Modify the constructor so that hardcoded rollup constants don't need to be passed (introduce a `Config` and use `configAPI` internally) - Common: - Update rollup constants with proper *big.Int when required - Add BidCoordinator and Slot structs used by the HistoryDB and Synchronizer. - Add helper methods to AuctionConstants - AuctionVariables: Add column `DefaultSlotSetBidSlotNum` (in the SQL table: `default_slot_set_bid_slot_num`), which indicates at which slotNum does the `DefaultSlotSetBid` specified starts applying. - Config: - Move coordinator exclusive configuration from the node config to the coordinator config - Coordinator: - Reorganize the code towards having the goroutines started and stopped from the coordinator itself instead of the node. - Remove all stop and stopped channels, and use context.Context and sync.WaitGroup instead. - Remove BatchInfo setters and assing variables directly - In ServerProof and ServerProofPool use context instead stop channel. - Use message passing to notify the coordinator about sync updates and reorgs - Introduce the Pipeline, which can be started and stopped by the Coordinator - Introduce the TxManager, which manages ethereum transactions (the TxManager is also in charge of making the forge call to the rollup smart contract). The TxManager keeps ethereum transactions and: 1. Waits for the transaction to be accepted 2. Waits for the transaction to be confirmed for N blocks - In forge logic, first prepare a batch and then wait for an available server proof to have all work ready once the proof server is ready. - Remove the `isForgeSequence` method which was querying the smart contract, and instead use notifications sent by the Synchronizer to figure out if it's forging time. - Update test (which is a minimal test to manually see if the coordinator starts) - HistoryDB: - Add method to get the number of batches in a slot (used to detect when a slot has passed the bid winner forging deadline) - Add method to get the best bid and associated coordinator of a slot (used to detect the forgerAddress that can forge the slot) - General: - Rename some instances of `currentBlock` to `lastBlock` to be more clear. - Node: - Connect the API to the node and call the methods to update cached state when the sync advances blocks. - Call methods to update Coordinator state when the sync advances blocks and finds reorgs. - Synchronizer: - Add Auction field in the Stats, which contain the current slot with info about highest bidder and other related info required to know who can forge in the current block. - Better organization of cached state: - On Sync, update the internal cached state - On Init or Reorg, load the state from HistoryDB into the internal cached state.
4 years ago
Redo coordinator structure, connect API to node - API: - Modify the constructor so that hardcoded rollup constants don't need to be passed (introduce a `Config` and use `configAPI` internally) - Common: - Update rollup constants with proper *big.Int when required - Add BidCoordinator and Slot structs used by the HistoryDB and Synchronizer. - Add helper methods to AuctionConstants - AuctionVariables: Add column `DefaultSlotSetBidSlotNum` (in the SQL table: `default_slot_set_bid_slot_num`), which indicates at which slotNum does the `DefaultSlotSetBid` specified starts applying. - Config: - Move coordinator exclusive configuration from the node config to the coordinator config - Coordinator: - Reorganize the code towards having the goroutines started and stopped from the coordinator itself instead of the node. - Remove all stop and stopped channels, and use context.Context and sync.WaitGroup instead. - Remove BatchInfo setters and assing variables directly - In ServerProof and ServerProofPool use context instead stop channel. - Use message passing to notify the coordinator about sync updates and reorgs - Introduce the Pipeline, which can be started and stopped by the Coordinator - Introduce the TxManager, which manages ethereum transactions (the TxManager is also in charge of making the forge call to the rollup smart contract). The TxManager keeps ethereum transactions and: 1. Waits for the transaction to be accepted 2. Waits for the transaction to be confirmed for N blocks - In forge logic, first prepare a batch and then wait for an available server proof to have all work ready once the proof server is ready. - Remove the `isForgeSequence` method which was querying the smart contract, and instead use notifications sent by the Synchronizer to figure out if it's forging time. - Update test (which is a minimal test to manually see if the coordinator starts) - HistoryDB: - Add method to get the number of batches in a slot (used to detect when a slot has passed the bid winner forging deadline) - Add method to get the best bid and associated coordinator of a slot (used to detect the forgerAddress that can forge the slot) - General: - Rename some instances of `currentBlock` to `lastBlock` to be more clear. - Node: - Connect the API to the node and call the methods to update cached state when the sync advances blocks. - Call methods to update Coordinator state when the sync advances blocks and finds reorgs. - Synchronizer: - Add Auction field in the Stats, which contain the current slot with info about highest bidder and other related info required to know who can forge in the current block. - Better organization of cached state: - On Sync, update the internal cached state - On Init or Reorg, load the state from HistoryDB into the internal cached state.
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
Redo coordinator structure, connect API to node - API: - Modify the constructor so that hardcoded rollup constants don't need to be passed (introduce a `Config` and use `configAPI` internally) - Common: - Update rollup constants with proper *big.Int when required - Add BidCoordinator and Slot structs used by the HistoryDB and Synchronizer. - Add helper methods to AuctionConstants - AuctionVariables: Add column `DefaultSlotSetBidSlotNum` (in the SQL table: `default_slot_set_bid_slot_num`), which indicates at which slotNum does the `DefaultSlotSetBid` specified starts applying. - Config: - Move coordinator exclusive configuration from the node config to the coordinator config - Coordinator: - Reorganize the code towards having the goroutines started and stopped from the coordinator itself instead of the node. - Remove all stop and stopped channels, and use context.Context and sync.WaitGroup instead. - Remove BatchInfo setters and assing variables directly - In ServerProof and ServerProofPool use context instead stop channel. - Use message passing to notify the coordinator about sync updates and reorgs - Introduce the Pipeline, which can be started and stopped by the Coordinator - Introduce the TxManager, which manages ethereum transactions (the TxManager is also in charge of making the forge call to the rollup smart contract). The TxManager keeps ethereum transactions and: 1. Waits for the transaction to be accepted 2. Waits for the transaction to be confirmed for N blocks - In forge logic, first prepare a batch and then wait for an available server proof to have all work ready once the proof server is ready. - Remove the `isForgeSequence` method which was querying the smart contract, and instead use notifications sent by the Synchronizer to figure out if it's forging time. - Update test (which is a minimal test to manually see if the coordinator starts) - HistoryDB: - Add method to get the number of batches in a slot (used to detect when a slot has passed the bid winner forging deadline) - Add method to get the best bid and associated coordinator of a slot (used to detect the forgerAddress that can forge the slot) - General: - Rename some instances of `currentBlock` to `lastBlock` to be more clear. - Node: - Connect the API to the node and call the methods to update cached state when the sync advances blocks. - Call methods to update Coordinator state when the sync advances blocks and finds reorgs. - Synchronizer: - Add Auction field in the Stats, which contain the current slot with info about highest bidder and other related info required to know who can forge in the current block. - Better organization of cached state: - On Sync, update the internal cached state - On Init or Reorg, load the state from HistoryDB into the internal cached state.
4 years ago
Redo coordinator structure, connect API to node - API: - Modify the constructor so that hardcoded rollup constants don't need to be passed (introduce a `Config` and use `configAPI` internally) - Common: - Update rollup constants with proper *big.Int when required - Add BidCoordinator and Slot structs used by the HistoryDB and Synchronizer. - Add helper methods to AuctionConstants - AuctionVariables: Add column `DefaultSlotSetBidSlotNum` (in the SQL table: `default_slot_set_bid_slot_num`), which indicates at which slotNum does the `DefaultSlotSetBid` specified starts applying. - Config: - Move coordinator exclusive configuration from the node config to the coordinator config - Coordinator: - Reorganize the code towards having the goroutines started and stopped from the coordinator itself instead of the node. - Remove all stop and stopped channels, and use context.Context and sync.WaitGroup instead. - Remove BatchInfo setters and assing variables directly - In ServerProof and ServerProofPool use context instead stop channel. - Use message passing to notify the coordinator about sync updates and reorgs - Introduce the Pipeline, which can be started and stopped by the Coordinator - Introduce the TxManager, which manages ethereum transactions (the TxManager is also in charge of making the forge call to the rollup smart contract). The TxManager keeps ethereum transactions and: 1. Waits for the transaction to be accepted 2. Waits for the transaction to be confirmed for N blocks - In forge logic, first prepare a batch and then wait for an available server proof to have all work ready once the proof server is ready. - Remove the `isForgeSequence` method which was querying the smart contract, and instead use notifications sent by the Synchronizer to figure out if it's forging time. - Update test (which is a minimal test to manually see if the coordinator starts) - HistoryDB: - Add method to get the number of batches in a slot (used to detect when a slot has passed the bid winner forging deadline) - Add method to get the best bid and associated coordinator of a slot (used to detect the forgerAddress that can forge the slot) - General: - Rename some instances of `currentBlock` to `lastBlock` to be more clear. - Node: - Connect the API to the node and call the methods to update cached state when the sync advances blocks. - Call methods to update Coordinator state when the sync advances blocks and finds reorgs. - Synchronizer: - Add Auction field in the Stats, which contain the current slot with info about highest bidder and other related info required to know who can forge in the current block. - Better organization of cached state: - On Sync, update the internal cached state - On Init or Reorg, load the state from HistoryDB into the internal cached state.
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/hermeznetwork/tracerr"
  12. "github.com/iden3/go-iden3-crypto/babyjub"
  13. )
  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, tracerr.Wrap(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, tracerr.Wrap(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, tracerr.Wrap(err)
  44. }
  45. return fromItem, order, limit, nil
  46. }
  47. // nolint reason: res may be not overwritten
  48. func parseQueryUint(name string, dflt *uint, min, max uint, c querier) (*uint, error) { //nolint:SA4009
  49. str := c.Query(name)
  50. return stringToUint(str, name, dflt, min, max)
  51. }
  52. // nolint reason: res may be not overwritten
  53. func parseQueryInt64(name string, dflt *int64, min, max int64, c querier) (*int64, error) { //nolint:SA4009
  54. str := c.Query(name)
  55. return stringToInt64(str, name, dflt, min, max)
  56. }
  57. // nolint reason: res may be not overwritten
  58. func parseQueryBool(name string, dflt *bool, c querier) (*bool, error) { //nolint:SA4009
  59. str := c.Query(name)
  60. if str == "" {
  61. return dflt, nil
  62. }
  63. if str == "true" {
  64. res := new(bool)
  65. *res = true
  66. return res, nil
  67. }
  68. if str == "false" {
  69. res := new(bool)
  70. *res = false
  71. return res, nil
  72. }
  73. return nil, tracerr.Wrap(fmt.Errorf("Invalid %s. Must be eithe true or false", name))
  74. }
  75. func parseQueryHezEthAddr(c querier) (*ethCommon.Address, error) {
  76. const name = "hezEthereumAddress"
  77. addrStr := c.Query(name)
  78. return hezStringToEthAddr(addrStr, name)
  79. }
  80. func parseQueryBJJ(c querier) (*babyjub.PublicKeyComp, error) {
  81. const name = "BJJ"
  82. bjjStr := c.Query(name)
  83. if bjjStr == "" {
  84. return nil, nil
  85. }
  86. return hezStringToBJJ(bjjStr, name)
  87. }
  88. func parseQueryPoolL2TxState(c querier) (*common.PoolL2TxState, error) {
  89. const name = "state"
  90. stateStr := c.Query(name)
  91. if stateStr == "" {
  92. return nil, nil
  93. }
  94. switch common.PoolL2TxState(stateStr) {
  95. case common.PoolL2TxStatePending:
  96. ret := common.PoolL2TxStatePending
  97. return &ret, nil
  98. case common.PoolL2TxStateForged:
  99. ret := common.PoolL2TxStateForged
  100. return &ret, nil
  101. case common.PoolL2TxStateForging:
  102. ret := common.PoolL2TxStateForging
  103. return &ret, nil
  104. case common.PoolL2TxStateInvalid:
  105. ret := common.PoolL2TxStateInvalid
  106. return &ret, nil
  107. }
  108. return nil, tracerr.Wrap(fmt.Errorf(
  109. "invalid %s, %s is not a valid option. Check the valid options in the docmentation",
  110. name, stateStr,
  111. ))
  112. }
  113. func parseQueryTxType(c querier) (*common.TxType, error) {
  114. const name = "type"
  115. typeStr := c.Query(name)
  116. if typeStr == "" {
  117. return nil, nil
  118. }
  119. switch common.TxType(typeStr) {
  120. case common.TxTypeExit:
  121. ret := common.TxTypeExit
  122. return &ret, nil
  123. case common.TxTypeTransfer:
  124. ret := common.TxTypeTransfer
  125. return &ret, nil
  126. case common.TxTypeDeposit:
  127. ret := common.TxTypeDeposit
  128. return &ret, nil
  129. case common.TxTypeCreateAccountDeposit:
  130. ret := common.TxTypeCreateAccountDeposit
  131. return &ret, nil
  132. case common.TxTypeCreateAccountDepositTransfer:
  133. ret := common.TxTypeCreateAccountDepositTransfer
  134. return &ret, nil
  135. case common.TxTypeDepositTransfer:
  136. ret := common.TxTypeDepositTransfer
  137. return &ret, nil
  138. case common.TxTypeForceTransfer:
  139. ret := common.TxTypeForceTransfer
  140. return &ret, nil
  141. case common.TxTypeForceExit:
  142. ret := common.TxTypeForceExit
  143. return &ret, nil
  144. case common.TxTypeTransferToEthAddr:
  145. ret := common.TxTypeTransferToEthAddr
  146. return &ret, nil
  147. case common.TxTypeTransferToBJJ:
  148. ret := common.TxTypeTransferToBJJ
  149. return &ret, nil
  150. }
  151. return nil, tracerr.Wrap(fmt.Errorf(
  152. "invalid %s, %s is not a valid option. Check the valid options in the docmentation",
  153. name, typeStr,
  154. ))
  155. }
  156. func parseIdx(c querier) (*common.Idx, error) {
  157. const name = "accountIndex"
  158. idxStr := c.Query(name)
  159. return stringToIdx(idxStr, name)
  160. }
  161. func parseFromIdx(c querier) (*common.Idx, error) {
  162. const name = "fromAccountIndex"
  163. idxStr := c.Query(name)
  164. return stringToIdx(idxStr, name)
  165. }
  166. func parseToIdx(c querier) (*common.Idx, error) {
  167. const name = "toAccountIndex"
  168. idxStr := c.Query(name)
  169. return stringToIdx(idxStr, name)
  170. }
  171. func parseExitFilters(c querier) (*common.TokenID, *ethCommon.Address, *babyjub.PublicKeyComp, *common.Idx, error) {
  172. // TokenID
  173. tid, err := parseQueryUint("tokenId", nil, 0, maxUint32, c)
  174. if err != nil {
  175. return nil, nil, nil, nil, tracerr.Wrap(err)
  176. }
  177. var tokenID *common.TokenID
  178. if tid != nil {
  179. tokenID = new(common.TokenID)
  180. *tokenID = common.TokenID(*tid)
  181. }
  182. // Hez Eth addr
  183. addr, err := parseQueryHezEthAddr(c)
  184. if err != nil {
  185. return nil, nil, nil, nil, tracerr.Wrap(err)
  186. }
  187. // BJJ
  188. bjj, err := parseQueryBJJ(c)
  189. if err != nil {
  190. return nil, nil, nil, nil, tracerr.Wrap(err)
  191. }
  192. if addr != nil && bjj != nil {
  193. return nil, nil, nil, nil, tracerr.Wrap(errors.New("bjj and hezEthereumAddress params are incompatible"))
  194. }
  195. // Idx
  196. idx, err := parseIdx(c)
  197. if err != nil {
  198. return nil, nil, nil, nil, tracerr.Wrap(err)
  199. }
  200. if idx != nil && (addr != nil || bjj != nil || tokenID != nil) {
  201. return nil, nil, nil, nil, tracerr.Wrap(errors.New("accountIndex is incompatible with BJJ, hezEthereumAddress and tokenId"))
  202. }
  203. return tokenID, addr, bjj, idx, nil
  204. }
  205. func parseTxsHistoryFilters(c querier) (*common.TokenID, *ethCommon.Address,
  206. *babyjub.PublicKeyComp, *common.Idx, *common.Idx, error) {
  207. // TokenID
  208. tid, err := parseQueryUint("tokenId", nil, 0, maxUint32, c)
  209. if err != nil {
  210. return nil, nil, nil, nil, nil, tracerr.Wrap(err)
  211. }
  212. var tokenID *common.TokenID
  213. if tid != nil {
  214. tokenID = new(common.TokenID)
  215. *tokenID = common.TokenID(*tid)
  216. }
  217. // Hez Eth addr
  218. addr, err := parseQueryHezEthAddr(c)
  219. if err != nil {
  220. return nil, nil, nil, nil, nil, tracerr.Wrap(err)
  221. }
  222. // BJJ
  223. bjj, err := parseQueryBJJ(c)
  224. if err != nil {
  225. return nil, nil, nil, nil, nil, tracerr.Wrap(err)
  226. }
  227. if addr != nil && bjj != nil {
  228. return nil, nil, nil, nil, nil, tracerr.Wrap(errors.New("bjj and hezEthereumAddress params are incompatible"))
  229. }
  230. // from Idx
  231. fromIdx, err := parseFromIdx(c)
  232. if err != nil {
  233. return nil, nil, nil, nil, nil, tracerr.Wrap(err)
  234. }
  235. // to Idx
  236. toIdx, err := parseToIdx(c)
  237. if err != nil {
  238. return nil, nil, nil, nil, nil, tracerr.Wrap(err)
  239. }
  240. if (fromIdx != nil || toIdx != nil) && (addr != nil || bjj != nil || tokenID != nil) {
  241. return nil, nil, nil, nil, nil, tracerr.Wrap(errors.New("accountIndex is incompatible with BJJ, hezEthereumAddress and tokenId"))
  242. }
  243. return tokenID, addr, bjj, fromIdx, toIdx, nil
  244. }
  245. func parseTokenFilters(c querier) ([]common.TokenID, []string, string, error) {
  246. idsStr := c.Query("ids")
  247. symbolsStr := c.Query("symbols")
  248. nameStr := c.Query("name")
  249. var tokensIDs []common.TokenID
  250. if idsStr != "" {
  251. ids := strings.Split(idsStr, ",")
  252. for _, id := range ids {
  253. idUint, err := strconv.Atoi(id)
  254. if err != nil {
  255. return nil, nil, "", tracerr.Wrap(err)
  256. }
  257. tokenID := common.TokenID(idUint)
  258. tokensIDs = append(tokensIDs, tokenID)
  259. }
  260. }
  261. var symbols []string
  262. if symbolsStr != "" {
  263. symbols = strings.Split(symbolsStr, ",")
  264. }
  265. return tokensIDs, symbols, nameStr, nil
  266. }
  267. func parseBidFilters(c querier) (*int64, *ethCommon.Address, error) {
  268. slotNum, err := parseQueryInt64("slotNum", nil, 0, maxInt64, c)
  269. if err != nil {
  270. return nil, nil, tracerr.Wrap(err)
  271. }
  272. bidderAddr, err := parseQueryEthAddr("bidderAddr", c)
  273. if err != nil {
  274. return nil, nil, tracerr.Wrap(err)
  275. }
  276. return slotNum, bidderAddr, nil
  277. }
  278. func parseSlotFilters(c querier) (*int64, *int64, *ethCommon.Address, *bool, error) {
  279. minSlotNum, err := parseQueryInt64("minSlotNum", nil, 0, maxInt64, c)
  280. if err != nil {
  281. return nil, nil, nil, nil, tracerr.Wrap(err)
  282. }
  283. maxSlotNum, err := parseQueryInt64("maxSlotNum", nil, 0, maxInt64, c)
  284. if err != nil {
  285. return nil, nil, nil, nil, tracerr.Wrap(err)
  286. }
  287. wonByEthereumAddress, err := parseQueryEthAddr("wonByEthereumAddress", c)
  288. if err != nil {
  289. return nil, nil, nil, nil, tracerr.Wrap(err)
  290. }
  291. finishedAuction, err := parseQueryBool("finishedAuction", nil, c)
  292. if err != nil {
  293. return nil, nil, nil, nil, tracerr.Wrap(err)
  294. }
  295. return minSlotNum, maxSlotNum, wonByEthereumAddress, finishedAuction, nil
  296. }
  297. func parseAccountFilters(c querier) ([]common.TokenID, *ethCommon.Address, *babyjub.PublicKeyComp, error) {
  298. // TokenID
  299. idsStr := c.Query("tokenIds")
  300. var tokenIDs []common.TokenID
  301. if idsStr != "" {
  302. ids := strings.Split(idsStr, ",")
  303. for _, id := range ids {
  304. idUint, err := strconv.Atoi(id)
  305. if err != nil {
  306. return nil, nil, nil, tracerr.Wrap(err)
  307. }
  308. tokenID := common.TokenID(idUint)
  309. tokenIDs = append(tokenIDs, tokenID)
  310. }
  311. }
  312. // Hez Eth addr
  313. addr, err := parseQueryHezEthAddr(c)
  314. if err != nil {
  315. return nil, nil, nil, tracerr.Wrap(err)
  316. }
  317. // BJJ
  318. bjj, err := parseQueryBJJ(c)
  319. if err != nil {
  320. return nil, nil, nil, tracerr.Wrap(err)
  321. }
  322. if addr != nil && bjj != nil {
  323. return nil, nil, nil, tracerr.Wrap(errors.New("bjj and hezEthereumAddress params are incompatible"))
  324. }
  325. return tokenIDs, addr, bjj, nil
  326. }
  327. // Param parsers
  328. type paramer interface {
  329. Param(string) string
  330. }
  331. func parseParamTxID(c paramer) (common.TxID, error) {
  332. const name = "id"
  333. txIDStr := c.Param(name)
  334. if txIDStr == "" {
  335. return common.TxID{}, tracerr.Wrap(fmt.Errorf("%s is required", name))
  336. }
  337. txID, err := common.NewTxIDFromString(txIDStr)
  338. if err != nil {
  339. return common.TxID{}, tracerr.Wrap(fmt.Errorf("invalid %s", name))
  340. }
  341. return txID, nil
  342. }
  343. func parseParamIdx(c paramer) (*common.Idx, error) {
  344. const name = "accountIndex"
  345. idxStr := c.Param(name)
  346. return stringToIdx(idxStr, name)
  347. }
  348. // nolint reason: res may be not overwritten
  349. func parseParamUint(name string, dflt *uint, min, max uint, c paramer) (*uint, error) { //nolint:SA4009
  350. str := c.Param(name)
  351. return stringToUint(str, name, dflt, min, max)
  352. }
  353. // nolint reason: res may be not overwritten
  354. func parseParamInt64(name string, dflt *int64, min, max int64, c paramer) (*int64, error) { //nolint:SA4009
  355. str := c.Param(name)
  356. return stringToInt64(str, name, dflt, min, max)
  357. }
  358. func stringToIdx(idxStr, name string) (*common.Idx, error) {
  359. if idxStr == "" {
  360. return nil, nil
  361. }
  362. splitted := strings.Split(idxStr, ":")
  363. const expectedLen = 3
  364. if len(splitted) != expectedLen || splitted[0] != "hez" {
  365. return nil, tracerr.Wrap(fmt.Errorf(
  366. "invalid %s, must follow this: hez:<tokenSymbol>:index", name))
  367. }
  368. // TODO: check that the tokenSymbol match the token related to the account index
  369. idxInt, err := strconv.Atoi(splitted[2])
  370. idx := common.Idx(idxInt)
  371. return &idx, tracerr.Wrap(err)
  372. }
  373. func stringToUint(uintStr, name string, dflt *uint, min, max uint) (*uint, error) {
  374. if uintStr != "" {
  375. resInt, err := strconv.Atoi(uintStr)
  376. if err != nil || resInt < 0 || resInt < int(min) || resInt > int(max) {
  377. return nil, tracerr.Wrap(fmt.Errorf(
  378. "Invalid %s. Must be an integer within the range [%d, %d]",
  379. name, min, max))
  380. }
  381. res := uint(resInt)
  382. return &res, nil
  383. }
  384. return dflt, nil
  385. }
  386. func stringToInt64(uintStr, name string, dflt *int64, min, max int64) (*int64, error) {
  387. if uintStr != "" {
  388. resInt, err := strconv.Atoi(uintStr)
  389. if err != nil || resInt < 0 || resInt < int(min) || resInt > int(max) {
  390. return nil, tracerr.Wrap(fmt.Errorf(
  391. "Invalid %s. Must be an integer within the range [%d, %d]",
  392. name, min, max))
  393. }
  394. res := int64(resInt)
  395. return &res, nil
  396. }
  397. return dflt, nil
  398. }
  399. func hezStringToEthAddr(addrStr, name string) (*ethCommon.Address, error) {
  400. if addrStr == "" {
  401. return nil, nil
  402. }
  403. splitted := strings.Split(addrStr, "hez:")
  404. if len(splitted) != 2 || len(splitted[1]) != 42 {
  405. return nil, tracerr.Wrap(fmt.Errorf(
  406. "Invalid %s, must follow this regex: ^hez:0x[a-fA-F0-9]{40}$", name))
  407. }
  408. var addr ethCommon.Address
  409. err := addr.UnmarshalText([]byte(splitted[1]))
  410. return &addr, tracerr.Wrap(err)
  411. }
  412. func hezStringToBJJ(bjjStr, name string) (*babyjub.PublicKeyComp, error) {
  413. const decodedLen = 33
  414. splitted := strings.Split(bjjStr, "hez:")
  415. if len(splitted) != 2 || len(splitted[1]) != 44 {
  416. return nil, tracerr.Wrap(fmt.Errorf(
  417. "Invalid %s, must follow this regex: ^hez:[A-Za-z0-9+/=]{44}$",
  418. name))
  419. }
  420. decoded, err := base64.RawURLEncoding.DecodeString(splitted[1])
  421. if err != nil {
  422. return nil, tracerr.Wrap(fmt.Errorf(
  423. "Invalid %s, error decoding base64 string: %s",
  424. name, err.Error()))
  425. }
  426. if len(decoded) != decodedLen {
  427. return nil, tracerr.Wrap(fmt.Errorf(
  428. "invalid %s, error decoding base64 string: unexpected byte array length",
  429. name))
  430. }
  431. bjjBytes := [decodedLen - 1]byte{}
  432. copy(bjjBytes[:decodedLen-1], decoded[:decodedLen-1])
  433. sum := bjjBytes[0]
  434. for i := 1; i < len(bjjBytes); i++ {
  435. sum += bjjBytes[i]
  436. }
  437. if decoded[decodedLen-1] != sum {
  438. return nil, tracerr.Wrap(fmt.Errorf("invalid %s, checksum failed",
  439. name))
  440. }
  441. bjjComp := babyjub.PublicKeyComp(bjjBytes)
  442. return &bjjComp, nil
  443. }
  444. func parseQueryEthAddr(name string, c querier) (*ethCommon.Address, error) {
  445. addrStr := c.Query(name)
  446. if addrStr == "" {
  447. return nil, nil
  448. }
  449. return parseEthAddr(addrStr)
  450. }
  451. func parseEthAddr(ethAddrStr string) (*ethCommon.Address, error) {
  452. var addr ethCommon.Address
  453. err := addr.UnmarshalText([]byte(ethAddrStr))
  454. return &addr, tracerr.Wrap(err)
  455. }
  456. func parseParamHezEthAddr(c paramer) (*ethCommon.Address, error) {
  457. const name = "hezEthereumAddress"
  458. addrStr := c.Param(name)
  459. return hezStringToEthAddr(addrStr, name)
  460. }
  461. type errorMsg struct {
  462. Message string
  463. }
  464. func bjjToString(bjj babyjub.PublicKeyComp) string {
  465. pkComp := [32]byte(bjj)
  466. sum := pkComp[0]
  467. for i := 1; i < len(pkComp); i++ {
  468. sum += pkComp[i]
  469. }
  470. bjjSum := append(pkComp[:], sum)
  471. return "hez:" + base64.RawURLEncoding.EncodeToString(bjjSum)
  472. }
  473. func ethAddrToHez(addr ethCommon.Address) string {
  474. return "hez:" + addr.String()
  475. }
  476. func idxToHez(idx common.Idx, tokenSymbol string) string {
  477. return "hez:" + tokenSymbol + ":" + strconv.Itoa(int(idx))
  478. }