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/json"
  7	"errors"
  8	"fmt"
  9	"math/big"
 10	"strings"
 11
 12	ethCommon "github.com/ethereum/go-ethereum/common"
 13	"github.com/hermeznetwork/hermez-node/common"
 14	"github.com/iden3/go-iden3-crypto/babyjub"
 15)
 16
 17// BigIntStr is used to scan/value *big.Int directly into strings from/to sql DBs.
 18// It assumes that *big.Int are inserted/fetched to/from the DB using the BigIntMeddler meddler
 19// defined at github.com/hermeznetwork/hermez-node/db
 20type BigIntStr string
 21
 22// NewBigIntStr creates a *BigIntStr from a *big.Int.
 23// If the provided bigInt is nil the returned *BigIntStr will also be nil
 24func NewBigIntStr(bigInt *big.Int) *BigIntStr {
 25	if bigInt == nil {
 26		return nil
 27	}
 28	bigIntStr := BigIntStr(bigInt.String())
 29	return &bigIntStr
 30}
 31
 32// Scan implements Scanner for database/sql
 33func (b *BigIntStr) Scan(src interface{}) error {
 34	// decode base64 src
 35	var decoded []byte
 36	var err error
 37	if srcStr, ok := src.(string); ok {
 38		// src is a string
 39		decoded, err = base64.StdEncoding.DecodeString(srcStr)
 40	} else if srcBytes, ok := src.([]byte); ok {
 41		// src is []byte
 42		decoded, err = base64.StdEncoding.DecodeString(string(srcBytes))
 43	} else {
 44		// unexpected src
 45		return fmt.Errorf("can't scan %T into apitypes.BigIntStr", src)
 46	}
 47	if err != nil {
 48		return err
 49	}
 50	// decoded bytes to *big.Int
 51	bigInt := &big.Int{}
 52	bigInt = bigInt.SetBytes(decoded)
 53	// *big.Int to BigIntStr
 54	bigIntStr := NewBigIntStr(bigInt)
 55	if bigIntStr == nil {
 56		return nil
 57	}
 58	*b = *bigIntStr
 59	return nil
 60}
 61
 62// Value implements valuer for database/sql
 63func (b BigIntStr) Value() (driver.Value, error) {
 64	// string to *big.Int
 65	bigInt := &big.Int{}
 66	bigInt, ok := bigInt.SetString(string(b), 10)
 67	if !ok || bigInt == nil {
 68		return nil, errors.New("invalid representation of a *big.Int")
 69	}
 70	// *big.Int to base64
 71	return base64.StdEncoding.EncodeToString(bigInt.Bytes()), nil
 72}
 73
 74type CollectedFees map[common.TokenID]BigIntStr
 75
 76func (c *CollectedFees) UnmarshalJSON(text []byte) error {
 77	fmt.Println(string(text))
 78	bigIntMap := make(map[common.TokenID]*big.Int)
 79	if err := json.Unmarshal(text, &bigIntMap); err != nil {
 80		return err
 81	}
 82	bStrMap := make(map[common.TokenID]BigIntStr)
 83	for k, v := range bigIntMap {
 84		bStr := NewBigIntStr(v)
 85		bStrMap[k] = *bStr
 86	}
 87	*c = CollectedFees(bStrMap)
 88	return nil
 89	// fmt.Println(string(text))
 90	// *b = BigIntStr(string(text))
 91	// return nil
 92	// bigInt := &big.Int{}
 93	// if err := bigInt.UnmarshalText(text); err != nil {
 94	// 	return err
 95	// }
 96	// bigIntStr := NewBigIntStr(bigInt)
 97	// if bigIntStr == nil {
 98	// 	return nil
 99	// }
100	// *b = *bigIntStr
101	// return nil
102}
103
104// 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.
105// It assumes that Ethereum Address are inserted/fetched to/from the DB using the default Scan/Value interface
106type HezEthAddr string
107
108// NewHezEthAddr creates a HezEthAddr from an Ethereum addr
109func NewHezEthAddr(addr ethCommon.Address) HezEthAddr {
110	return HezEthAddr("hez:" + addr.String())
111}
112
113// ToEthAddr returns an Ethereum Address created from HezEthAddr
114func (a HezEthAddr) ToEthAddr() (ethCommon.Address, error) {
115	addrStr := strings.TrimPrefix(string(a), "hez:")
116	var addr ethCommon.Address
117	return addr, addr.UnmarshalText([]byte(addrStr))
118}
119
120// Scan implements Scanner for database/sql
121func (a *HezEthAddr) Scan(src interface{}) error {
122	ethAddr := &ethCommon.Address{}
123	if err := ethAddr.Scan(src); err != nil {
124		return err
125	}
126	if ethAddr == nil {
127		return nil
128	}
129	*a = NewHezEthAddr(*ethAddr)
130	return nil
131}
132
133// Value implements valuer for database/sql
134func (a HezEthAddr) Value() (driver.Value, error) {
135	ethAddr, err := a.ToEthAddr()
136	if err != nil {
137		return nil, err
138	}
139	return ethAddr.Value()
140}
141
142// 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.
143// It assumes that *babyjub.PublicKey are inserted/fetched to/from the DB using the default Scan/Value interface
144type HezBJJ string
145
146// NewHezBJJ creates a HezBJJ from a *babyjub.PublicKey.
147// Calling this method with a nil bjj causes panic
148func NewHezBJJ(bjj *babyjub.PublicKey) HezBJJ {
149	pkComp := [32]byte(bjj.Compress())
150	sum := pkComp[0]
151	for i := 1; i < len(pkComp); i++ {
152		sum += pkComp[i]
153	}
154	bjjSum := append(pkComp[:], sum)
155	return HezBJJ("hez:" + base64.RawURLEncoding.EncodeToString(bjjSum))
156}
157
158// ToBJJ returns a *babyjub.PublicKey created from HezBJJ
159func (b HezBJJ) ToBJJ() (*babyjub.PublicKey, error) {
160	const decodedLen = 33
161	const encodedLen = 44
162	formatErr := errors.New("invalid BJJ format. Must follow this regex: ^hez:[A-Za-z0-9_-]{44}$")
163	encoded := strings.TrimPrefix(string(b), "hez:")
164	if len(encoded) != encodedLen {
165		return nil, formatErr
166	}
167	decoded, err := base64.RawURLEncoding.DecodeString(encoded)
168	if err != nil {
169		return nil, formatErr
170	}
171	if len(decoded) != decodedLen {
172		return nil, formatErr
173	}
174	bjjBytes := [decodedLen - 1]byte{}
175	copy(bjjBytes[:decodedLen-1], decoded[:decodedLen-1])
176	sum := bjjBytes[0]
177	for i := 1; i < len(bjjBytes); i++ {
178		sum += bjjBytes[i]
179	}
180	if decoded[decodedLen-1] != sum {
181		return nil, errors.New("checksum verification failed")
182	}
183	bjjComp := babyjub.PublicKeyComp(bjjBytes)
184	return bjjComp.Decompress()
185}
186
187// Scan implements Scanner for database/sql
188func (b *HezBJJ) Scan(src interface{}) error {
189	bjj := &babyjub.PublicKey{}
190	if err := bjj.Scan(src); err != nil {
191		return 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, err
205	}
206	return bjj.Value()
207}