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.

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