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/iden3/go-iden3-crypto/babyjub"
 13)
 14
 15type querier interface {
 16	Query(string) string
 17}
 18
 19func parsePagination(c querier) (*uint, *bool, *uint, error) {
 20	// Offset
 21	offset := new(uint)
 22	*offset = 0
 23	offset, err := parseQueryUint("offset", offset, 0, maxUint32, c)
 24	if err != nil {
 25		return nil, nil, nil, err
 26	}
 27	// Last
 28	last := new(bool)
 29	*last = dfltLast
 30	last, err = parseQueryBool("last", last, c)
 31	if err != nil {
 32		return nil, nil, nil, err
 33	}
 34	if *last && (offset != nil && *offset > 0) {
 35		return nil, nil, nil, errors.New(
 36			"last and offset are incompatible, provide only one of them",
 37		)
 38	}
 39	// Limit
 40	limit := new(uint)
 41	*limit = dfltLimit
 42	limit, err = parseQueryUint("limit", limit, 1, maxLimit, c)
 43	if err != nil {
 44		return nil, nil, nil, err
 45	}
 46	return offset, last, limit, nil
 47}
 48
 49func parseQueryUint(name string, dflt *uint, min, max uint, c querier) (*uint, error) { //nolint:SA4009 res may be not overwriten
 50	str := c.Query(name)
 51	if str != "" {
 52		resInt, err := strconv.Atoi(str)
 53		if err != nil || resInt < 0 || resInt < int(min) || resInt > int(max) {
 54			return nil, fmt.Errorf(
 55				"Inavlid %s. Must be an integer within the range [%d, %d]",
 56				name, min, max)
 57		}
 58		res := uint(resInt)
 59		return &res, nil
 60	}
 61	return dflt, nil
 62}
 63
 64func parseQueryBool(name string, dflt *bool, c querier) (*bool, error) { //nolint:SA4009 res may be not overwriten
 65	str := c.Query(name)
 66	if str == "" {
 67		return dflt, nil
 68	}
 69	if str == "true" {
 70		res := new(bool)
 71		*res = true
 72		return res, nil
 73	}
 74	if str == "false" {
 75		res := new(bool)
 76		*res = false
 77		return res, nil
 78	}
 79	return nil, fmt.Errorf("Inavlid %s. Must be eithe true or false", name)
 80}
 81
 82func parseQueryHezEthAddr(c querier) (*ethCommon.Address, error) {
 83	const name = "hermezEthereumAddress"
 84	addrStr := c.Query(name)
 85	if addrStr == "" {
 86		return nil, nil
 87	}
 88	splitted := strings.Split(addrStr, "hez:")
 89	if len(splitted) != 2 || len(splitted[1]) != 42 {
 90		return nil, fmt.Errorf(
 91			"Invalid %s, must follow this regex: ^hez:0x[a-fA-F0-9]{40}$", name)
 92	}
 93	var addr ethCommon.Address
 94	err := addr.UnmarshalText([]byte(splitted[1]))
 95	return &addr, err
 96}
 97
 98func parseQueryBJJ(c querier) (*babyjub.PublicKey, error) {
 99	const name = "BJJ"
100	const decodedLen = 33
101	bjjStr := c.Query(name)
102	if bjjStr == "" {
103		return nil, nil
104	}
105	splitted := strings.Split(bjjStr, "hez:")
106	if len(splitted) != 2 || len(splitted[1]) != 44 {
107		return nil, fmt.Errorf(
108			"Invalid %s, must follow this regex: ^hez:[A-Za-z0-9+/=]{44}$",
109			name)
110	}
111	decoded, err := base64.RawURLEncoding.DecodeString(splitted[1])
112	if err != nil {
113		return nil, fmt.Errorf(
114			"Invalid %s, error decoding base64 string: %s",
115			name, err.Error())
116	}
117	if len(decoded) != decodedLen {
118		return nil, fmt.Errorf(
119			"invalid %s, error decoding base64 string: unexpected byte array length",
120			name)
121	}
122	bjjBytes := [decodedLen - 1]byte{}
123	copy(bjjBytes[:decodedLen-1], decoded[:decodedLen-1])
124	sum := bjjBytes[0]
125	for i := 1; i < len(bjjBytes); i++ {
126		sum += bjjBytes[i]
127	}
128	if decoded[decodedLen-1] != sum {
129		return nil, fmt.Errorf("invalid %s, checksum failed",
130			name)
131	}
132	bjjComp := babyjub.PublicKeyComp(bjjBytes)
133	bjj, err := bjjComp.Decompress()
134	if err != nil {
135		return nil, fmt.Errorf(
136			"invalid %s, error decompressing public key: %s",
137			name, err.Error())
138	}
139	return bjj, nil
140}
141
142func parseQueryTxType(c querier) (*common.TxType, error) {
143	const name = "type"
144	typeStr := c.Query(name)
145	if typeStr == "" {
146		return nil, nil
147	}
148	switch common.TxType(typeStr) {
149	case common.TxTypeExit:
150		ret := common.TxTypeExit
151		return &ret, nil
152	case common.TxTypeTransfer:
153		ret := common.TxTypeTransfer
154		return &ret, nil
155	case common.TxTypeDeposit:
156		ret := common.TxTypeDeposit
157		return &ret, nil
158	case common.TxTypeCreateAccountDeposit:
159		ret := common.TxTypeCreateAccountDeposit
160		return &ret, nil
161	case common.TxTypeCreateAccountDepositTransfer:
162		ret := common.TxTypeCreateAccountDepositTransfer
163		return &ret, nil
164	case common.TxTypeDepositTransfer:
165		ret := common.TxTypeDepositTransfer
166		return &ret, nil
167	case common.TxTypeForceTransfer:
168		ret := common.TxTypeForceTransfer
169		return &ret, nil
170	case common.TxTypeForceExit:
171		ret := common.TxTypeForceExit
172		return &ret, nil
173	case common.TxTypeTransferToEthAddr:
174		ret := common.TxTypeTransferToEthAddr
175		return &ret, nil
176	case common.TxTypeTransferToBJJ:
177		ret := common.TxTypeTransferToBJJ
178		return &ret, nil
179	}
180	return nil, fmt.Errorf(
181		"invalid %s, %s is not a valid option. Check the valid options in the docmentation",
182		name, typeStr,
183	)
184}
185
186func parseIdx(c querier) (*uint, error) {
187	const name = "accountIndex"
188	addrStr := c.Query(name)
189	if addrStr == "" {
190		return nil, nil
191	}
192	splitted := strings.Split(addrStr, ":")
193	const expectedLen = 3
194	if len(splitted) != expectedLen {
195		return nil, fmt.Errorf(
196			"invalid %s, must follow this: hez:<tokenSymbol>:index", name)
197	}
198	idxInt, err := strconv.Atoi(splitted[2])
199	idx := uint(idxInt)
200	return &idx, err
201}