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.

259 lines
8.1 KiB

  1. package common
  2. import (
  3. "fmt"
  4. "math/big"
  5. "time"
  6. ethCommon "github.com/ethereum/go-ethereum/common"
  7. "github.com/iden3/go-iden3-crypto/babyjub"
  8. "github.com/iden3/go-iden3-crypto/poseidon"
  9. )
  10. // PoolL2Tx is a struct that represents a L2Tx sent by an account to the coordinator hat is waiting to be forged
  11. type PoolL2Tx struct {
  12. // Stored in DB: mandatory fileds
  13. // TxID (12 bytes) for L2Tx is:
  14. // bytes: | 1 | 6 | 5 |
  15. // values: | type | FromIdx | Nonce |
  16. TxID TxID `meddler:"tx_id"`
  17. FromIdx Idx `meddler:"from_idx"`
  18. ToIdx Idx `meddler:"to_idx,zeroisnull"`
  19. ToEthAddr ethCommon.Address `meddler:"to_eth_addr"`
  20. ToBJJ *babyjub.PublicKey `meddler:"to_bjj"`
  21. TokenID TokenID `meddler:"token_id"`
  22. Amount *big.Int `meddler:"amount,bigint"` // TODO: change to float16
  23. Fee FeeSelector `meddler:"fee"`
  24. Nonce Nonce `meddler:"nonce"` // effective 40 bits used
  25. State PoolL2TxState `meddler:"state"`
  26. Signature *babyjub.Signature `meddler:"signature"` // tx signature
  27. Timestamp time.Time `meddler:"timestamp,utctime"` // time when added to the tx pool
  28. // Stored in DB: optional fileds, may be uninitialized
  29. RqFromIdx Idx `meddler:"rq_from_idx,zeroisnull"` // FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.LoadAmount (deposit)
  30. RqToIdx Idx `meddler:"rq_to_idx,zeroisnull"` // FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.LoadAmount (deposit)
  31. RqToEthAddr ethCommon.Address `meddler:"rq_to_eth_addr"`
  32. RqToBJJ *babyjub.PublicKey `meddler:"rq_to_bjj"` // TODO: stop using json, use scanner/valuer
  33. RqTokenID TokenID `meddler:"rq_token_id,zeroisnull"`
  34. RqAmount *big.Int `meddler:"rq_amount,bigintnull"` // TODO: change to float16
  35. RqFee FeeSelector `meddler:"rq_fee,zeroisnull"`
  36. RqNonce uint64 `meddler:"rq_nonce,zeroisnull"` // effective 48 bits used
  37. AbsoluteFee float64 `meddler:"fee_usd,zeroisnull"`
  38. AbsoluteFeeUpdate time.Time `meddler:"usd_update,utctimez"`
  39. Type TxType `meddler:"tx_type"`
  40. // Extra metadata, may be uninitialized
  41. RqTxCompressedData []byte `meddler:"-"` // 253 bits, optional for atomic txs
  42. }
  43. // NewPoolL2Tx returns the given L2Tx with the TxId & Type parameters calculated
  44. // from the L2Tx values
  45. func NewPoolL2Tx(poolL2Tx *PoolL2Tx) (*PoolL2Tx, error) {
  46. // calculate TxType
  47. var txType TxType
  48. if poolL2Tx.ToIdx == Idx(0) {
  49. txType = TxTypeTransfer
  50. } else if poolL2Tx.ToIdx == Idx(1) {
  51. txType = TxTypeExit
  52. } else if poolL2Tx.ToIdx >= IdxUserThreshold {
  53. txType = TxTypeTransfer
  54. } else {
  55. return poolL2Tx, fmt.Errorf("Can not determine type of PoolL2Tx, invalid ToIdx value: %d", poolL2Tx.ToIdx)
  56. }
  57. // if TxType!=poolL2Tx.TxType return error
  58. if poolL2Tx.Type != "" && poolL2Tx.Type != txType {
  59. return poolL2Tx, fmt.Errorf("PoolL2Tx.Type: %s, should be: %s", poolL2Tx.Type, txType)
  60. }
  61. poolL2Tx.Type = txType
  62. var txid [TxIDLen]byte
  63. txid[0] = TxIDPrefixL2Tx
  64. fromIdxBytes, err := poolL2Tx.FromIdx.Bytes()
  65. if err != nil {
  66. return poolL2Tx, err
  67. }
  68. copy(txid[1:7], fromIdxBytes[:])
  69. nonceBytes, err := poolL2Tx.Nonce.Bytes()
  70. if err != nil {
  71. return poolL2Tx, err
  72. }
  73. copy(txid[7:12], nonceBytes[:])
  74. poolL2Tx.TxID = TxID(txid)
  75. return poolL2Tx, nil
  76. }
  77. // TxCompressedData spec:
  78. // [ 1 bits ] toBJJSign // 1 byte
  79. // [ 8 bits ] userFee // 1 byte
  80. // [ 40 bits ] nonce // 5 bytes
  81. // [ 32 bits ] tokenID // 4 bytes
  82. // [ 16 bits ] amountFloat16 // 2 bytes
  83. // [ 48 bits ] toIdx // 6 bytes
  84. // [ 48 bits ] fromIdx // 6 bytes
  85. // [ 16 bits ] chainId // 2 bytes
  86. // [ 32 bits ] signatureConstant // 4 bytes
  87. // Total bits compressed data: 241 bits // 31 bytes in *big.Int representation
  88. func (tx *PoolL2Tx) TxCompressedData() (*big.Int, error) {
  89. // sigconstant
  90. sc, ok := new(big.Int).SetString("3322668559", 10)
  91. if !ok {
  92. return nil, fmt.Errorf("error parsing SignatureConstant")
  93. }
  94. amountFloat16, err := NewFloat16(tx.Amount)
  95. if err != nil {
  96. return nil, err
  97. }
  98. var b [31]byte
  99. toBJJSign := byte(0)
  100. if babyjub.PointCoordSign(tx.ToBJJ.X) {
  101. toBJJSign = byte(1)
  102. }
  103. b[0] = toBJJSign
  104. b[1] = byte(tx.Fee)
  105. nonceBytes, err := tx.Nonce.Bytes()
  106. if err != nil {
  107. return nil, err
  108. }
  109. copy(b[2:7], nonceBytes[:])
  110. copy(b[7:11], tx.TokenID.Bytes())
  111. copy(b[11:13], amountFloat16.Bytes())
  112. toIdxBytes, err := tx.ToIdx.Bytes()
  113. if err != nil {
  114. return nil, err
  115. }
  116. copy(b[13:19], toIdxBytes[:])
  117. fromIdxBytes, err := tx.FromIdx.Bytes()
  118. if err != nil {
  119. return nil, err
  120. }
  121. copy(b[19:25], fromIdxBytes[:])
  122. copy(b[25:27], []byte{0, 1, 0, 0}) // TODO check js implementation (unexpected behaviour from test vector generated from js)
  123. copy(b[27:31], sc.Bytes())
  124. bi := new(big.Int).SetBytes(b[:])
  125. return bi, nil
  126. }
  127. // TxCompressedDataV2 spec:
  128. // [ 1 bits ] toBJJSign // 1 byte
  129. // [ 8 bits ] userFee // 1 byte
  130. // [ 40 bits ] nonce // 5 bytes
  131. // [ 32 bits ] tokenID // 4 bytes
  132. // [ 16 bits ] amountFloat16 // 2 bytes
  133. // [ 48 bits ] toIdx // 6 bytes
  134. // [ 48 bits ] fromIdx // 6 bytes
  135. // Total bits compressed data: 193 bits // 25 bytes in *big.Int representation
  136. func (tx *PoolL2Tx) TxCompressedDataV2() (*big.Int, error) {
  137. amountFloat16, err := NewFloat16(tx.Amount)
  138. if err != nil {
  139. return nil, err
  140. }
  141. var b [25]byte
  142. toBJJSign := byte(0)
  143. if babyjub.PointCoordSign(tx.ToBJJ.X) {
  144. toBJJSign = byte(1)
  145. }
  146. b[0] = toBJJSign
  147. b[1] = byte(tx.Fee)
  148. nonceBytes, err := tx.Nonce.Bytes()
  149. if err != nil {
  150. return nil, err
  151. }
  152. copy(b[2:7], nonceBytes[:])
  153. copy(b[7:11], tx.TokenID.Bytes())
  154. copy(b[11:13], amountFloat16.Bytes())
  155. toIdxBytes, err := tx.ToIdx.Bytes()
  156. if err != nil {
  157. return nil, err
  158. }
  159. copy(b[13:19], toIdxBytes[:])
  160. fromIdxBytes, err := tx.FromIdx.Bytes()
  161. if err != nil {
  162. return nil, err
  163. }
  164. copy(b[19:25], fromIdxBytes[:])
  165. bi := new(big.Int).SetBytes(b[:])
  166. return bi, nil
  167. }
  168. // HashToSign returns the computed Poseidon hash from the *PoolL2Tx that will be signed by the sender.
  169. func (tx *PoolL2Tx) HashToSign() (*big.Int, error) {
  170. toCompressedData, err := tx.TxCompressedData()
  171. if err != nil {
  172. return nil, err
  173. }
  174. toEthAddr := EthAddrToBigInt(tx.ToEthAddr)
  175. rqToEthAddr := EthAddrToBigInt(tx.RqToEthAddr)
  176. toBJJAy := tx.ToBJJ.Y
  177. rqTxCompressedDataV2, err := tx.TxCompressedDataV2()
  178. if err != nil {
  179. return nil, err
  180. }
  181. return poseidon.Hash([]*big.Int{toCompressedData, toEthAddr, toBJJAy, rqTxCompressedDataV2, rqToEthAddr, tx.RqToBJJ.Y})
  182. }
  183. // VerifySignature returns true if the signature verification is correct for the given PublicKey
  184. func (tx *PoolL2Tx) VerifySignature(pk *babyjub.PublicKey) bool {
  185. h, err := tx.HashToSign()
  186. if err != nil {
  187. return false
  188. }
  189. return pk.VerifyPoseidon(h, tx.Signature)
  190. }
  191. // L2Tx returns a *L2Tx from the PoolL2Tx
  192. func (tx *PoolL2Tx) L2Tx() (*L2Tx, error) {
  193. return &L2Tx{
  194. TxID: tx.TxID,
  195. FromIdx: tx.FromIdx,
  196. ToIdx: tx.ToIdx,
  197. Amount: tx.Amount,
  198. Fee: tx.Fee,
  199. Nonce: tx.Nonce,
  200. Type: tx.Type,
  201. }, nil
  202. }
  203. // Tx returns a *Tx from the PoolL2Tx
  204. func (tx *PoolL2Tx) Tx() (*Tx, error) {
  205. return &Tx{
  206. TxID: tx.TxID,
  207. FromIdx: tx.FromIdx,
  208. ToIdx: tx.ToIdx,
  209. Amount: tx.Amount,
  210. Nonce: &tx.Nonce,
  211. Fee: &tx.Fee,
  212. Type: tx.Type,
  213. }, nil
  214. }
  215. // PoolL2TxsToL2Txs returns an array of []L2Tx from an array of []PoolL2Tx
  216. func PoolL2TxsToL2Txs(txs []PoolL2Tx) ([]L2Tx, error) {
  217. var r []L2Tx
  218. for _, poolTx := range txs {
  219. tx, err := poolTx.L2Tx()
  220. if err != nil {
  221. return nil, err
  222. }
  223. r = append(r, *tx)
  224. }
  225. return r, nil
  226. }
  227. // PoolL2TxState is a struct that represents the status of a L2 transaction
  228. type PoolL2TxState string
  229. const (
  230. // PoolL2TxStatePending represents a valid L2Tx that hasn't started the forging process
  231. PoolL2TxStatePending PoolL2TxState = "pend"
  232. // PoolL2TxStateForging represents a valid L2Tx that has started the forging process
  233. PoolL2TxStateForging PoolL2TxState = "fing"
  234. // PoolL2TxStateForged represents a L2Tx that has already been forged
  235. PoolL2TxStateForged PoolL2TxState = "fged"
  236. // PoolL2TxStateInvalid represents a L2Tx that has been invalidated
  237. PoolL2TxStateInvalid PoolL2TxState = "invl"
  238. )