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
 74// CollectedFees is used to retrieve common.batch.CollectedFee from the DB
 75type CollectedFees map[common.TokenID]BigIntStr
 76
 77// UnmarshalJSON unmarshals a json representation of map[common.TokenID]*big.Int
 78func (c *CollectedFees) UnmarshalJSON(text []byte) error {
 79	bigIntMap := make(map[common.TokenID]*big.Int)
 80	if err := json.Unmarshal(text, &bigIntMap); err != nil {
 81		return err
 82	}
 83	bStrMap := make(map[common.TokenID]BigIntStr)
 84	for k, v := range bigIntMap {
 85		bStr := NewBigIntStr(v)
 86		bStrMap[k] = *bStr
 87	}
 88	*c = CollectedFees(bStrMap)
 89	return nil
 90}
 91
 92// 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.
 93// It assumes that Ethereum Address are inserted/fetched to/from the DB using the default Scan/Value interface
 94type HezEthAddr string
 95
 96// NewHezEthAddr creates a HezEthAddr from an Ethereum addr
 97func NewHezEthAddr(addr ethCommon.Address) HezEthAddr {
 98	return HezEthAddr("hez:" + addr.String())
 99}
100
101// ToEthAddr returns an Ethereum Address created from HezEthAddr
102func (a HezEthAddr) ToEthAddr() (ethCommon.Address, error) {
103	addrStr := strings.TrimPrefix(string(a), "hez:")
104	var addr ethCommon.Address
105	return addr, addr.UnmarshalText([]byte(addrStr))
106}
107
108// Scan implements Scanner for database/sql
109func (a *HezEthAddr) Scan(src interface{}) error {
110	ethAddr := &ethCommon.Address{}
111	if err := ethAddr.Scan(src); err != nil {
112		return err
113	}
114	if ethAddr == nil {
115		return nil
116	}
117	*a = NewHezEthAddr(*ethAddr)
118	return nil
119}
120
121// Value implements valuer for database/sql
122func (a HezEthAddr) Value() (driver.Value, error) {
123	ethAddr, err := a.ToEthAddr()
124	if err != nil {
125		return nil, err
126	}
127	return ethAddr.Value()
128}
129
130// 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.
131// It assumes that *babyjub.PublicKey are inserted/fetched to/from the DB using the default Scan/Value interface
132type HezBJJ string
133
134// NewHezBJJ creates a HezBJJ from a *babyjub.PublicKey.
135// Calling this method with a nil bjj causes panic
136func NewHezBJJ(bjj *babyjub.PublicKey) HezBJJ {
137	pkComp := [32]byte(bjj.Compress())
138	sum := pkComp[0]
139	for i := 1; i < len(pkComp); i++ {
140		sum += pkComp[i]
141	}
142	bjjSum := append(pkComp[:], sum)
143	return HezBJJ("hez:" + base64.RawURLEncoding.EncodeToString(bjjSum))
144}
145
146// ToBJJ returns a *babyjub.PublicKey created from HezBJJ
147func (b HezBJJ) ToBJJ() (*babyjub.PublicKey, error) {
148	const decodedLen = 33
149	const encodedLen = 44
150	formatErr := errors.New("invalid BJJ format. Must follow this regex: ^hez:[A-Za-z0-9_-]{44}$")
151	encoded := strings.TrimPrefix(string(b), "hez:")
152	if len(encoded) != encodedLen {
153		return nil, formatErr
154	}
155	decoded, err := base64.RawURLEncoding.DecodeString(encoded)
156	if err != nil {
157		return nil, formatErr
158	}
159	if len(decoded) != decodedLen {
160		return nil, formatErr
161	}
162	bjjBytes := [decodedLen - 1]byte{}
163	copy(bjjBytes[:decodedLen-1], decoded[:decodedLen-1])
164	sum := bjjBytes[0]
165	for i := 1; i < len(bjjBytes); i++ {
166		sum += bjjBytes[i]
167	}
168	if decoded[decodedLen-1] != sum {
169		return nil, errors.New("checksum verification failed")
170	}
171	bjjComp := babyjub.PublicKeyComp(bjjBytes)
172	return bjjComp.Decompress()
173}
174
175// Scan implements Scanner for database/sql
176func (b *HezBJJ) Scan(src interface{}) error {
177	bjj := &babyjub.PublicKey{}
178	if err := bjj.Scan(src); err != nil {
179		return err
180	}
181	if bjj == nil {
182		return nil
183	}
184	*b = NewHezBJJ(bjj)
185	return nil
186}
187
188// Value implements valuer for database/sql
189func (b HezBJJ) Value() (driver.Value, error) {
190	bjj, err := b.ToBJJ()
191	if err != nil {
192		return nil, err
193	}
194	return bjj.Value()
195}