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	"github.com/hermeznetwork/tracerr"
  8)
  9
 10// L2Tx is a struct that represents an already forged L2 tx
 11type L2Tx struct {
 12	// Stored in DB: mandatory fileds
 13	TxID        TxID        `meddler:"id"`
 14	BatchNum    BatchNum    `meddler:"batch_num"` // batchNum in which this tx was forged.
 15	Position    int         `meddler:"position"`
 16	FromIdx     Idx         `meddler:"from_idx"`
 17	ToIdx       Idx         `meddler:"to_idx"`
 18	Amount      *big.Int    `meddler:"amount,bigint"`
 19	Fee         FeeSelector `meddler:"fee"`
 20	Nonce       Nonce       `meddler:"nonce"`
 21	Type        TxType      `meddler:"type"`
 22	EthBlockNum int64       `meddler:"eth_block_num"` // Ethereum Block Number in which this L2Tx was added to the queue
 23}
 24
 25// NewL2Tx returns the given L2Tx with the TxId & Type parameters calculated
 26// from the L2Tx values
 27func NewL2Tx(tx *L2Tx) (*L2Tx, error) {
 28	txTypeOld := tx.Type
 29	if err := tx.SetType(); err != nil {
 30		return nil, tracerr.Wrap(err)
 31	}
 32	// If original Type doesn't match the correct one, return error
 33	if txTypeOld != "" && txTypeOld != tx.Type {
 34		return nil, tracerr.Wrap(fmt.Errorf("L2Tx.Type: %s, should be: %s",
 35			tx.Type, txTypeOld))
 36	}
 37
 38	txIDOld := tx.TxID
 39	if err := tx.SetID(); err != nil {
 40		return nil, tracerr.Wrap(err)
 41	}
 42	// If original TxID doesn't match the correct one, return error
 43	if txIDOld != (TxID{}) && txIDOld != tx.TxID {
 44		return tx, tracerr.Wrap(fmt.Errorf("L2Tx.TxID: %s, should be: %s",
 45			tx.TxID.String(), txIDOld.String()))
 46	}
 47
 48	return tx, nil
 49}
 50
 51// SetType sets the type of the transaction.  Uses (FromIdx, Nonce).
 52func (tx *L2Tx) SetType() error {
 53	if tx.ToIdx == Idx(1) {
 54		tx.Type = TxTypeExit
 55	} else if tx.ToIdx >= IdxUserThreshold {
 56		tx.Type = TxTypeTransfer
 57	} else {
 58		return tracerr.Wrap(fmt.Errorf(
 59			"cannot determine type of L2Tx, invalid ToIdx value: %d", tx.ToIdx))
 60	}
 61	return nil
 62}
 63
 64// SetID sets the ID of the transaction
 65func (tx *L2Tx) SetID() error {
 66	tx.TxID[0] = TxIDPrefixL2Tx
 67	fromIdxBytes, err := tx.FromIdx.Bytes()
 68	if err != nil {
 69		return tracerr.Wrap(err)
 70	}
 71	copy(tx.TxID[1:7], fromIdxBytes[:])
 72	nonceBytes, err := tx.Nonce.Bytes()
 73	if err != nil {
 74		return tracerr.Wrap(err)
 75	}
 76	copy(tx.TxID[7:12], nonceBytes[:])
 77	return nil
 78}
 79
 80// Tx returns a *Tx from the L2Tx
 81func (tx *L2Tx) Tx() *Tx {
 82	batchNum := new(BatchNum)
 83	*batchNum = tx.BatchNum
 84	fee := new(FeeSelector)
 85	*fee = tx.Fee
 86	nonce := new(Nonce)
 87	*nonce = tx.Nonce
 88	return &Tx{
 89		IsL1:        false,
 90		TxID:        tx.TxID,
 91		Type:        tx.Type,
 92		Position:    tx.Position,
 93		FromIdx:     tx.FromIdx,
 94		ToIdx:       tx.ToIdx,
 95		Amount:      tx.Amount,
 96		BatchNum:    batchNum,
 97		EthBlockNum: tx.EthBlockNum,
 98		Fee:         fee,
 99		Nonce:       nonce,
100	}
101}
102
103// PoolL2Tx returns the data structure of PoolL2Tx with the parameters of a
104// L2Tx filled
105func (tx L2Tx) PoolL2Tx() *PoolL2Tx {
106	return &PoolL2Tx{
107		TxID:    tx.TxID,
108		FromIdx: tx.FromIdx,
109		ToIdx:   tx.ToIdx,
110		Amount:  tx.Amount,
111		Fee:     tx.Fee,
112		Nonce:   tx.Nonce,
113		Type:    tx.Type,
114	}
115}
116
117// L2TxsToPoolL2Txs returns an array of []*PoolL2Tx from an array of []*L2Tx,
118// where the PoolL2Tx only have the parameters of a L2Tx filled.
119func L2TxsToPoolL2Txs(txs []L2Tx) []PoolL2Tx {
120	var r []PoolL2Tx
121	for _, tx := range txs {
122		r = append(r, *tx.PoolL2Tx())
123	}
124	return r
125}
126
127// BytesDataAvailability encodes a L2Tx into []byte for the Data Availability
128func (tx L2Tx) BytesDataAvailability(nLevels uint32) ([]byte, error) {
129	idxLen := nLevels / 8 //nolint:gomnd
130
131	b := make([]byte, ((nLevels*2)+16+8)/8) //nolint:gomnd
132
133	fromIdxBytes, err := tx.FromIdx.Bytes()
134	if err != nil {
135		return nil, tracerr.Wrap(err)
136	}
137	copy(b[0:idxLen], fromIdxBytes[6-idxLen:]) // [6-idxLen:] as is BigEndian
138
139	toIdxBytes, err := tx.ToIdx.Bytes()
140	if err != nil {
141		return nil, tracerr.Wrap(err)
142	}
143	copy(b[idxLen:idxLen*2], toIdxBytes[6-idxLen:])
144
145	amountFloat16, err := NewFloat16(tx.Amount)
146	if err != nil {
147		return nil, tracerr.Wrap(err)
148	}
149
150	copy(b[idxLen*2:idxLen*2+2], amountFloat16.Bytes())
151	b[idxLen*2+2] = byte(tx.Fee)
152
153	return b[:], nil
154}
155
156// L2TxFromBytesDataAvailability decodes a L2Tx from []byte (Data Availability)
157func L2TxFromBytesDataAvailability(b []byte, nLevels int) (*L2Tx, error) {
158	idxLen := nLevels / 8 //nolint:gomnd
159	tx := &L2Tx{}
160	var err error
161
162	var paddedFromIdxBytes [6]byte
163	copy(paddedFromIdxBytes[6-idxLen:], b[0:idxLen])
164	tx.FromIdx, err = IdxFromBytes(paddedFromIdxBytes[:])
165	if err != nil {
166		return nil, tracerr.Wrap(err)
167	}
168
169	var paddedToIdxBytes [6]byte
170	copy(paddedToIdxBytes[6-idxLen:6], b[idxLen:idxLen*2])
171	tx.ToIdx, err = IdxFromBytes(paddedToIdxBytes[:])
172	if err != nil {
173		return nil, tracerr.Wrap(err)
174	}
175
176	tx.Amount = Float16FromBytes(b[idxLen*2 : idxLen*2+2]).BigInt()
177	tx.Fee = FeeSelector(b[idxLen*2+2])
178	return tx, nil
179}