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