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