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