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