hermez-node/apitypes/apitypes.go
apitypes/apitypes.goRaw
1package apitypes
2
3import (
4 "database/sql/driver"
5 "encoding/base64"
6 "encoding/hex"
7 "errors"
8 "fmt"
9 "math/big"
10 "strconv"
11 "strings"
12
13 ethCommon "github.com/ethereum/go-ethereum/common"
14 "github.com/hermeznetwork/hermez-node/common"
15 "github.com/hermeznetwork/tracerr"
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 tracerr.Wrap(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, tracerr.Wrap(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 tracerr.Wrap(fmt.Errorf("could not unmarshal %s into a StrBigInt", text))
70 }
71 *s = StrBigInt(*bi)
72 return nil
73}
74
75// CollectedFeesAPI is send common.batch.CollectedFee through the API
76type CollectedFeesAPI map[common.TokenID]BigIntStr
77
78// NewCollectedFeesAPI creates a new CollectedFeesAPI from a *big.Int map
79func NewCollectedFeesAPI(m map[common.TokenID]*big.Int) CollectedFeesAPI {
80 c := CollectedFeesAPI(make(map[common.TokenID]BigIntStr))
81 for k, v := range m {
82 c[k] = *NewBigIntStr(v)
83 }
84 return c
85}
86
87// 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.
88// It assumes that Ethereum Address are inserted/fetched to/from the DB using the default Scan/Value interface
89type HezEthAddr string
90
91// NewHezEthAddr creates a HezEthAddr from an Ethereum addr
92func NewHezEthAddr(addr ethCommon.Address) HezEthAddr {
93 return HezEthAddr("hez:" + addr.String())
94}
95
96// ToEthAddr returns an Ethereum Address created from HezEthAddr
97func (a HezEthAddr) ToEthAddr() (ethCommon.Address, error) {
98 addrStr := strings.TrimPrefix(string(a), "hez:")
99 var addr ethCommon.Address
100 return addr, addr.UnmarshalText([]byte(addrStr))
101}
102
103// Scan implements Scanner for database/sql
104func (a *HezEthAddr) Scan(src interface{}) error {
105 ethAddr := ðCommon.Address{}
106 if err := ethAddr.Scan(src); err != nil {
107 return tracerr.Wrap(err)
108 }
109 if ethAddr == nil {
110 return nil
111 }
112 *a = NewHezEthAddr(*ethAddr)
113 return nil
114}
115
116// Value implements valuer for database/sql
117func (a HezEthAddr) Value() (driver.Value, error) {
118 ethAddr, err := a.ToEthAddr()
119 if err != nil {
120 return nil, tracerr.Wrap(err)
121 }
122 return ethAddr.Value()
123}
124
125// StrHezEthAddr is used to unmarshal HezEthAddr directly into an alias of ethCommon.Address
126type StrHezEthAddr ethCommon.Address
127
128// UnmarshalText unmarshals a StrHezEthAddr
129func (s *StrHezEthAddr) UnmarshalText(text []byte) error {
130 withoutHez := strings.TrimPrefix(string(text), "hez:")
131 var addr ethCommon.Address
132 if err := addr.UnmarshalText([]byte(withoutHez)); err != nil {
133 return tracerr.Wrap(err)
134 }
135 *s = StrHezEthAddr(addr)
136 return nil
137}
138
139// 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.
140// It assumes that *babyjub.PublicKeyComp are inserted/fetched to/from the DB using the default Scan/Value interface
141type HezBJJ string
142
143// NewHezBJJ creates a HezBJJ from a *babyjub.PublicKeyComp.
144// Calling this method with a nil bjj causes panic
145func NewHezBJJ(pkComp babyjub.PublicKeyComp) HezBJJ {
146 sum := pkComp[0]
147 for i := 1; i < len(pkComp); i++ {
148 sum += pkComp[i]
149 }
150 bjjSum := append(pkComp[:], sum)
151 return HezBJJ("hez:" + base64.RawURLEncoding.EncodeToString(bjjSum))
152}
153
154func hezStrToBJJ(s string) (babyjub.PublicKeyComp, error) {
155 const decodedLen = 33
156 const encodedLen = 44
157 formatErr := errors.New("invalid BJJ format. Must follow this regex: ^hez:[A-Za-z0-9_-]{44}$")
158 encoded := strings.TrimPrefix(s, "hez:")
159 if len(encoded) != encodedLen {
160 return common.EmptyBJJComp, formatErr
161 }
162 decoded, err := base64.RawURLEncoding.DecodeString(encoded)
163 if err != nil {
164 return common.EmptyBJJComp, formatErr
165 }
166 if len(decoded) != decodedLen {
167 return common.EmptyBJJComp, formatErr
168 }
169 bjjBytes := [decodedLen - 1]byte{}
170 copy(bjjBytes[:decodedLen-1], decoded[:decodedLen-1])
171 sum := bjjBytes[0]
172 for i := 1; i < len(bjjBytes); i++ {
173 sum += bjjBytes[i]
174 }
175 if decoded[decodedLen-1] != sum {
176 return common.EmptyBJJComp, tracerr.Wrap(errors.New("checksum verification failed"))
177 }
178 bjjComp := babyjub.PublicKeyComp(bjjBytes)
179 return bjjComp, nil
180}
181
182// ToBJJ returns a babyjub.PublicKeyComp created from HezBJJ
183func (b HezBJJ) ToBJJ() (babyjub.PublicKeyComp, error) {
184 return hezStrToBJJ(string(b))
185}
186
187// Scan implements Scanner for database/sql
188func (b *HezBJJ) Scan(src interface{}) error {
189 bjj := &babyjub.PublicKeyComp{}
190 if err := bjj.Scan(src); err != nil {
191 return tracerr.Wrap(err)
192 }
193 if bjj == nil {
194 return nil
195 }
196 *b = NewHezBJJ(*bjj)
197 return nil
198}
199
200// Value implements valuer for database/sql
201func (b HezBJJ) Value() (driver.Value, error) {
202 bjj, err := b.ToBJJ()
203 if err != nil {
204 return nil, tracerr.Wrap(err)
205 }
206 return bjj.Value()
207}
208
209// StrHezBJJ is used to unmarshal HezBJJ directly into an alias of babyjub.PublicKeyComp
210type StrHezBJJ babyjub.PublicKeyComp
211
212// UnmarshalText unmarshalls a StrHezBJJ
213func (s *StrHezBJJ) UnmarshalText(text []byte) error {
214 bjj, err := hezStrToBJJ(string(text))
215 if err != nil {
216 return tracerr.Wrap(err)
217 }
218 *s = StrHezBJJ(bjj)
219 return nil
220}
221
222// HezIdx is used to value common.Idx directly into strings that follow the Idx key hez format (hez:tokenSymbol:idx) to sql DBs.
223// 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
224type HezIdx string
225
226// StrHezIdx is used to unmarshal HezIdx directly into an alias of common.Idx
227type StrHezIdx common.Idx
228
229// UnmarshalText unmarshals a StrHezIdx
230func (s *StrHezIdx) UnmarshalText(text []byte) error {
231 withoutHez := strings.TrimPrefix(string(text), "hez:")
232 splitted := strings.Split(withoutHez, ":")
233 const expectedLen = 2
234 if len(splitted) != expectedLen {
235 return tracerr.Wrap(fmt.Errorf("can not unmarshal %s into StrHezIdx", text))
236 }
237 idxInt, err := strconv.Atoi(splitted[1])
238 if err != nil {
239 return tracerr.Wrap(err)
240 }
241 *s = StrHezIdx(common.Idx(idxInt))
242 return nil
243}
244
245// EthSignature is used to scan/value []byte representing an Ethereum signature directly into strings from/to sql DBs.
246type EthSignature string
247
248// NewEthSignature creates a *EthSignature from []byte
249// If the provided signature is nil the returned *EthSignature will also be nil
250func NewEthSignature(signature []byte) *EthSignature {
251 if signature == nil {
252 return nil
253 }
254 ethSignature := EthSignature("0x" + hex.EncodeToString(signature))
255 return ðSignature
256}
257
258// Scan implements Scanner for database/sql
259func (e *EthSignature) Scan(src interface{}) error {
260 if srcStr, ok := src.(string); ok {
261 // src is a string
262 *e = *(NewEthSignature([]byte(srcStr)))
263 return nil
264 } else if srcBytes, ok := src.([]byte); ok {
265 // src is []byte
266 *e = *(NewEthSignature(srcBytes))
267 return nil
268 } else {
269 // unexpected src
270 return tracerr.Wrap(fmt.Errorf("can't scan %T into apitypes.EthSignature", src))
271 }
272}
273
274// Value implements valuer for database/sql
275func (e EthSignature) Value() (driver.Value, error) {
276 without0x := strings.TrimPrefix(string(e), "0x")
277 return hex.DecodeString(without0x)
278}
279
280// UnmarshalText unmarshals a StrEthSignature
281func (e *EthSignature) UnmarshalText(text []byte) error {
282 without0x := strings.TrimPrefix(string(text), "0x")
283 signature, err := hex.DecodeString(without0x)
284 if err != nil {
285 return tracerr.Wrap(err)
286 }
287 *e = EthSignature([]byte(signature))
288 return nil
289}