hermez-node/common/l1tx.go
common/l1tx.goRaw
1package common
2
3import (
4 "encoding/binary"
5 "fmt"
6 "math/big"
7
8 ethCommon "github.com/ethereum/go-ethereum/common"
9 "github.com/iden3/go-iden3-crypto/babyjub"
10)
11
12const (
13 // L1TxBytesLen is the length of the byte array that represents the L1Tx
14 L1TxBytesLen = 72
15)
16
17// L1Tx is a struct that represents a L1 tx
18type L1Tx struct {
19 // Stored in DB: mandatory fileds
20
21 // TxID (12 bytes) for L1Tx is:
22 // bytes: | 1 | 8 | 2 | 1 |
23 // values: | type | ToForgeL1TxsNum | Position | 0 (padding) |
24 // where type:
25 // - L1UserTx: 0
26 // - L1CoordinatorTx: 1
27 TxID TxID
28 ToForgeL1TxsNum *int64 // toForgeL1TxsNum in which the tx was forged / will be forged
29 Position int
30 UserOrigin bool // true if the tx was originated by a user, false if it was aoriginated by a coordinator. Note that this differ from the spec for implementation simplification purpposes
31 FromIdx *Idx // FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.LoadAmount (deposit)
32 FromEthAddr ethCommon.Address
33 FromBJJ *babyjub.PublicKey
34 ToIdx Idx // ToIdx is ignored in L1Tx/Deposit, but used in the L1Tx/DepositAndTransfer
35 TokenID TokenID
36 Amount *big.Int
37 LoadAmount *big.Int
38 EthBlockNum int64 // Ethereum Block Number in which this L1Tx was added to the queue
39 Type TxType
40 BatchNum *BatchNum
41 USD *float64
42 LoadAmountUSD *float64
43}
44
45// NewL1Tx returns the given L1Tx with the TxId & Type parameters calculated
46// from the L1Tx values
47func NewL1Tx(l1Tx *L1Tx) (*L1Tx, error) {
48 // calculate TxType
49 var txType TxType
50 if l1Tx.FromIdx == nil {
51 if l1Tx.ToIdx == Idx(0) {
52 txType = TxTypeCreateAccountDeposit
53 } else if l1Tx.ToIdx >= IdxUserThreshold {
54 txType = TxTypeCreateAccountDepositTransfer
55 } else {
56 return l1Tx, fmt.Errorf("Can not determine type of L1Tx, invalid ToIdx value: %d", l1Tx.ToIdx)
57 }
58 } else if *l1Tx.FromIdx >= IdxUserThreshold {
59 if l1Tx.ToIdx == Idx(0) {
60 txType = TxTypeDeposit
61 } else if l1Tx.ToIdx == Idx(1) {
62 txType = TxTypeExit
63 } else if l1Tx.ToIdx >= IdxUserThreshold {
64 if l1Tx.LoadAmount.Int64() == int64(0) {
65 txType = TxTypeForceTransfer
66 } else {
67 txType = TxTypeDepositTransfer
68 }
69 } else {
70 return l1Tx, fmt.Errorf("Can not determine type of L1Tx, invalid ToIdx value: %d", l1Tx.ToIdx)
71 }
72 } else {
73 return l1Tx, fmt.Errorf("Can not determine type of L1Tx, invalid FromIdx value: %d", l1Tx.FromIdx)
74 }
75
76 if l1Tx.Type != "" && l1Tx.Type != txType {
77 return l1Tx, fmt.Errorf("L1Tx.Type: %s, should be: %s", l1Tx.Type, txType)
78 }
79 l1Tx.Type = txType
80
81 var txid [TxIDLen]byte
82 if !l1Tx.UserOrigin {
83 txid[0] = TxIDPrefixL1CoordTx
84 }
85 var toForgeL1TxsNumBytes [8]byte
86 var toForge uint64 = 0
87 if l1Tx.ToForgeL1TxsNum != nil {
88 toForge = uint64(*l1Tx.ToForgeL1TxsNum)
89 }
90 binary.BigEndian.PutUint64(toForgeL1TxsNumBytes[:], toForge)
91 copy(txid[1:9], toForgeL1TxsNumBytes[:])
92
93 var positionBytes [2]byte
94 binary.BigEndian.PutUint16(positionBytes[:], uint16(l1Tx.Position))
95 copy(txid[9:11], positionBytes[:])
96 l1Tx.TxID = TxID(txid)
97
98 return l1Tx, nil
99}
100
101// Tx returns a *Tx from the L1Tx
102func (tx *L1Tx) Tx() *Tx {
103 f := new(big.Float).SetInt(tx.Amount)
104 amountFloat, _ := f.Float64()
105 userOrigin := new(bool)
106 *userOrigin = tx.UserOrigin
107 fromEthAddr := new(ethCommon.Address)
108 *fromEthAddr = tx.FromEthAddr
109 toIdx := new(Idx)
110 *toIdx = tx.ToIdx
111 genericTx := &Tx{
112 IsL1: true,
113 TxID: tx.TxID,
114 Type: tx.Type,
115 Position: tx.Position,
116 FromIdx: tx.FromIdx,
117 ToIdx: toIdx,
118 Amount: tx.Amount,
119 AmountFloat: amountFloat,
120 TokenID: tx.TokenID,
121 ToForgeL1TxsNum: tx.ToForgeL1TxsNum,
122 UserOrigin: userOrigin,
123 FromEthAddr: fromEthAddr,
124 FromBJJ: tx.FromBJJ,
125 LoadAmount: tx.LoadAmount,
126 EthBlockNum: tx.EthBlockNum,
127 USD: tx.USD,
128 LoadAmountUSD: tx.LoadAmountUSD,
129 }
130 if tx.LoadAmount != nil {
131 lf := new(big.Float).SetInt(tx.LoadAmount)
132 loadAmountFloat, _ := lf.Float64()
133 genericTx.LoadAmountFloat = &loadAmountFloat
134 }
135 return genericTx
136}
137
138// Bytes encodes a L1Tx into []byte
139func (tx *L1Tx) Bytes(nLevels int) ([]byte, error) {
140 var b [L1TxBytesLen]byte
141 copy(b[0:20], tx.FromEthAddr.Bytes())
142 pkComp := tx.FromBJJ.Compress()
143 copy(b[20:52], pkComp[:])
144 fromIdxBytes, err := tx.FromIdx.Bytes()
145 if err != nil {
146 return nil, err
147 }
148 copy(b[52:58], fromIdxBytes[:])
149 loadAmountFloat16, err := NewFloat16(tx.LoadAmount)
150 if err != nil {
151 return nil, err
152 }
153 copy(b[58:60], loadAmountFloat16.Bytes())
154 amountFloat16, err := NewFloat16(tx.Amount)
155 if err != nil {
156 return nil, err
157 }
158 copy(b[60:62], amountFloat16.Bytes())
159 copy(b[62:66], tx.TokenID.Bytes())
160 toIdxBytes, err := tx.ToIdx.Bytes()
161 if err != nil {
162 return nil, err
163 }
164 copy(b[66:72], toIdxBytes[:])
165 return b[:], nil
166}
167
168// L1TxFromBytes decodes a L1Tx from []byte
169func L1TxFromBytes(b []byte) (*L1Tx, error) {
170 if len(b) != L1TxBytesLen {
171 return nil, fmt.Errorf("Can not parse L1Tx bytes, expected length %d, current: %d", 68, len(b))
172 }
173
174 tx := &L1Tx{}
175 var err error
176 tx.FromEthAddr = ethCommon.BytesToAddress(b[0:20])
177 pkCompB := b[20:52]
178 var pkComp babyjub.PublicKeyComp
179 copy(pkComp[:], pkCompB)
180 tx.FromBJJ, err = pkComp.Decompress()
181 if err != nil {
182 return nil, err
183 }
184 fromIdx, err := IdxFromBytes(b[52:58])
185 if err != nil {
186 return nil, err
187 }
188 if fromIdx != 0 {
189 tx.FromIdx = new(Idx)
190 *tx.FromIdx = fromIdx
191 }
192 tx.LoadAmount = Float16FromBytes(b[58:60]).BigInt()
193 tx.Amount = Float16FromBytes(b[60:62]).BigInt()
194 tx.TokenID, err = TokenIDFromBytes(b[62:66])
195 if err != nil {
196 return nil, err
197 }
198 tx.ToIdx, err = IdxFromBytes(b[66:72])
199 if err != nil {
200 return nil, err
201 }
202
203 return tx, nil
204}