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