arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/common/l2tx.go
common/l2tx.goRaw
  1package common
  2
  3import (
  4	"fmt"
  5	"math/big"
  6
  7	ethCrypto "github.com/ethereum/go-ethereum/crypto"
  8	"github.com/hermeznetwork/tracerr"
  9)
 10
 11// L2Tx is a struct that represents an already forged L2 tx
 12type L2Tx struct {
 13	// Stored in DB: mandatory fields
 14	TxID     TxID     `meddler:"id"`
 15	BatchNum BatchNum `meddler:"batch_num"` // batchNum in which this tx was forged.
 16	Position int      `meddler:"position"`
 17	FromIdx  Idx      `meddler:"from_idx"`
 18	ToIdx    Idx      `meddler:"to_idx"`
 19	// TokenID is filled by the TxProcessor
 20	TokenID TokenID     `meddler:"token_id"`
 21	Amount  *big.Int    `meddler:"amount,bigint"`
 22	Fee     FeeSelector `meddler:"fee"`
 23	// Nonce is filled by the TxProcessor
 24	Nonce Nonce  `meddler:"nonce"`
 25	Type  TxType `meddler:"type"`
 26	// EthBlockNum in which this L2Tx was added to the queue
 27	EthBlockNum int64 `meddler:"eth_block_num"`
 28}
 29
 30// NewL2Tx returns the given L2Tx with the TxId & Type parameters calculated
 31// from the L2Tx values
 32func NewL2Tx(tx *L2Tx) (*L2Tx, error) {
 33	txTypeOld := tx.Type
 34	if err := tx.SetType(); err != nil {
 35		return nil, tracerr.Wrap(err)
 36	}
 37	// If original Type doesn't match the correct one, return error
 38	if txTypeOld != "" && txTypeOld != tx.Type {
 39		return nil, tracerr.Wrap(fmt.Errorf("L2Tx.Type: %s, should be: %s",
 40			tx.Type, txTypeOld))
 41	}
 42
 43	txIDOld := tx.TxID
 44	if err := tx.SetID(); err != nil {
 45		return nil, tracerr.Wrap(err)
 46	}
 47	// If original TxID doesn't match the correct one, return error
 48	if txIDOld != (TxID{}) && txIDOld != tx.TxID {
 49		return tx, tracerr.Wrap(fmt.Errorf("L2Tx.TxID: %s, should be: %s",
 50			tx.TxID.String(), txIDOld.String()))
 51	}
 52
 53	return tx, nil
 54}
 55
 56// SetType sets the type of the transaction.  Uses (FromIdx, Nonce).
 57func (tx *L2Tx) SetType() error {
 58	if tx.ToIdx == Idx(1) {
 59		tx.Type = TxTypeExit
 60	} else if tx.ToIdx >= IdxUserThreshold {
 61		tx.Type = TxTypeTransfer
 62	} else {
 63		return tracerr.Wrap(fmt.Errorf(
 64			"cannot determine type of L2Tx, invalid ToIdx value: %d", tx.ToIdx))
 65	}
 66	return nil
 67}
 68
 69// SetID sets the ID of the transaction
 70func (tx *L2Tx) SetID() error {
 71	txID, err := tx.CalculateTxID()
 72	if err != nil {
 73		return err
 74	}
 75	tx.TxID = txID
 76	return nil
 77}
 78
 79// CalculateTxID returns the TxID of the transaction. This method is used to
 80// set the TxID for L2Tx and for PoolL2Tx.
 81func (tx L2Tx) CalculateTxID() ([TxIDLen]byte, error) {
 82	var txID TxID
 83	var b []byte
 84	// FromIdx
 85	fromIdxBytes, err := tx.FromIdx.Bytes()
 86	if err != nil {
 87		return txID, tracerr.Wrap(err)
 88	}
 89	b = append(b, fromIdxBytes[:]...)
 90	// TokenID
 91	b = append(b, tx.TokenID.Bytes()[:]...)
 92	// Amount
 93	amountFloat40, err := NewFloat40(tx.Amount)
 94	if err != nil {
 95		return txID, tracerr.Wrap(fmt.Errorf("%s: %d", err, tx.Amount))
 96	}
 97	amountFloat40Bytes, err := amountFloat40.Bytes()
 98	if err != nil {
 99		return txID, tracerr.Wrap(err)
100	}
101	b = append(b, amountFloat40Bytes...)
102	// Nonce
103	nonceBytes, err := tx.Nonce.Bytes()
104	if err != nil {
105		return txID, tracerr.Wrap(err)
106	}
107	b = append(b, nonceBytes[:]...)
108	// Fee
109	b = append(b, byte(tx.Fee))
110
111	// calculate hash
112	h := ethCrypto.Keccak256Hash(b).Bytes()
113
114	txID[0] = TxIDPrefixL2Tx
115	copy(txID[1:], h)
116	return txID, nil
117}
118
119// Tx returns a *Tx from the L2Tx
120func (tx *L2Tx) Tx() *Tx {
121	batchNum := new(BatchNum)
122	*batchNum = tx.BatchNum
123	fee := new(FeeSelector)
124	*fee = tx.Fee
125	nonce := new(Nonce)
126	*nonce = tx.Nonce
127	return &Tx{
128		IsL1:        false,
129		TxID:        tx.TxID,
130		Type:        tx.Type,
131		Position:    tx.Position,
132		FromIdx:     tx.FromIdx,
133		ToIdx:       tx.ToIdx,
134		TokenID:     tx.TokenID,
135		Amount:      tx.Amount,
136		BatchNum:    batchNum,
137		EthBlockNum: tx.EthBlockNum,
138		Fee:         fee,
139		Nonce:       nonce,
140	}
141}
142
143// PoolL2Tx returns the data structure of PoolL2Tx with the parameters of a
144// L2Tx filled
145func (tx L2Tx) PoolL2Tx() *PoolL2Tx {
146	return &PoolL2Tx{
147		TxID:    tx.TxID,
148		FromIdx: tx.FromIdx,
149		ToIdx:   tx.ToIdx,
150		TokenID: tx.TokenID,
151		Amount:  tx.Amount,
152		Fee:     tx.Fee,
153		Nonce:   tx.Nonce,
154		Type:    tx.Type,
155	}
156}
157
158// L2TxsToPoolL2Txs returns an array of []*PoolL2Tx from an array of []*L2Tx,
159// where the PoolL2Tx only have the parameters of a L2Tx filled.
160func L2TxsToPoolL2Txs(txs []L2Tx) []PoolL2Tx {
161	var r []PoolL2Tx
162	for _, tx := range txs {
163		r = append(r, *tx.PoolL2Tx())
164	}
165	return r
166}
167
168// TxIDsFromL2Txs returns an array of TxID from the []L2Tx
169func TxIDsFromL2Txs(txs []L2Tx) []TxID {
170	txIDs := make([]TxID, len(txs))
171	for i, tx := range txs {
172		txIDs[i] = tx.TxID
173	}
174	return txIDs
175}
176
177// BytesDataAvailability encodes a L2Tx into []byte for the Data Availability
178// [ fromIdx | toIdx | amountFloat40 | Fee ]
179func (tx L2Tx) BytesDataAvailability(nLevels uint32) ([]byte, error) {
180	idxLen := nLevels / 8 //nolint:gomnd
181
182	b := make([]byte, ((nLevels*2)+40+8)/8) //nolint:gomnd
183
184	fromIdxBytes, err := tx.FromIdx.Bytes()
185	if err != nil {
186		return nil, tracerr.Wrap(err)
187	}
188	copy(b[0:idxLen], fromIdxBytes[6-idxLen:]) // [6-idxLen:] as is BigEndian
189
190	toIdxBytes, err := tx.ToIdx.Bytes()
191	if err != nil {
192		return nil, tracerr.Wrap(err)
193	}
194	copy(b[idxLen:idxLen*2], toIdxBytes[6-idxLen:])
195
196	amountFloat40, err := NewFloat40(tx.Amount)
197	if err != nil {
198		return nil, tracerr.Wrap(err)
199	}
200	amountFloat40Bytes, err := amountFloat40.Bytes()
201	if err != nil {
202		return nil, tracerr.Wrap(err)
203	}
204	copy(b[idxLen*2:idxLen*2+Float40BytesLength], amountFloat40Bytes)
205	b[idxLen*2+Float40BytesLength] = byte(tx.Fee)
206
207	return b[:], nil
208}
209
210// L2TxFromBytesDataAvailability decodes a L2Tx from []byte (Data Availability)
211func L2TxFromBytesDataAvailability(b []byte, nLevels int) (*L2Tx, error) {
212	idxLen := nLevels / 8 //nolint:gomnd
213	tx := &L2Tx{}
214	var err error
215
216	var paddedFromIdxBytes [6]byte
217	copy(paddedFromIdxBytes[6-idxLen:], b[0:idxLen])
218	tx.FromIdx, err = IdxFromBytes(paddedFromIdxBytes[:])
219	if err != nil {
220		return nil, tracerr.Wrap(err)
221	}
222
223	var paddedToIdxBytes [6]byte
224	copy(paddedToIdxBytes[6-idxLen:6], b[idxLen:idxLen*2])
225	tx.ToIdx, err = IdxFromBytes(paddedToIdxBytes[:])
226	if err != nil {
227		return nil, tracerr.Wrap(err)
228	}
229
230	tx.Amount, err = Float40FromBytes(b[idxLen*2 : idxLen*2+Float40BytesLength]).BigInt()
231	if err != nil {
232		return nil, tracerr.Wrap(err)
233	}
234	tx.Fee = FeeSelector(b[idxLen*2+Float40BytesLength])
235	return tx, nil
236}