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	"errors"
  8	"fmt"
  9	"math/big"
 10	"strconv"
 11	"strings"
 12
 13	ethCommon "github.com/ethereum/go-ethereum/common"
 14	"github.com/hermeznetwork/hermez-node/common"
 15	"github.com/hermeznetwork/tracerr"
 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 tracerr.Wrap(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, tracerr.Wrap(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 tracerr.Wrap(fmt.Errorf("could not unmarshal %s into a StrBigInt", text))
 70	}
 71	*s = StrBigInt(*bi)
 72	return nil
 73}
 74
 75// CollectedFeesAPI is send common.batch.CollectedFee through the API
 76type CollectedFeesAPI map[common.TokenID]BigIntStr
 77
 78// NewCollectedFeesAPI creates a new CollectedFeesAPI from a *big.Int map
 79func NewCollectedFeesAPI(m map[common.TokenID]*big.Int) CollectedFeesAPI {
 80	c := CollectedFeesAPI(make(map[common.TokenID]BigIntStr))
 81	for k, v := range m {
 82		c[k] = *NewBigIntStr(v)
 83	}
 84	return c
 85}
 86
 87// HezEthAddr is used to scan/value Ethereum Address directly into strings that follow the Ethereum address hez format (^hez:0x[a-fA-F0-9]{40}$) from/to sql DBs.
 88// It assumes that Ethereum Address are inserted/fetched to/from the DB using the default Scan/Value interface
 89type HezEthAddr string
 90
 91// NewHezEthAddr creates a HezEthAddr from an Ethereum addr
 92func NewHezEthAddr(addr ethCommon.Address) HezEthAddr {
 93	return HezEthAddr("hez:" + addr.String())
 94}
 95
 96// ToEthAddr returns an Ethereum Address created from HezEthAddr
 97func (a HezEthAddr) ToEthAddr() (ethCommon.Address, error) {
 98	addrStr := strings.TrimPrefix(string(a), "hez:")
 99	var addr ethCommon.Address
100	return addr, addr.UnmarshalText([]byte(addrStr))
101}
102
103// Scan implements Scanner for database/sql
104func (a *HezEthAddr) Scan(src interface{}) error {
105	ethAddr := &ethCommon.Address{}
106	if err := ethAddr.Scan(src); err != nil {
107		return tracerr.Wrap(err)
108	}
109	if ethAddr == nil {
110		return nil
111	}
112	*a = NewHezEthAddr(*ethAddr)
113	return nil
114}
115
116// Value implements valuer for database/sql
117func (a HezEthAddr) Value() (driver.Value, error) {
118	ethAddr, err := a.ToEthAddr()
119	if err != nil {
120		return nil, tracerr.Wrap(err)
121	}
122	return ethAddr.Value()
123}
124
125// StrHezEthAddr is used to unmarshal HezEthAddr directly into an alias of ethCommon.Address
126type StrHezEthAddr ethCommon.Address
127
128// UnmarshalText unmarshals a StrHezEthAddr
129func (s *StrHezEthAddr) UnmarshalText(text []byte) error {
130	withoutHez := strings.TrimPrefix(string(text), "hez:")
131	var addr ethCommon.Address
132	if err := addr.UnmarshalText([]byte(withoutHez)); err != nil {
133		return tracerr.Wrap(err)
134	}
135	*s = StrHezEthAddr(addr)
136	return nil
137}
138
139// HezBJJ is used to scan/value *babyjub.PublicKeyComp directly into strings that follow the BJJ public key hez format (^hez:[A-Za-z0-9_-]{44}$) from/to sql DBs.
140// It assumes that *babyjub.PublicKeyComp are inserted/fetched to/from the DB using the default Scan/Value interface
141type HezBJJ string
142
143// NewHezBJJ creates a HezBJJ from a *babyjub.PublicKeyComp.
144// Calling this method with a nil bjj causes panic
145func NewHezBJJ(pkComp babyjub.PublicKeyComp) HezBJJ {
146	sum := pkComp[0]
147	for i := 1; i < len(pkComp); i++ {
148		sum += pkComp[i]
149	}
150	bjjSum := append(pkComp[:], sum)
151	return HezBJJ("hez:" + base64.RawURLEncoding.EncodeToString(bjjSum))
152}
153
154func hezStrToBJJ(s string) (babyjub.PublicKeyComp, error) {
155	const decodedLen = 33
156	const encodedLen = 44
157	formatErr := errors.New("invalid BJJ format. Must follow this regex: ^hez:[A-Za-z0-9_-]{44}$")
158	encoded := strings.TrimPrefix(s, "hez:")
159	if len(encoded) != encodedLen {
160		return common.EmptyBJJComp, formatErr
161	}
162	decoded, err := base64.RawURLEncoding.DecodeString(encoded)
163	if err != nil {
164		return common.EmptyBJJComp, formatErr
165	}
166	if len(decoded) != decodedLen {
167		return common.EmptyBJJComp, formatErr
168	}
169	bjjBytes := [decodedLen - 1]byte{}
170	copy(bjjBytes[:decodedLen-1], decoded[:decodedLen-1])
171	sum := bjjBytes[0]
172	for i := 1; i < len(bjjBytes); i++ {
173		sum += bjjBytes[i]
174	}
175	if decoded[decodedLen-1] != sum {
176		return common.EmptyBJJComp, tracerr.Wrap(errors.New("checksum verification failed"))
177	}
178	bjjComp := babyjub.PublicKeyComp(bjjBytes)
179	return bjjComp, nil
180}
181
182// ToBJJ returns a babyjub.PublicKeyComp created from HezBJJ
183func (b HezBJJ) ToBJJ() (babyjub.PublicKeyComp, error) {
184	return hezStrToBJJ(string(b))
185}
186
187// Scan implements Scanner for database/sql
188func (b *HezBJJ) Scan(src interface{}) error {
189	bjj := &babyjub.PublicKeyComp{}
190	if err := bjj.Scan(src); err != nil {
191		return tracerr.Wrap(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, tracerr.Wrap(err)
205	}
206	return bjj.Value()
207}
208
209// StrHezBJJ is used to unmarshal HezBJJ directly into an alias of babyjub.PublicKeyComp
210type StrHezBJJ babyjub.PublicKeyComp
211
212// UnmarshalText unmarshalls a StrHezBJJ
213func (s *StrHezBJJ) UnmarshalText(text []byte) error {
214	bjj, err := hezStrToBJJ(string(text))
215	if err != nil {
216		return tracerr.Wrap(err)
217	}
218	*s = StrHezBJJ(bjj)
219	return nil
220}
221
222// HezIdx is used to value common.Idx directly into strings that follow the Idx key hez format (hez:tokenSymbol:idx) to sql DBs.
223// Note that this can only be used to insert to DB since there is no way to automatically read from the DB since it needs the tokenSymbol
224type HezIdx string
225
226// StrHezIdx is used to unmarshal HezIdx directly into an alias of common.Idx
227type StrHezIdx common.Idx
228
229// UnmarshalText unmarshals a StrHezIdx
230func (s *StrHezIdx) UnmarshalText(text []byte) error {
231	withoutHez := strings.TrimPrefix(string(text), "hez:")
232	splitted := strings.Split(withoutHez, ":")
233	const expectedLen = 2
234	if len(splitted) != expectedLen {
235		return tracerr.Wrap(fmt.Errorf("can not unmarshal %s into StrHezIdx", text))
236	}
237	idxInt, err := strconv.Atoi(splitted[1])
238	if err != nil {
239		return tracerr.Wrap(err)
240	}
241	*s = StrHezIdx(common.Idx(idxInt))
242	return nil
243}
244
245// EthSignature is used to scan/value []byte representing an Ethereum signature directly into strings from/to sql DBs.
246type EthSignature string
247
248// NewEthSignature creates a *EthSignature from []byte
249// If the provided signature is nil the returned *EthSignature will also be nil
250func NewEthSignature(signature []byte) *EthSignature {
251	if signature == nil {
252		return nil
253	}
254	ethSignature := EthSignature("0x" + hex.EncodeToString(signature))
255	return &ethSignature
256}
257
258// Scan implements Scanner for database/sql
259func (e *EthSignature) Scan(src interface{}) error {
260	if srcStr, ok := src.(string); ok {
261		// src is a string
262		*e = *(NewEthSignature([]byte(srcStr)))
263		return nil
264	} else if srcBytes, ok := src.([]byte); ok {
265		// src is []byte
266		*e = *(NewEthSignature(srcBytes))
267		return nil
268	} else {
269		// unexpected src
270		return tracerr.Wrap(fmt.Errorf("can't scan %T into apitypes.EthSignature", src))
271	}
272}
273
274// Value implements valuer for database/sql
275func (e EthSignature) Value() (driver.Value, error) {
276	without0x := strings.TrimPrefix(string(e), "0x")
277	return hex.DecodeString(without0x)
278}
279
280// UnmarshalText unmarshals a StrEthSignature
281func (e *EthSignature) UnmarshalText(text []byte) error {
282	without0x := strings.TrimPrefix(string(text), "0x")
283	signature, err := hex.DecodeString(without0x)
284	if err != nil {
285		return tracerr.Wrap(err)
286	}
287	*e = EthSignature([]byte(signature))
288	return nil
289}