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.TxTypeWithdrawn:
153 ret := common.TxTypeWithdrawn
154 return &ret, nil
155 case common.TxTypeTransfer:
156 ret := common.TxTypeTransfer
157 return &ret, nil
158 case common.TxTypeDeposit:
159 ret := common.TxTypeDeposit
160 return &ret, nil
161 case common.TxTypeCreateAccountDeposit:
162 ret := common.TxTypeCreateAccountDeposit
163 return &ret, nil
164 case common.TxTypeCreateAccountDepositTransfer:
165 ret := common.TxTypeCreateAccountDepositTransfer
166 return &ret, nil
167 case common.TxTypeDepositTransfer:
168 ret := common.TxTypeDepositTransfer
169 return &ret, nil
170 case common.TxTypeForceTransfer:
171 ret := common.TxTypeForceTransfer
172 return &ret, nil
173 case common.TxTypeForceExit:
174 ret := common.TxTypeForceExit
175 return &ret, nil
176 case common.TxTypeTransferToEthAddr:
177 ret := common.TxTypeTransferToEthAddr
178 return &ret, nil
179 case common.TxTypeTransferToBJJ:
180 ret := common.TxTypeTransferToBJJ
181 return &ret, nil
182 }
183 return nil, fmt.Errorf(
184 "invalid %s, %s is not a valid option. Check the valid options in the docmentation",
185 name, typeStr,
186 )
187}
188
189func parseIdx(c querier) (*uint, error) {
190 const name = "accountIndex"
191 addrStr := c.Query(name)
192 if addrStr == "" {
193 return nil, nil
194 }
195 splitted := strings.Split(addrStr, ":")
196 const expectedLen = 3
197 if len(splitted) != expectedLen {
198 return nil, fmt.Errorf(
199 "invalid %s, must follow this: hez:<tokenSymbol>:index", name)
200 }
201 idxInt, err := strconv.Atoi(splitted[2])
202 idx := uint(idxInt)
203 return &idx, err
204}