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