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 srcBytes, ok := src.([]byte)
37 if !ok {
38 return fmt.Errorf("can't scan %T into apitypes.BigIntStr", src)
39 }
40 // bytes to *big.Int
41 bigInt := new(big.Int).SetBytes(srcBytes)
42 // *big.Int to BigIntStr
43 bigIntStr := NewBigIntStr(bigInt)
44 if bigIntStr == nil {
45 return nil
46 }
47 *b = *bigIntStr
48 return nil
49}
50
51// Value implements valuer for database/sql
52func (b BigIntStr) Value() (driver.Value, error) {
53 // string to *big.Int
54 bigInt, ok := new(big.Int).SetString(string(b), 10)
55 if !ok || bigInt == nil {
56 return nil, errors.New("invalid representation of a *big.Int")
57 }
58 // *big.Int to bytes
59 return bigInt.Bytes(), nil
60}
61
62// StrBigInt is used to unmarshal BigIntStr directly into an alias of big.Int
63type StrBigInt big.Int
64
65// UnmarshalText unmarshals a StrBigInt
66func (s *StrBigInt) UnmarshalText(text []byte) error {
67 bi, ok := (*big.Int)(s).SetString(string(text), 10)
68 if !ok {
69 return fmt.Errorf("could not unmarshal %s into a StrBigInt", text)
70 }
71 *s = StrBigInt(*bi)
72 return nil
73}
74
75// CollectedFees is used to retrieve common.batch.CollectedFee from the DB
76type CollectedFees map[common.TokenID]BigIntStr
77
78// UnmarshalJSON unmarshals a json representation of map[common.TokenID]*big.Int
79func (c *CollectedFees) UnmarshalJSON(text []byte) error {
80 bigIntMap := make(map[common.TokenID]*big.Int)
81 if err := json.Unmarshal(text, &bigIntMap); err != nil {
82 return err
83 }
84 *c = CollectedFees(make(map[common.TokenID]BigIntStr))
85 for k, v := range bigIntMap {
86 bStr := NewBigIntStr(v)
87 (CollectedFees(*c)[k]) = *bStr
88 }
89 // *c = CollectedFees(bStrMap)
90 return nil
91}
92
93// 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.
94// It assumes that Ethereum Address are inserted/fetched to/from the DB using the default Scan/Value interface
95type HezEthAddr string
96
97// NewHezEthAddr creates a HezEthAddr from an Ethereum addr
98func NewHezEthAddr(addr ethCommon.Address) HezEthAddr {
99 return HezEthAddr("hez:" + addr.String())
100}
101
102// ToEthAddr returns an Ethereum Address created from HezEthAddr
103func (a HezEthAddr) ToEthAddr() (ethCommon.Address, error) {
104 addrStr := strings.TrimPrefix(string(a), "hez:")
105 var addr ethCommon.Address
106 return addr, addr.UnmarshalText([]byte(addrStr))
107}
108
109// Scan implements Scanner for database/sql
110func (a *HezEthAddr) Scan(src interface{}) error {
111 ethAddr := ðCommon.Address{}
112 if err := ethAddr.Scan(src); err != nil {
113 return err
114 }
115 if ethAddr == nil {
116 return nil
117 }
118 *a = NewHezEthAddr(*ethAddr)
119 return nil
120}
121
122// Value implements valuer for database/sql
123func (a HezEthAddr) Value() (driver.Value, error) {
124 ethAddr, err := a.ToEthAddr()
125 if err != nil {
126 return nil, err
127 }
128 return ethAddr.Value()
129}
130
131// StrHezEthAddr is used to unmarshal HezEthAddr directly into an alias of ethCommon.Address
132type StrHezEthAddr ethCommon.Address
133
134// UnmarshalText unmarshals a StrHezEthAddr
135func (s *StrHezEthAddr) UnmarshalText(text []byte) error {
136 withoutHez := strings.TrimPrefix(string(text), "hez:")
137 var addr ethCommon.Address
138 if err := addr.UnmarshalText([]byte(withoutHez)); err != nil {
139 return err
140 }
141 *s = StrHezEthAddr(addr)
142 return nil
143}
144
145// 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.
146// It assumes that *babyjub.PublicKey are inserted/fetched to/from the DB using the default Scan/Value interface
147type HezBJJ string
148
149// NewHezBJJ creates a HezBJJ from a *babyjub.PublicKey.
150// Calling this method with a nil bjj causes panic
151func NewHezBJJ(bjj *babyjub.PublicKey) HezBJJ {
152 pkComp := [32]byte(bjj.Compress())
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.PublicKey, 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 nil, formatErr
168 }
169 decoded, err := base64.RawURLEncoding.DecodeString(encoded)
170 if err != nil {
171 return nil, formatErr
172 }
173 if len(decoded) != decodedLen {
174 return nil, 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 nil, errors.New("checksum verification failed")
184 }
185 bjjComp := babyjub.PublicKeyComp(bjjBytes)
186 return bjjComp.Decompress()
187}
188
189// ToBJJ returns a *babyjub.PublicKey created from HezBJJ
190func (b HezBJJ) ToBJJ() (*babyjub.PublicKey, 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.PublicKey{}
197 if err := bjj.Scan(src); err != nil {
198 return 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, err
212 }
213 return bjj.Value()
214}
215
216// StrHezBJJ is used to unmarshal HezBJJ directly into an alias of babyjub.PublicKey
217type StrHezBJJ babyjub.PublicKey
218
219// UnmarshalText unmarshals a StrHezBJJ
220func (s *StrHezBJJ) UnmarshalText(text []byte) error {
221 bjj, err := hezStrToBJJ(string(text))
222 if err != nil {
223 return 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 fotmat (hez:tokenSymbol:idx) to sql DBs.
230// 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
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 fmt.Errorf("can not unmarshal %s into StrHezIdx", text)
243 }
244 idxInt, err := strconv.Atoi(splitted[1])
245 if err != nil {
246 return 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 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 err
293 }
294 *e = EthSignature([]byte(signature))
295 return nil
296}