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 == Idx(0) {
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 binary.BigEndian.PutUint64(toForgeL1TxsNumBytes[:], uint64(l1Tx.ToForgeL1TxsNum))
87 copy(txid[1:9], toForgeL1TxsNumBytes[:])
88
89 var positionBytes [2]byte
90 binary.BigEndian.PutUint16(positionBytes[:], uint16(l1Tx.Position))
91 copy(txid[9:11], positionBytes[:])
92 l1Tx.TxID = TxID(txid)
93
94 return l1Tx, nil
95}
96
97// Tx returns a *Tx from the L1Tx
98func (tx *L1Tx) Tx() *Tx {
99 f := new(big.Float).SetInt(tx.Amount)
100 amountFloat, _ := f.Float64()
101 genericTx := &Tx{
102 IsL1: true,
103 TxID: tx.TxID,
104 Type: tx.Type,
105 Position: tx.Position,
106 FromIdx: tx.FromIdx,
107 ToIdx: tx.ToIdx,
108 Amount: tx.Amount,
109 AmountFloat: amountFloat,
110 TokenID: tx.TokenID,
111 ToForgeL1TxsNum: tx.ToForgeL1TxsNum,
112 UserOrigin: tx.UserOrigin,
113 FromEthAddr: tx.FromEthAddr,
114 FromBJJ: tx.FromBJJ,
115 LoadAmount: tx.LoadAmount,
116 EthBlockNum: tx.EthBlockNum,
117 USD: tx.USD,
118 LoadAmountUSD: tx.LoadAmountUSD,
119 }
120 if tx.LoadAmount != nil {
121 lf := new(big.Float).SetInt(tx.LoadAmount)
122 loadAmountFloat, _ := lf.Float64()
123 genericTx.LoadAmountFloat = &loadAmountFloat
124 }
125 return genericTx
126}
127
128// Bytes encodes a L1Tx into []byte
129func (tx *L1Tx) Bytes(nLevels int) ([]byte, error) {
130 var b [L1TxBytesLen]byte
131 copy(b[0:20], tx.FromEthAddr.Bytes())
132 pkComp := tx.FromBJJ.Compress()
133 copy(b[20:52], pkComp[:])
134 fromIdxBytes, err := tx.FromIdx.Bytes()
135 if err != nil {
136 return nil, err
137 }
138 copy(b[52:58], fromIdxBytes[:])
139 loadAmountFloat16, err := NewFloat16(tx.LoadAmount)
140 if err != nil {
141 return nil, err
142 }
143 copy(b[58:60], loadAmountFloat16.Bytes())
144 amountFloat16, err := NewFloat16(tx.Amount)
145 if err != nil {
146 return nil, err
147 }
148 copy(b[60:62], amountFloat16.Bytes())
149 copy(b[62:66], tx.TokenID.Bytes())
150 toIdxBytes, err := tx.ToIdx.Bytes()
151 if err != nil {
152 return nil, err
153 }
154 copy(b[66:72], toIdxBytes[:])
155 return b[:], nil
156}
157
158// L1TxFromBytes decodes a L1Tx from []byte
159func L1TxFromBytes(b []byte) (*L1Tx, error) {
160 if len(b) != L1TxBytesLen {
161 return nil, fmt.Errorf("Can not parse L1Tx bytes, expected length %d, current: %d", 68, len(b))
162 }
163
164 tx := &L1Tx{}
165 var err error
166 tx.FromEthAddr = ethCommon.BytesToAddress(b[0:20])
167 pkCompB := b[20:52]
168 var pkComp babyjub.PublicKeyComp
169 copy(pkComp[:], pkCompB)
170 tx.FromBJJ, err = pkComp.Decompress()
171 if err != nil {
172 return nil, err
173 }
174 tx.FromIdx, err = IdxFromBytes(b[52:58])
175 if err != nil {
176 return nil, err
177 }
178 tx.LoadAmount = Float16FromBytes(b[58:60]).BigInt()
179 tx.Amount = Float16FromBytes(b[60:62]).BigInt()
180 tx.TokenID, err = TokenIDFromBytes(b[62:66])
181 if err != nil {
182 return nil, err
183 }
184 tx.ToIdx, err = IdxFromBytes(b[66:72])
185 if err != nil {
186 return nil, err
187 }
188
189 return tx, nil
190}