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/hermeznetwork/tracerr"
 17	"github.com/iden3/go-iden3-crypto/babyjub"
 18)
 19
 20// BigIntStr is used to scan/value *big.Int directly into strings from/to sql DBs.
 21// It assumes that *big.Int are inserted/fetched to/from the DB using the BigIntMeddler meddler
 22// defined at github.com/hermeznetwork/hermez-node/db
 23type BigIntStr string
 24
 25// NewBigIntStr creates a *BigIntStr from a *big.Int.
 26// If the provided bigInt is nil the returned *BigIntStr will also be nil
 27func NewBigIntStr(bigInt *big.Int) *BigIntStr {
 28	if bigInt == nil {
 29		return nil
 30	}
 31	bigIntStr := BigIntStr(bigInt.String())
 32	return &bigIntStr
 33}
 34
 35// Scan implements Scanner for database/sql
 36func (b *BigIntStr) Scan(src interface{}) error {
 37	srcBytes, ok := src.([]byte)
 38	if !ok {
 39		return tracerr.Wrap(fmt.Errorf("can't scan %T into apitypes.BigIntStr", src))
 40	}
 41	// bytes to *big.Int
 42	bigInt := new(big.Int).SetBytes(srcBytes)
 43	// *big.Int to BigIntStr
 44	bigIntStr := NewBigIntStr(bigInt)
 45	if bigIntStr == nil {
 46		return nil
 47	}
 48	*b = *bigIntStr
 49	return nil
 50}
 51
 52// Value implements valuer for database/sql
 53func (b BigIntStr) Value() (driver.Value, error) {
 54	// string to *big.Int
 55	bigInt, ok := new(big.Int).SetString(string(b), 10)
 56	if !ok || bigInt == nil {
 57		return nil, tracerr.Wrap(errors.New("invalid representation of a *big.Int"))
 58	}
 59	// *big.Int to bytes
 60	return bigInt.Bytes(), nil
 61}
 62
 63// StrBigInt is used to unmarshal BigIntStr directly into an alias of big.Int
 64type StrBigInt big.Int
 65
 66// UnmarshalText unmarshals a StrBigInt
 67func (s *StrBigInt) UnmarshalText(text []byte) error {
 68	bi, ok := (*big.Int)(s).SetString(string(text), 10)
 69	if !ok {
 70		return tracerr.Wrap(fmt.Errorf("could not unmarshal %s into a StrBigInt", text))
 71	}
 72	*s = StrBigInt(*bi)
 73	return nil
 74}
 75
 76// CollectedFees is used to retrieve common.batch.CollectedFee from the DB
 77type CollectedFees map[common.TokenID]BigIntStr
 78
 79// UnmarshalJSON unmarshals a json representation of map[common.TokenID]*big.Int
 80func (c *CollectedFees) UnmarshalJSON(text []byte) error {
 81	bigIntMap := make(map[common.TokenID]*big.Int)
 82	if err := json.Unmarshal(text, &bigIntMap); err != nil {
 83		return tracerr.Wrap(err)
 84	}
 85	*c = CollectedFees(make(map[common.TokenID]BigIntStr))
 86	for k, v := range bigIntMap {
 87		bStr := NewBigIntStr(v)
 88		(CollectedFees(*c)[k]) = *bStr
 89	}
 90	// *c = CollectedFees(bStrMap)
 91	return nil
 92}
 93
 94// 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.
 95// It assumes that Ethereum Address are inserted/fetched to/from the DB using the default Scan/Value interface
 96type HezEthAddr string
 97
 98// NewHezEthAddr creates a HezEthAddr from an Ethereum addr
 99func NewHezEthAddr(addr ethCommon.Address) HezEthAddr {
100	return HezEthAddr("hez:" + addr.String())
101}
102
103// ToEthAddr returns an Ethereum Address created from HezEthAddr
104func (a HezEthAddr) ToEthAddr() (ethCommon.Address, error) {
105	addrStr := strings.TrimPrefix(string(a), "hez:")
106	var addr ethCommon.Address
107	return addr, addr.UnmarshalText([]byte(addrStr))
108}
109
110// Scan implements Scanner for database/sql
111func (a *HezEthAddr) Scan(src interface{}) error {
112	ethAddr := &ethCommon.Address{}
113	if err := ethAddr.Scan(src); err != nil {
114		return tracerr.Wrap(err)
115	}
116	if ethAddr == nil {
117		return nil
118	}
119	*a = NewHezEthAddr(*ethAddr)
120	return nil
121}
122
123// Value implements valuer for database/sql
124func (a HezEthAddr) Value() (driver.Value, error) {
125	ethAddr, err := a.ToEthAddr()
126	if err != nil {
127		return nil, tracerr.Wrap(err)
128	}
129	return ethAddr.Value()
130}
131
132// StrHezEthAddr is used to unmarshal HezEthAddr directly into an alias of ethCommon.Address
133type StrHezEthAddr ethCommon.Address
134
135// UnmarshalText unmarshals a StrHezEthAddr
136func (s *StrHezEthAddr) UnmarshalText(text []byte) error {
137	withoutHez := strings.TrimPrefix(string(text), "hez:")
138	var addr ethCommon.Address
139	if err := addr.UnmarshalText([]byte(withoutHez)); err != nil {
140		return tracerr.Wrap(err)
141	}
142	*s = StrHezEthAddr(addr)
143	return nil
144}
145
146// HezBJJ is used to scan/value *babyjub.PublicKeyComp directly into strings that follow the BJJ public key hez fotmat (^hez:[A-Za-z0-9_-]{44}$) from/to sql DBs.
147// It assumes that *babyjub.PublicKeyComp are inserted/fetched to/from the DB using the default Scan/Value interface
148type HezBJJ string
149
150// NewHezBJJ creates a HezBJJ from a *babyjub.PublicKeyComp.
151// Calling this method with a nil bjj causes panic
152func NewHezBJJ(pkComp babyjub.PublicKeyComp) HezBJJ {
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.PublicKeyComp, 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 common.EmptyBJJComp, formatErr
168	}
169	decoded, err := base64.RawURLEncoding.DecodeString(encoded)
170	if err != nil {
171		return common.EmptyBJJComp, formatErr
172	}
173	if len(decoded) != decodedLen {
174		return common.EmptyBJJComp, 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 common.EmptyBJJComp, tracerr.Wrap(errors.New("checksum verification failed"))
184	}
185	bjjComp := babyjub.PublicKeyComp(bjjBytes)
186	return bjjComp, nil
187}
188
189// ToBJJ returns a babyjub.PublicKeyComp created from HezBJJ
190func (b HezBJJ) ToBJJ() (babyjub.PublicKeyComp, 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.PublicKeyComp{}
197	if err := bjj.Scan(src); err != nil {
198		return tracerr.Wrap(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, tracerr.Wrap(err)
212	}
213	return bjj.Value()
214}
215
216// StrHezBJJ is used to unmarshal HezBJJ directly into an alias of babyjub.PublicKeyComp
217type StrHezBJJ babyjub.PublicKeyComp
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 tracerr.Wrap(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 tracerr.Wrap(fmt.Errorf("can not unmarshal %s into StrHezIdx", text))
243	}
244	idxInt, err := strconv.Atoi(splitted[1])
245	if err != nil {
246		return tracerr.Wrap(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 tracerr.Wrap(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 tracerr.Wrap(err)
293	}
294	*e = EthSignature([]byte(signature))
295	return nil
296}