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// 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.
160// It assumes that *babyjub.PublicKey are inserted/fetched to/from the DB using the default Scan/Value interface
161type HezBJJ string
162
163// NewHezBJJ creates a HezBJJ from a *babyjub.PublicKey.
164// Calling this method with a nil bjj causes panic
165func NewHezBJJ(bjj *babyjub.PublicKey) HezBJJ {
166	pkComp := [32]byte(bjj.Compress())
167	sum := pkComp[0]
168	for i := 1; i < len(pkComp); i++ {
169		sum += pkComp[i]
170	}
171	bjjSum := append(pkComp[:], sum)
172	return HezBJJ("hez:" + base64.RawURLEncoding.EncodeToString(bjjSum))
173}
174
175func hezStrToBJJ(s string) (*babyjub.PublicKey, error) {
176	const decodedLen = 33
177	const encodedLen = 44
178	formatErr := errors.New("invalid BJJ format. Must follow this regex: ^hez:[A-Za-z0-9_-]{44}$")
179	encoded := strings.TrimPrefix(s, "hez:")
180	if len(encoded) != encodedLen {
181		return nil, formatErr
182	}
183	decoded, err := base64.RawURLEncoding.DecodeString(encoded)
184	if err != nil {
185		return nil, formatErr
186	}
187	if len(decoded) != decodedLen {
188		return nil, formatErr
189	}
190	bjjBytes := [decodedLen - 1]byte{}
191	copy(bjjBytes[:decodedLen-1], decoded[:decodedLen-1])
192	sum := bjjBytes[0]
193	for i := 1; i < len(bjjBytes); i++ {
194		sum += bjjBytes[i]
195	}
196	if decoded[decodedLen-1] != sum {
197		return nil, errors.New("checksum verification failed")
198	}
199	bjjComp := babyjub.PublicKeyComp(bjjBytes)
200	return bjjComp.Decompress()
201}
202
203// ToBJJ returns a *babyjub.PublicKey created from HezBJJ
204func (b HezBJJ) ToBJJ() (*babyjub.PublicKey, error) {
205	return hezStrToBJJ(string(b))
206}
207
208// Scan implements Scanner for database/sql
209func (b *HezBJJ) Scan(src interface{}) error {
210	bjj := &babyjub.PublicKey{}
211	if err := bjj.Scan(src); err != nil {
212		return err
213	}
214	if bjj == nil {
215		return nil
216	}
217	*b = NewHezBJJ(bjj)
218	return nil
219}
220
221// Value implements valuer for database/sql
222func (b HezBJJ) Value() (driver.Value, error) {
223	bjj, err := b.ToBJJ()
224	if err != nil {
225		return nil, err
226	}
227	return bjj.Value()
228}
229
230// StrHezBJJ is used to unmarshal HezBJJ directly into an alias of babyjub.PublicKey
231type StrHezBJJ babyjub.PublicKey
232
233// UnmarshalText unmarshals a StrHezBJJ
234func (s *StrHezBJJ) UnmarshalText(text []byte) error {
235	bjj, err := hezStrToBJJ(string(text))
236	if err != nil {
237		return err
238	}
239	*s = StrHezBJJ(*bjj)
240	return nil
241}
242
243// HezIdx is used to value common.Idx directly into strings that follow the Idx key hez fotmat (hez:tokenSymbol:idx) to sql DBs.
244// 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
245type HezIdx string
246
247// StrHezIdx is used to unmarshal HezIdx directly into an alias of common.Idx
248type StrHezIdx common.Idx
249
250// UnmarshalText unmarshals a StrHezIdx
251func (s *StrHezIdx) UnmarshalText(text []byte) error {
252	withoutHez := strings.TrimPrefix(string(text), "hez:")
253	splitted := strings.Split(withoutHez, ":")
254	const expectedLen = 2
255	if len(splitted) != expectedLen {
256		return fmt.Errorf("can not unmarshal %s into StrHezIdx", text)
257	}
258	idxInt, err := strconv.Atoi(splitted[1])
259	if err != nil {
260		return err
261	}
262	*s = StrHezIdx(common.Idx(idxInt))
263	return nil
264}
265
266// EthSignature is used to scan/value []byte representing an Ethereum signature directly into strings from/to sql DBs.
267type EthSignature string
268
269// NewEthSignature creates a *EthSignature from []byte
270// If the provided signature is nil the returned *EthSignature will also be nil
271func NewEthSignature(signature []byte) *EthSignature {
272	if signature == nil {
273		return nil
274	}
275	ethSignature := EthSignature("0x" + hex.EncodeToString(signature))
276	return &ethSignature
277}
278
279// Scan implements Scanner for database/sql
280func (e *EthSignature) Scan(src interface{}) error {
281	if srcStr, ok := src.(string); ok {
282		// src is a string
283		*e = *(NewEthSignature([]byte(srcStr)))
284		return nil
285	} else if srcBytes, ok := src.([]byte); ok {
286		// src is []byte
287		*e = *(NewEthSignature(srcBytes))
288		return nil
289	} else {
290		// unexpected src
291		return fmt.Errorf("can't scan %T into apitypes.EthSignature", src)
292	}
293}
294
295// Value implements valuer for database/sql
296func (e EthSignature) Value() (driver.Value, error) {
297	without0x := strings.TrimPrefix(string(e), "0x")
298	return hex.DecodeString(without0x)
299}
300
301// UnmarshalText unmarshals a StrEthSignature
302func (e *EthSignature) UnmarshalText(text []byte) error {
303	without0x := strings.TrimPrefix(string(text), "0x")
304	signature, err := hex.DecodeString(without0x)
305	if err != nil {
306		return err
307	}
308	*e = EthSignature([]byte(signature))
309	return nil
310}