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 fotmat (^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.PublicKey directly into strings that follow the BJJ public key hez fotmat (^hez:[A-Za-z0-9_-]{44}$) from/to sql DBs.
147// It assumes that *babyjub.PublicKey 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.PublicKey.
151// Calling this method with a nil bjj causes panic
152func NewHezBJJ(bjj *babyjub.PublicKey) HezBJJ {
153 pkComp := [32]byte(bjj.Compress())
154 sum := pkComp[0]
155 for i := 1; i < len(pkComp); i++ {
156 sum += pkComp[i]
157 }
158 bjjSum := append(pkComp[:], sum)
159 return HezBJJ("hez:" + base64.RawURLEncoding.EncodeToString(bjjSum))
160}
161
162func hezStrToBJJ(s string) (*babyjub.PublicKey, error) {
163 const decodedLen = 33
164 const encodedLen = 44
165 formatErr := errors.New("invalid BJJ format. Must follow this regex: ^hez:[A-Za-z0-9_-]{44}$")
166 encoded := strings.TrimPrefix(s, "hez:")
167 if len(encoded) != encodedLen {
168 return nil, formatErr
169 }
170 decoded, err := base64.RawURLEncoding.DecodeString(encoded)
171 if err != nil {
172 return nil, formatErr
173 }
174 if len(decoded) != decodedLen {
175 return nil, formatErr
176 }
177 bjjBytes := [decodedLen - 1]byte{}
178 copy(bjjBytes[:decodedLen-1], decoded[:decodedLen-1])
179 sum := bjjBytes[0]
180 for i := 1; i < len(bjjBytes); i++ {
181 sum += bjjBytes[i]
182 }
183 if decoded[decodedLen-1] != sum {
184 return nil, tracerr.Wrap(errors.New("checksum verification failed"))
185 }
186 bjjComp := babyjub.PublicKeyComp(bjjBytes)
187 return bjjComp.Decompress()
188}
189
190// ToBJJ returns a *babyjub.PublicKey created from HezBJJ
191func (b HezBJJ) ToBJJ() (*babyjub.PublicKey, error) {
192 return hezStrToBJJ(string(b))
193}
194
195// Scan implements Scanner for database/sql
196func (b *HezBJJ) Scan(src interface{}) error {
197 bjj := &babyjub.PublicKey{}
198 if err := bjj.Scan(src); err != nil {
199 return tracerr.Wrap(err)
200 }
201 if bjj == nil {
202 return nil
203 }
204 *b = NewHezBJJ(bjj)
205 return nil
206}
207
208// Value implements valuer for database/sql
209func (b HezBJJ) Value() (driver.Value, error) {
210 bjj, err := b.ToBJJ()
211 if err != nil {
212 return nil, tracerr.Wrap(err)
213 }
214 return bjj.Value()
215}
216
217// StrHezBJJ is used to unmarshal HezBJJ directly into an alias of babyjub.PublicKey
218type StrHezBJJ babyjub.PublicKey
219
220// UnmarshalText unmarshals a StrHezBJJ
221func (s *StrHezBJJ) UnmarshalText(text []byte) error {
222 bjj, err := hezStrToBJJ(string(text))
223 if err != nil {
224 return tracerr.Wrap(err)
225 }
226 *s = StrHezBJJ(*bjj)
227 return nil
228}
229
230// HezIdx is used to value common.Idx directly into strings that follow the Idx key hez fotmat (hez:tokenSymbol:idx) to sql DBs.
231// 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
232type HezIdx string
233
234// StrHezIdx is used to unmarshal HezIdx directly into an alias of common.Idx
235type StrHezIdx common.Idx
236
237// UnmarshalText unmarshals a StrHezIdx
238func (s *StrHezIdx) UnmarshalText(text []byte) error {
239 withoutHez := strings.TrimPrefix(string(text), "hez:")
240 splitted := strings.Split(withoutHez, ":")
241 const expectedLen = 2
242 if len(splitted) != expectedLen {
243 return tracerr.Wrap(fmt.Errorf("can not unmarshal %s into StrHezIdx", text))
244 }
245 idxInt, err := strconv.Atoi(splitted[1])
246 if err != nil {
247 return tracerr.Wrap(err)
248 }
249 *s = StrHezIdx(common.Idx(idxInt))
250 return nil
251}
252
253// EthSignature is used to scan/value []byte representing an Ethereum signature directly into strings from/to sql DBs.
254type EthSignature string
255
256// NewEthSignature creates a *EthSignature from []byte
257// If the provided signature is nil the returned *EthSignature will also be nil
258func NewEthSignature(signature []byte) *EthSignature {
259 if signature == nil {
260 return nil
261 }
262 ethSignature := EthSignature("0x" + hex.EncodeToString(signature))
263 return ðSignature
264}
265
266// Scan implements Scanner for database/sql
267func (e *EthSignature) Scan(src interface{}) error {
268 if srcStr, ok := src.(string); ok {
269 // src is a string
270 *e = *(NewEthSignature([]byte(srcStr)))
271 return nil
272 } else if srcBytes, ok := src.([]byte); ok {
273 // src is []byte
274 *e = *(NewEthSignature(srcBytes))
275 return nil
276 } else {
277 // unexpected src
278 return tracerr.Wrap(fmt.Errorf("can't scan %T into apitypes.EthSignature", src))
279 }
280}
281
282// Value implements valuer for database/sql
283func (e EthSignature) Value() (driver.Value, error) {
284 without0x := strings.TrimPrefix(string(e), "0x")
285 return hex.DecodeString(without0x)
286}
287
288// UnmarshalText unmarshals a StrEthSignature
289func (e *EthSignature) UnmarshalText(text []byte) error {
290 without0x := strings.TrimPrefix(string(text), "0x")
291 signature, err := hex.DecodeString(without0x)
292 if err != nil {
293 return tracerr.Wrap(err)
294 }
295 *e = EthSignature([]byte(signature))
296 return nil
297}