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 "strings"
11
12 ethCommon "github.com/ethereum/go-ethereum/common"
13 "github.com/hermeznetwork/hermez-node/common"
14 "github.com/iden3/go-iden3-crypto/babyjub"
15)
16
17// BigIntStr is used to scan/value *big.Int directly into strings from/to sql DBs.
18// It assumes that *big.Int are inserted/fetched to/from the DB using the BigIntMeddler meddler
19// defined at github.com/hermeznetwork/hermez-node/db
20type BigIntStr string
21
22// NewBigIntStr creates a *BigIntStr from a *big.Int.
23// If the provided bigInt is nil the returned *BigIntStr will also be nil
24func NewBigIntStr(bigInt *big.Int) *BigIntStr {
25 if bigInt == nil {
26 return nil
27 }
28 bigIntStr := BigIntStr(bigInt.String())
29 return &bigIntStr
30}
31
32// Scan implements Scanner for database/sql
33func (b *BigIntStr) Scan(src interface{}) error {
34 // decode base64 src
35 var decoded []byte
36 var err error
37 if srcStr, ok := src.(string); ok {
38 // src is a string
39 decoded, err = base64.StdEncoding.DecodeString(srcStr)
40 } else if srcBytes, ok := src.([]byte); ok {
41 // src is []byte
42 decoded, err = base64.StdEncoding.DecodeString(string(srcBytes))
43 } else {
44 // unexpected src
45 return fmt.Errorf("can't scan %T into apitypes.BigIntStr", src)
46 }
47 if err != nil {
48 return err
49 }
50 // decoded bytes to *big.Int
51 bigInt := &big.Int{}
52 bigInt = bigInt.SetBytes(decoded)
53 // *big.Int to BigIntStr
54 bigIntStr := NewBigIntStr(bigInt)
55 if bigIntStr == nil {
56 return nil
57 }
58 *b = *bigIntStr
59 return nil
60}
61
62// Value implements valuer for database/sql
63func (b BigIntStr) Value() (driver.Value, error) {
64 // string to *big.Int
65 bigInt := &big.Int{}
66 bigInt, ok := bigInt.SetString(string(b), 10)
67 if !ok || bigInt == nil {
68 return nil, errors.New("invalid representation of a *big.Int")
69 }
70 // *big.Int to base64
71 return base64.StdEncoding.EncodeToString(bigInt.Bytes()), nil
72}
73
74type CollectedFees map[common.TokenID]BigIntStr
75
76func (c *CollectedFees) UnmarshalJSON(text []byte) error {
77 fmt.Println(string(text))
78 bigIntMap := make(map[common.TokenID]*big.Int)
79 if err := json.Unmarshal(text, &bigIntMap); err != nil {
80 return err
81 }
82 bStrMap := make(map[common.TokenID]BigIntStr)
83 for k, v := range bigIntMap {
84 bStr := NewBigIntStr(v)
85 bStrMap[k] = *bStr
86 }
87 *c = CollectedFees(bStrMap)
88 return nil
89 // fmt.Println(string(text))
90 // *b = BigIntStr(string(text))
91 // return nil
92 // bigInt := &big.Int{}
93 // if err := bigInt.UnmarshalText(text); err != nil {
94 // return err
95 // }
96 // bigIntStr := NewBigIntStr(bigInt)
97 // if bigIntStr == nil {
98 // return nil
99 // }
100 // *b = *bigIntStr
101 // return nil
102}
103
104// 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.
105// It assumes that Ethereum Address are inserted/fetched to/from the DB using the default Scan/Value interface
106type HezEthAddr string
107
108// NewHezEthAddr creates a HezEthAddr from an Ethereum addr
109func NewHezEthAddr(addr ethCommon.Address) HezEthAddr {
110 return HezEthAddr("hez:" + addr.String())
111}
112
113// ToEthAddr returns an Ethereum Address created from HezEthAddr
114func (a HezEthAddr) ToEthAddr() (ethCommon.Address, error) {
115 addrStr := strings.TrimPrefix(string(a), "hez:")
116 var addr ethCommon.Address
117 return addr, addr.UnmarshalText([]byte(addrStr))
118}
119
120// Scan implements Scanner for database/sql
121func (a *HezEthAddr) Scan(src interface{}) error {
122 ethAddr := ðCommon.Address{}
123 if err := ethAddr.Scan(src); err != nil {
124 return err
125 }
126 if ethAddr == nil {
127 return nil
128 }
129 *a = NewHezEthAddr(*ethAddr)
130 return nil
131}
132
133// Value implements valuer for database/sql
134func (a HezEthAddr) Value() (driver.Value, error) {
135 ethAddr, err := a.ToEthAddr()
136 if err != nil {
137 return nil, err
138 }
139 return ethAddr.Value()
140}
141
142// 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.
143// It assumes that *babyjub.PublicKey are inserted/fetched to/from the DB using the default Scan/Value interface
144type HezBJJ string
145
146// NewHezBJJ creates a HezBJJ from a *babyjub.PublicKey.
147// Calling this method with a nil bjj causes panic
148func NewHezBJJ(bjj *babyjub.PublicKey) HezBJJ {
149 pkComp := [32]byte(bjj.Compress())
150 sum := pkComp[0]
151 for i := 1; i < len(pkComp); i++ {
152 sum += pkComp[i]
153 }
154 bjjSum := append(pkComp[:], sum)
155 return HezBJJ("hez:" + base64.RawURLEncoding.EncodeToString(bjjSum))
156}
157
158// ToBJJ returns a *babyjub.PublicKey created from HezBJJ
159func (b HezBJJ) ToBJJ() (*babyjub.PublicKey, error) {
160 const decodedLen = 33
161 const encodedLen = 44
162 formatErr := errors.New("invalid BJJ format. Must follow this regex: ^hez:[A-Za-z0-9_-]{44}$")
163 encoded := strings.TrimPrefix(string(b), "hez:")
164 if len(encoded) != encodedLen {
165 return nil, formatErr
166 }
167 decoded, err := base64.RawURLEncoding.DecodeString(encoded)
168 if err != nil {
169 return nil, formatErr
170 }
171 if len(decoded) != decodedLen {
172 return nil, formatErr
173 }
174 bjjBytes := [decodedLen - 1]byte{}
175 copy(bjjBytes[:decodedLen-1], decoded[:decodedLen-1])
176 sum := bjjBytes[0]
177 for i := 1; i < len(bjjBytes); i++ {
178 sum += bjjBytes[i]
179 }
180 if decoded[decodedLen-1] != sum {
181 return nil, errors.New("checksum verification failed")
182 }
183 bjjComp := babyjub.PublicKeyComp(bjjBytes)
184 return bjjComp.Decompress()
185}
186
187// Scan implements Scanner for database/sql
188func (b *HezBJJ) Scan(src interface{}) error {
189 bjj := &babyjub.PublicKey{}
190 if err := bjj.Scan(src); err != nil {
191 return 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, err
205 }
206 return bjj.Value()
207}