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