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.

340 lines
11 KiB

  1. package common
  2. import (
  3. "errors"
  4. "fmt"
  5. "math/big"
  6. "time"
  7. ethCommon "github.com/ethereum/go-ethereum/common"
  8. "github.com/hermeznetwork/tracerr"
  9. "github.com/iden3/go-iden3-crypto/babyjub"
  10. "github.com/iden3/go-iden3-crypto/poseidon"
  11. )
  12. // PoolL2Tx is a struct that represents a L2Tx sent by an account to the coordinator hat is waiting to be forged
  13. type PoolL2Tx struct {
  14. // Stored in DB: mandatory fileds
  15. // TxID (12 bytes) for L2Tx is:
  16. // bytes: | 1 | 6 | 5 |
  17. // values: | type | FromIdx | Nonce |
  18. TxID TxID `meddler:"tx_id"`
  19. FromIdx Idx `meddler:"from_idx"`
  20. ToIdx Idx `meddler:"to_idx,zeroisnull"`
  21. AuxToIdx Idx `meddler:"-"` // AuxToIdx is only used internally at the StateDB to avoid repeated computation when processing transactions (from Synchronizer, TxSelector, BatchBuilder)
  22. ToEthAddr ethCommon.Address `meddler:"to_eth_addr,zeroisnull"`
  23. ToBJJ *babyjub.PublicKey `meddler:"to_bjj"`
  24. TokenID TokenID `meddler:"token_id"`
  25. Amount *big.Int `meddler:"amount,bigint"` // TODO: change to float16
  26. Fee FeeSelector `meddler:"fee"`
  27. Nonce Nonce `meddler:"nonce"` // effective 40 bits used
  28. State PoolL2TxState `meddler:"state"`
  29. Signature babyjub.SignatureComp `meddler:"signature"` // tx signature
  30. Timestamp time.Time `meddler:"timestamp,utctime"` // time when added to the tx pool
  31. // Stored in DB: optional fileds, may be uninitialized
  32. RqFromIdx Idx `meddler:"rq_from_idx,zeroisnull"` // FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.DepositAmount (deposit)
  33. RqToIdx Idx `meddler:"rq_to_idx,zeroisnull"` // FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.DepositAmount (deposit)
  34. RqToEthAddr ethCommon.Address `meddler:"rq_to_eth_addr,zeroisnull"`
  35. RqToBJJ *babyjub.PublicKey `meddler:"rq_to_bjj"` // TODO: stop using json, use scanner/valuer
  36. RqTokenID TokenID `meddler:"rq_token_id,zeroisnull"`
  37. RqAmount *big.Int `meddler:"rq_amount,bigintnull"` // TODO: change to float16
  38. RqFee FeeSelector `meddler:"rq_fee,zeroisnull"`
  39. RqNonce Nonce `meddler:"rq_nonce,zeroisnull"` // effective 48 bits used
  40. AbsoluteFee float64 `meddler:"fee_usd,zeroisnull"`
  41. AbsoluteFeeUpdate time.Time `meddler:"usd_update,utctimez"`
  42. Type TxType `meddler:"tx_type"`
  43. // Extra metadata, may be uninitialized
  44. RqTxCompressedData []byte `meddler:"-"` // 253 bits, optional for atomic txs
  45. }
  46. // NewPoolL2Tx returns the given L2Tx with the TxId & Type parameters calculated
  47. // from the L2Tx values
  48. func NewPoolL2Tx(tx *PoolL2Tx) (*PoolL2Tx, error) {
  49. txTypeOld := tx.Type
  50. if err := tx.SetType(); err != nil {
  51. return nil, tracerr.Wrap(err)
  52. }
  53. // If original Type doesn't match the correct one, return error
  54. if txTypeOld != "" && txTypeOld != tx.Type {
  55. return nil, tracerr.Wrap(fmt.Errorf("L2Tx.Type: %s, should be: %s",
  56. tx.Type, txTypeOld))
  57. }
  58. txIDOld := tx.TxID
  59. if err := tx.SetID(); err != nil {
  60. return nil, tracerr.Wrap(err)
  61. }
  62. // If original TxID doesn't match the correct one, return error
  63. if txIDOld != (TxID{}) && txIDOld != tx.TxID {
  64. return tx, tracerr.Wrap(fmt.Errorf("PoolL2Tx.TxID: %s, should be: %s",
  65. tx.TxID.String(), txIDOld.String()))
  66. }
  67. return tx, nil
  68. }
  69. // SetType sets the type of the transaction
  70. func (tx *PoolL2Tx) SetType() error {
  71. if tx.ToIdx >= IdxUserThreshold {
  72. tx.Type = TxTypeTransfer
  73. } else if tx.ToIdx == 1 {
  74. tx.Type = TxTypeExit
  75. } else if tx.ToIdx == 0 {
  76. if tx.ToBJJ != nil && tx.ToEthAddr == FFAddr {
  77. tx.Type = TxTypeTransferToBJJ
  78. } else if tx.ToEthAddr != FFAddr && tx.ToEthAddr != EmptyAddr {
  79. tx.Type = TxTypeTransferToEthAddr
  80. }
  81. } else {
  82. return tracerr.Wrap(errors.New("malformed transaction"))
  83. }
  84. return nil
  85. }
  86. // SetID sets the ID of the transaction. Uses (FromIdx, Nonce).
  87. func (tx *PoolL2Tx) SetID() error {
  88. tx.TxID[0] = TxIDPrefixL2Tx
  89. fromIdxBytes, err := tx.FromIdx.Bytes()
  90. if err != nil {
  91. return tracerr.Wrap(err)
  92. }
  93. copy(tx.TxID[1:7], fromIdxBytes[:])
  94. nonceBytes, err := tx.Nonce.Bytes()
  95. if err != nil {
  96. return tracerr.Wrap(err)
  97. }
  98. copy(tx.TxID[7:12], nonceBytes[:])
  99. return nil
  100. }
  101. // TxCompressedData spec:
  102. // [ 1 bits ] toBJJSign // 1 byte
  103. // [ 8 bits ] userFee // 1 byte
  104. // [ 40 bits ] nonce // 5 bytes
  105. // [ 32 bits ] tokenID // 4 bytes
  106. // [ 16 bits ] amountFloat16 // 2 bytes
  107. // [ 48 bits ] toIdx // 6 bytes
  108. // [ 48 bits ] fromIdx // 6 bytes
  109. // [ 16 bits ] chainId // 2 bytes
  110. // [ 32 bits ] signatureConstant // 4 bytes
  111. // Total bits compressed data: 241 bits // 31 bytes in *big.Int representation
  112. func (tx *PoolL2Tx) TxCompressedData() (*big.Int, error) {
  113. amountFloat16, err := NewFloat16(tx.Amount)
  114. if err != nil {
  115. return nil, tracerr.Wrap(err)
  116. }
  117. var b [31]byte
  118. toBJJSign := byte(0)
  119. if tx.ToBJJ != nil && babyjub.PointCoordSign(tx.ToBJJ.X) {
  120. toBJJSign = byte(1)
  121. }
  122. b[0] = toBJJSign
  123. b[1] = byte(tx.Fee)
  124. nonceBytes, err := tx.Nonce.Bytes()
  125. if err != nil {
  126. return nil, tracerr.Wrap(err)
  127. }
  128. copy(b[2:7], nonceBytes[:])
  129. copy(b[7:11], tx.TokenID.Bytes())
  130. copy(b[11:13], amountFloat16.Bytes())
  131. toIdxBytes, err := tx.ToIdx.Bytes()
  132. if err != nil {
  133. return nil, tracerr.Wrap(err)
  134. }
  135. copy(b[13:19], toIdxBytes[:])
  136. fromIdxBytes, err := tx.FromIdx.Bytes()
  137. if err != nil {
  138. return nil, tracerr.Wrap(err)
  139. }
  140. copy(b[19:25], fromIdxBytes[:])
  141. copy(b[25:27], []byte{0, 0}) // TODO this will be generated by the ChainID config parameter
  142. copy(b[27:31], SignatureConstantBytes[:])
  143. bi := new(big.Int).SetBytes(b[:])
  144. return bi, nil
  145. }
  146. // TxCompressedDataV2 spec:
  147. // [ 1 bits ] toBJJSign // 1 byte
  148. // [ 8 bits ] userFee // 1 byte
  149. // [ 40 bits ] nonce // 5 bytes
  150. // [ 32 bits ] tokenID // 4 bytes
  151. // [ 16 bits ] amountFloat16 // 2 bytes
  152. // [ 48 bits ] toIdx // 6 bytes
  153. // [ 48 bits ] fromIdx // 6 bytes
  154. // Total bits compressed data: 193 bits // 25 bytes in *big.Int representation
  155. func (tx *PoolL2Tx) TxCompressedDataV2() (*big.Int, error) {
  156. if tx.Amount == nil {
  157. tx.Amount = big.NewInt(0)
  158. }
  159. amountFloat16, err := NewFloat16(tx.Amount)
  160. if err != nil {
  161. return nil, tracerr.Wrap(err)
  162. }
  163. var b [25]byte
  164. toBJJSign := byte(0)
  165. if tx.ToBJJ != nil && babyjub.PointCoordSign(tx.ToBJJ.X) {
  166. toBJJSign = byte(1)
  167. }
  168. b[0] = toBJJSign
  169. b[1] = byte(tx.Fee)
  170. nonceBytes, err := tx.Nonce.Bytes()
  171. if err != nil {
  172. return nil, tracerr.Wrap(err)
  173. }
  174. copy(b[2:7], nonceBytes[:])
  175. copy(b[7:11], tx.TokenID.Bytes())
  176. copy(b[11:13], amountFloat16.Bytes())
  177. toIdxBytes, err := tx.ToIdx.Bytes()
  178. if err != nil {
  179. return nil, tracerr.Wrap(err)
  180. }
  181. copy(b[13:19], toIdxBytes[:])
  182. fromIdxBytes, err := tx.FromIdx.Bytes()
  183. if err != nil {
  184. return nil, tracerr.Wrap(err)
  185. }
  186. copy(b[19:25], fromIdxBytes[:])
  187. bi := new(big.Int).SetBytes(b[:])
  188. return bi, nil
  189. }
  190. // RqTxCompressedDataV2 is like the TxCompressedDataV2 but using the 'Rq'
  191. // parameters. In a future iteration of the hermez-node, the 'Rq' parameters
  192. // can be inside a struct, which contains the 'Rq' transaction grouped inside,
  193. // so then computing the 'RqTxCompressedDataV2' would be just calling
  194. // 'tx.Rq.TxCompressedDataV2()'.
  195. // RqTxCompressedDataV2 spec:
  196. // [ 1 bits ] rqToBJJSign // 1 byte
  197. // [ 8 bits ] rqUserFee // 1 byte
  198. // [ 40 bits ] rqNonce // 5 bytes
  199. // [ 32 bits ] rqTokenID // 4 bytes
  200. // [ 16 bits ] rqAmountFloat16 // 2 bytes
  201. // [ 48 bits ] rqToIdx // 6 bytes
  202. // [ 48 bits ] rqFromIdx // 6 bytes
  203. // Total bits compressed data: 193 bits // 25 bytes in *big.Int representation
  204. func (tx *PoolL2Tx) RqTxCompressedDataV2() (*big.Int, error) {
  205. if tx.RqAmount == nil {
  206. tx.RqAmount = big.NewInt(0)
  207. }
  208. amountFloat16, err := NewFloat16(tx.RqAmount)
  209. if err != nil {
  210. return nil, tracerr.Wrap(err)
  211. }
  212. var b [25]byte
  213. toBJJSign := byte(0)
  214. if tx.RqToBJJ != nil && babyjub.PointCoordSign(tx.RqToBJJ.X) {
  215. toBJJSign = byte(1)
  216. }
  217. b[0] = toBJJSign
  218. b[1] = byte(tx.RqFee)
  219. nonceBytes, err := tx.RqNonce.Bytes()
  220. if err != nil {
  221. return nil, tracerr.Wrap(err)
  222. }
  223. copy(b[2:7], nonceBytes[:])
  224. copy(b[7:11], tx.RqTokenID.Bytes())
  225. copy(b[11:13], amountFloat16.Bytes())
  226. toIdxBytes, err := tx.RqToIdx.Bytes()
  227. if err != nil {
  228. return nil, tracerr.Wrap(err)
  229. }
  230. copy(b[13:19], toIdxBytes[:])
  231. fromIdxBytes, err := tx.RqFromIdx.Bytes()
  232. if err != nil {
  233. return nil, tracerr.Wrap(err)
  234. }
  235. copy(b[19:25], fromIdxBytes[:])
  236. bi := new(big.Int).SetBytes(b[:])
  237. return bi, nil
  238. }
  239. // HashToSign returns the computed Poseidon hash from the *PoolL2Tx that will be signed by the sender.
  240. func (tx *PoolL2Tx) HashToSign() (*big.Int, error) {
  241. toCompressedData, err := tx.TxCompressedData()
  242. if err != nil {
  243. return nil, tracerr.Wrap(err)
  244. }
  245. toEthAddr := EthAddrToBigInt(tx.ToEthAddr)
  246. rqToEthAddr := EthAddrToBigInt(tx.RqToEthAddr)
  247. toBJJY := big.NewInt(0)
  248. if tx.ToBJJ != nil {
  249. toBJJY = tx.ToBJJ.Y
  250. }
  251. rqTxCompressedDataV2, err := tx.RqTxCompressedDataV2()
  252. if err != nil {
  253. return nil, tracerr.Wrap(err)
  254. }
  255. rqToBJJY := big.NewInt(0)
  256. if tx.RqToBJJ != nil {
  257. rqToBJJY = tx.RqToBJJ.Y
  258. }
  259. return poseidon.Hash([]*big.Int{toCompressedData, toEthAddr, toBJJY, rqTxCompressedDataV2, rqToEthAddr, rqToBJJY})
  260. }
  261. // VerifySignature returns true if the signature verification is correct for the given PublicKey
  262. func (tx *PoolL2Tx) VerifySignature(pk *babyjub.PublicKey) bool {
  263. h, err := tx.HashToSign()
  264. if err != nil {
  265. return false
  266. }
  267. s, err := tx.Signature.Decompress()
  268. if err != nil {
  269. return false
  270. }
  271. return pk.VerifyPoseidon(h, s)
  272. }
  273. // L2Tx returns a *L2Tx from the PoolL2Tx
  274. func (tx PoolL2Tx) L2Tx() L2Tx {
  275. return L2Tx{
  276. TxID: tx.TxID,
  277. FromIdx: tx.FromIdx,
  278. ToIdx: tx.ToIdx,
  279. Amount: tx.Amount,
  280. Fee: tx.Fee,
  281. Nonce: tx.Nonce,
  282. Type: tx.Type,
  283. }
  284. }
  285. // Tx returns a *Tx from the PoolL2Tx
  286. func (tx PoolL2Tx) Tx() Tx {
  287. return Tx{
  288. TxID: tx.TxID,
  289. FromIdx: tx.FromIdx,
  290. ToIdx: tx.ToIdx,
  291. Amount: tx.Amount,
  292. TokenID: tx.TokenID,
  293. Nonce: &tx.Nonce,
  294. Fee: &tx.Fee,
  295. Type: tx.Type,
  296. }
  297. }
  298. // PoolL2TxsToL2Txs returns an array of []L2Tx from an array of []PoolL2Tx
  299. func PoolL2TxsToL2Txs(txs []PoolL2Tx) ([]L2Tx, error) {
  300. l2Txs := make([]L2Tx, len(txs))
  301. for i, poolTx := range txs {
  302. l2Txs[i] = poolTx.L2Tx()
  303. }
  304. return l2Txs, nil
  305. }
  306. // PoolL2TxState is a struct that represents the status of a L2 transaction
  307. type PoolL2TxState string
  308. const (
  309. // PoolL2TxStatePending represents a valid L2Tx that hasn't started the forging process
  310. PoolL2TxStatePending PoolL2TxState = "pend"
  311. // PoolL2TxStateForging represents a valid L2Tx that has started the forging process
  312. PoolL2TxStateForging PoolL2TxState = "fing"
  313. // PoolL2TxStateForged represents a L2Tx that has already been forged
  314. PoolL2TxStateForged PoolL2TxState = "fged"
  315. // PoolL2TxStateInvalid represents a L2Tx that has been invalidated
  316. PoolL2TxStateInvalid PoolL2TxState = "invl"
  317. )