You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

297 lines
8.7 KiB

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