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