hermez-node/apitypes/apitypes.go
apitypes/apitypes.goRaw
1package apitypes
2
3import (
4 "database/sql/driver"
5 "encoding/base64"
6 "encoding/hex"
7 "encoding/json"
8 "errors"
9 "fmt"
10 "math/big"
11 "strconv"
12 "strings"
13
14 ethCommon "github.com/ethereum/go-ethereum/common"
15 "github.com/hermeznetwork/hermez-node/common"
16 "github.com/iden3/go-iden3-crypto/babyjub"
17)
18
19// BigIntStr is used to scan/value *big.Int directly into strings from/to sql DBs.
20// It assumes that *big.Int are inserted/fetched to/from the DB using the BigIntMeddler meddler
21// defined at github.com/hermeznetwork/hermez-node/db
22type BigIntStr string
23
24// NewBigIntStr creates a *BigIntStr from a *big.Int.
25// If the provided bigInt is nil the returned *BigIntStr will also be nil
26func NewBigIntStr(bigInt *big.Int) *BigIntStr {
27 if bigInt == nil {
28 return nil
29 }
30 bigIntStr := BigIntStr(bigInt.String())
31 return &bigIntStr
32}
33
34// Scan implements Scanner for database/sql
35func (b *BigIntStr) Scan(src interface{}) error {
36 // decode base64 src
37 var decoded []byte
38 var err error
39 if srcStr, ok := src.(string); ok {
40 // src is a string
41 decoded, err = base64.StdEncoding.DecodeString(srcStr)
42 } else if srcBytes, ok := src.([]byte); ok {
43 // src is []byte
44 decoded, err = base64.StdEncoding.DecodeString(string(srcBytes))
45 } else {
46 // unexpected src
47 return fmt.Errorf("can't scan %T into apitypes.BigIntStr", src)
48 }
49 if err != nil {
50 return err
51 }
52 // decoded bytes to *big.Int
53 bigInt := &big.Int{}
54 bigInt = bigInt.SetBytes(decoded)
55 // *big.Int to BigIntStr
56 bigIntStr := NewBigIntStr(bigInt)
57 if bigIntStr == nil {
58 return nil
59 }
60 *b = *bigIntStr
61 return nil
62}
63
64// Value implements valuer for database/sql
65func (b BigIntStr) Value() (driver.Value, error) {
66 // string to *big.Int
67 bigInt := &big.Int{}
68 bigInt, ok := bigInt.SetString(string(b), 10)
69 if !ok || bigInt == nil {
70 return nil, errors.New("invalid representation of a *big.Int")
71 }
72 // *big.Int to base64
73 return base64.StdEncoding.EncodeToString(bigInt.Bytes()), nil
74}
75
76// StrBigInt is used to unmarshal BigIntStr directly into an alias of big.Int
77type StrBigInt big.Int
78
79// UnmarshalText unmarshals a StrBigInt
80func (s *StrBigInt) UnmarshalText(text []byte) error {
81 bi, ok := (*big.Int)(s).SetString(string(text), 10)
82 if !ok {
83 return fmt.Errorf("could not unmarshal %s into a StrBigInt", text)
84 }
85 *s = StrBigInt(*bi)
86 return nil
87}
88
89// CollectedFees is used to retrieve common.batch.CollectedFee from the DB
90type CollectedFees map[common.TokenID]BigIntStr
91
92// UnmarshalJSON unmarshals a json representation of map[common.TokenID]*big.Int
93func (c *CollectedFees) UnmarshalJSON(text []byte) error {
94 bigIntMap := make(map[common.TokenID]*big.Int)
95 if err := json.Unmarshal(text, &bigIntMap); err != nil {
96 return err
97 }
98 bStrMap := make(map[common.TokenID]BigIntStr)
99 for k, v := range bigIntMap {
100 bStr := NewBigIntStr(v)
101 bStrMap[k] = *bStr
102 }
103 *c = CollectedFees(bStrMap)
104 return nil
105}
106
107// HezEthAddr is used to scan/value Ethereum Address directly into strings that follow the Ethereum address hez fotmat (^hez:0x[a-fA-F0-9]{40}$) from/to sql DBs.
108// It assumes that Ethereum Address are inserted/fetched to/from the DB using the default Scan/Value interface
109type HezEthAddr string
110
111// NewHezEthAddr creates a HezEthAddr from an Ethereum addr
112func NewHezEthAddr(addr ethCommon.Address) HezEthAddr {
113 return HezEthAddr("hez:" + addr.String())
114}
115
116// ToEthAddr returns an Ethereum Address created from HezEthAddr
117func (a HezEthAddr) ToEthAddr() (ethCommon.Address, error) {
118 addrStr := strings.TrimPrefix(string(a), "hez:")
119 var addr ethCommon.Address
120 return addr, addr.UnmarshalText([]byte(addrStr))
121}
122
123// Scan implements Scanner for database/sql
124func (a *HezEthAddr) Scan(src interface{}) error {
125 ethAddr := ðCommon.Address{}
126 if err := ethAddr.Scan(src); err != nil {
127 return err
128 }
129 if ethAddr == nil {
130 return nil
131 }
132 *a = NewHezEthAddr(*ethAddr)
133 return nil
134}
135
136// Value implements valuer for database/sql
137func (a HezEthAddr) Value() (driver.Value, error) {
138 ethAddr, err := a.ToEthAddr()
139 if err != nil {
140 return nil, err
141 }
142 return ethAddr.Value()
143}
144
145// StrHezEthAddr is used to unmarshal HezEthAddr directly into an alias of ethCommon.Address
146type StrHezEthAddr ethCommon.Address
147
148// UnmarshalText unmarshals a StrHezEthAddr
149func (s *StrHezEthAddr) UnmarshalText(text []byte) error {
150 withoutHez := strings.TrimPrefix(string(text), "hez:")
151 var addr ethCommon.Address
152 if err := addr.UnmarshalText([]byte(withoutHez)); err != nil {
153 return err
154 }
155 *s = StrHezEthAddr(addr)
156 return nil
157}
158
159// StrEthSignature is used to unmarshal EthSignature directly into an alias of []byte
160type StrEthSignature []byte
161
162// UnmarshalText unmarshals a StrEthSignature
163func (s *StrEthSignature) UnmarshalText(text []byte) error {
164 without0x := strings.TrimPrefix(string(text), "0x")
165 signature, err := hex.DecodeString(without0x)
166 if err != nil {
167 return err
168 }
169 *s = signature
170 return nil
171}
172
173// HezBJJ is used to scan/value *babyjub.PublicKey directly into strings that follow the BJJ public key hez fotmat (^hez:[A-Za-z0-9_-]{44}$) from/to sql DBs.
174// It assumes that *babyjub.PublicKey are inserted/fetched to/from the DB using the default Scan/Value interface
175type HezBJJ string
176
177// NewHezBJJ creates a HezBJJ from a *babyjub.PublicKey.
178// Calling this method with a nil bjj causes panic
179func NewHezBJJ(bjj *babyjub.PublicKey) HezBJJ {
180 pkComp := [32]byte(bjj.Compress())
181 sum := pkComp[0]
182 for i := 1; i < len(pkComp); i++ {
183 sum += pkComp[i]
184 }
185 bjjSum := append(pkComp[:], sum)
186 return HezBJJ("hez:" + base64.RawURLEncoding.EncodeToString(bjjSum))
187}
188
189func hezStrToBJJ(s string) (*babyjub.PublicKey, error) {
190 const decodedLen = 33
191 const encodedLen = 44
192 formatErr := errors.New("invalid BJJ format. Must follow this regex: ^hez:[A-Za-z0-9_-]{44}$")
193 encoded := strings.TrimPrefix(s, "hez:")
194 if len(encoded) != encodedLen {
195 return nil, formatErr
196 }
197 decoded, err := base64.RawURLEncoding.DecodeString(encoded)
198 if err != nil {
199 return nil, formatErr
200 }
201 if len(decoded) != decodedLen {
202 return nil, formatErr
203 }
204 bjjBytes := [decodedLen - 1]byte{}
205 copy(bjjBytes[:decodedLen-1], decoded[:decodedLen-1])
206 sum := bjjBytes[0]
207 for i := 1; i < len(bjjBytes); i++ {
208 sum += bjjBytes[i]
209 }
210 if decoded[decodedLen-1] != sum {
211 return nil, errors.New("checksum verification failed")
212 }
213 bjjComp := babyjub.PublicKeyComp(bjjBytes)
214 return bjjComp.Decompress()
215}
216
217// ToBJJ returns a *babyjub.PublicKey created from HezBJJ
218func (b HezBJJ) ToBJJ() (*babyjub.PublicKey, error) {
219 return hezStrToBJJ(string(b))
220}
221
222// Scan implements Scanner for database/sql
223func (b *HezBJJ) Scan(src interface{}) error {
224 bjj := &babyjub.PublicKey{}
225 if err := bjj.Scan(src); err != nil {
226 return err
227 }
228 if bjj == nil {
229 return nil
230 }
231 *b = NewHezBJJ(bjj)
232 return nil
233}
234
235// Value implements valuer for database/sql
236func (b HezBJJ) Value() (driver.Value, error) {
237 bjj, err := b.ToBJJ()
238 if err != nil {
239 return nil, err
240 }
241 return bjj.Value()
242}
243
244// StrHezBJJ is used to unmarshal HezBJJ directly into an alias of babyjub.PublicKey
245type StrHezBJJ babyjub.PublicKey
246
247// UnmarshalText unmarshals a StrHezBJJ
248func (s *StrHezBJJ) UnmarshalText(text []byte) error {
249 bjj, err := hezStrToBJJ(string(text))
250 if err != nil {
251 return err
252 }
253 *s = StrHezBJJ(*bjj)
254 return nil
255}
256
257// HezIdx is used to value common.Idx directly into strings that follow the Idx key hez fotmat (hez:tokenSymbol:idx) to sql DBs.
258// Note that this can only be used to insert to DB since there is no way to automaticaly read from the DB since it needs the tokenSymbol
259type HezIdx string
260
261// StrHezIdx is used to unmarshal HezIdx directly into an alias of common.Idx
262type StrHezIdx common.Idx
263
264// UnmarshalText unmarshals a StrHezIdx
265func (s *StrHezIdx) UnmarshalText(text []byte) error {
266 withoutHez := strings.TrimPrefix(string(text), "hez:")
267 splitted := strings.Split(withoutHez, ":")
268 const expectedLen = 2
269 if len(splitted) != expectedLen {
270 return fmt.Errorf("can not unmarshal %s into StrHezIdx", text)
271 }
272 idxInt, err := strconv.Atoi(splitted[1])
273 if err != nil {
274 return err
275 }
276 *s = StrHezIdx(common.Idx(idxInt))
277 return nil
278}
279
280// EthSignature is used to scan/value []byte representing an Ethereum signatue directly into strings from/to sql DBs.
281type EthSignature string
282
283// NewEthSignature creates a *EthSignature from []byte
284// If the provided signature is nil the returned *EthSignature will also be nil
285func NewEthSignature(signature []byte) *EthSignature {
286 if signature == nil {
287 return nil
288 }
289 ethSignature := EthSignature("0x" + hex.EncodeToString(signature))
290 return ðSignature
291}
292
293// Scan implements Scanner for database/sql
294func (e *EthSignature) Scan(src interface{}) error {
295 if srcStr, ok := src.(string); ok {
296 // src is a string
297 *e = *(NewEthSignature([]byte(srcStr)))
298 return nil
299 } else if srcBytes, ok := src.([]byte); ok {
300 // src is []byte
301 *e = *(NewEthSignature(srcBytes))
302 return nil
303 } else {
304 // unexpected src
305 return fmt.Errorf("can't scan %T into apitypes.EthSignature", src)
306 }
307}
308
309// Value implements valuer for database/sql
310func (e EthSignature) Value() (driver.Value, error) {
311 without0x := strings.TrimPrefix(string(e), "0x")
312 return hex.DecodeString(without0x)
313}