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