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