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.

362 lines
11 KiB

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