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