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