arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
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	ethCrypto "github.com/ethereum/go-ethereum/crypto"
 10	"github.com/hermeznetwork/tracerr"
 11	"github.com/iden3/go-iden3-crypto/babyjub"
 12)
 13
 14// L1Tx is a struct that represents a L1 tx
 15type L1Tx struct {
 16	// Stored in DB: mandatory fileds
 17
 18	// TxID (32 bytes) for L1Tx is the Keccak256 (ethereum) hash of:
 19	// bytes:  |  1   |        8        |    2     |      1      |
 20	// values: | type | ToForgeL1TxsNum | Position | 0 (padding) |
 21	// where type:
 22	// 	- L1UserTx: 0
 23	// 	- L1CoordinatorTx: 1
 24	TxID TxID `meddler:"id"`
 25	// ToForgeL1TxsNum indicates in which the tx was forged / will be forged
 26	ToForgeL1TxsNum *int64 `meddler:"to_forge_l1_txs_num"`
 27	Position        int    `meddler:"position"`
 28	// UserOrigin is set to true if the tx was originated by a user, false if it was
 29	// aoriginated by a coordinator. Note that this differ from the spec for implementation
 30	// simplification purpposes
 31	UserOrigin bool `meddler:"user_origin"`
 32	// FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.DepositAmount
 33	// (deposit)
 34	FromIdx          Idx                   `meddler:"from_idx,zeroisnull"`
 35	EffectiveFromIdx Idx                   `meddler:"effective_from_idx,zeroisnull"`
 36	FromEthAddr      ethCommon.Address     `meddler:"from_eth_addr,zeroisnull"`
 37	FromBJJ          babyjub.PublicKeyComp `meddler:"from_bjj,zeroisnull"`
 38	// ToIdx is ignored in L1Tx/Deposit, but used in the L1Tx/DepositAndTransfer
 39	ToIdx   Idx      `meddler:"to_idx"`
 40	TokenID TokenID  `meddler:"token_id"`
 41	Amount  *big.Int `meddler:"amount,bigint"`
 42	// EffectiveAmount only applies to L1UserTx.
 43	EffectiveAmount *big.Int `meddler:"effective_amount,bigintnull"`
 44	DepositAmount   *big.Int `meddler:"deposit_amount,bigint"`
 45	// EffectiveDepositAmount only applies to L1UserTx.
 46	EffectiveDepositAmount *big.Int `meddler:"effective_deposit_amount,bigintnull"`
 47	// Ethereum Block Number in which this L1Tx was added to the queue
 48	EthBlockNum int64     `meddler:"eth_block_num"`
 49	Type        TxType    `meddler:"type"`
 50	BatchNum    *BatchNum `meddler:"batch_num"`
 51}
 52
 53// NewL1Tx returns the given L1Tx with the TxId & Type parameters calculated
 54// from the L1Tx values
 55func NewL1Tx(tx *L1Tx) (*L1Tx, error) {
 56	txTypeOld := tx.Type
 57	if err := tx.SetType(); err != nil {
 58		return nil, tracerr.Wrap(err)
 59	}
 60	// If original Type doesn't match the correct one, return error
 61	if txTypeOld != "" && txTypeOld != tx.Type {
 62		return nil, tracerr.Wrap(fmt.Errorf("L1Tx.Type: %s, should be: %s",
 63			tx.Type, txTypeOld))
 64	}
 65
 66	txIDOld := tx.TxID
 67	if err := tx.SetID(); err != nil {
 68		return nil, tracerr.Wrap(err)
 69	}
 70	// If original TxID doesn't match the correct one, return error
 71	if txIDOld != (TxID{}) && txIDOld != tx.TxID {
 72		return tx, tracerr.Wrap(fmt.Errorf("L1Tx.TxID: %s, should be: %s",
 73			tx.TxID.String(), txIDOld.String()))
 74	}
 75
 76	return tx, nil
 77}
 78
 79// SetType sets the type of the transaction
 80func (tx *L1Tx) SetType() error {
 81	if tx.FromIdx == 0 {
 82		if tx.ToIdx == Idx(0) {
 83			tx.Type = TxTypeCreateAccountDeposit
 84		} else if tx.ToIdx >= IdxUserThreshold {
 85			tx.Type = TxTypeCreateAccountDepositTransfer
 86		} else {
 87			return tracerr.Wrap(fmt.Errorf(
 88				"Can not determine type of L1Tx, invalid ToIdx value: %d", tx.ToIdx))
 89		}
 90	} else if tx.FromIdx >= IdxUserThreshold {
 91		if tx.ToIdx == Idx(0) {
 92			tx.Type = TxTypeDeposit
 93		} else if tx.ToIdx == Idx(1) {
 94			tx.Type = TxTypeForceExit
 95		} else if tx.ToIdx >= IdxUserThreshold {
 96			if tx.DepositAmount.Int64() == int64(0) {
 97				tx.Type = TxTypeForceTransfer
 98			} else {
 99				tx.Type = TxTypeDepositTransfer
100			}
101		} else {
102			return tracerr.Wrap(fmt.Errorf(
103				"Can not determine type of L1Tx, invalid ToIdx value: %d", tx.ToIdx))
104		}
105	} else {
106		return tracerr.Wrap(fmt.Errorf(
107			"Can not determine type of L1Tx, invalid FromIdx value: %d", tx.FromIdx))
108	}
109	return nil
110}
111
112// SetID sets the ID of the transaction.  For L1UserTx uses (ToForgeL1TxsNum,
113// Position), for L1CoordinatorTx uses (BatchNum, Position).
114func (tx *L1Tx) SetID() error {
115	var b []byte
116	if tx.UserOrigin {
117		if tx.ToForgeL1TxsNum == nil {
118			return tracerr.Wrap(fmt.Errorf("L1Tx.UserOrigin == true && L1Tx.ToForgeL1TxsNum == nil"))
119		}
120		tx.TxID[0] = TxIDPrefixL1UserTx
121
122		var toForgeL1TxsNumBytes [8]byte
123		binary.BigEndian.PutUint64(toForgeL1TxsNumBytes[:], uint64(*tx.ToForgeL1TxsNum))
124		b = append(b, toForgeL1TxsNumBytes[:]...)
125	} else {
126		if tx.BatchNum == nil {
127			return tracerr.Wrap(fmt.Errorf("L1Tx.UserOrigin == false && L1Tx.BatchNum == nil"))
128		}
129		tx.TxID[0] = TxIDPrefixL1CoordTx
130
131		var batchNumBytes [8]byte
132		binary.BigEndian.PutUint64(batchNumBytes[:], uint64(*tx.BatchNum))
133		b = append(b, batchNumBytes[:]...)
134	}
135	var positionBytes [2]byte
136	binary.BigEndian.PutUint16(positionBytes[:], uint16(tx.Position))
137	b = append(b, positionBytes[:]...)
138
139	// calculate hash
140	h := ethCrypto.Keccak256Hash(b).Bytes()
141
142	copy(tx.TxID[1:], h)
143
144	return nil
145}
146
147// Tx returns a *Tx from the L1Tx
148func (tx L1Tx) Tx() Tx {
149	f := new(big.Float).SetInt(tx.EffectiveAmount)
150	amountFloat, _ := f.Float64()
151	userOrigin := new(bool)
152	*userOrigin = tx.UserOrigin
153	genericTx := Tx{
154		IsL1:            true,
155		TxID:            tx.TxID,
156		Type:            tx.Type,
157		Position:        tx.Position,
158		FromIdx:         tx.FromIdx,
159		ToIdx:           tx.ToIdx,
160		Amount:          tx.EffectiveAmount,
161		AmountFloat:     amountFloat,
162		TokenID:         tx.TokenID,
163		ToForgeL1TxsNum: tx.ToForgeL1TxsNum,
164		UserOrigin:      userOrigin,
165		FromEthAddr:     tx.FromEthAddr,
166		FromBJJ:         tx.FromBJJ,
167		DepositAmount:   tx.EffectiveDepositAmount,
168		EthBlockNum:     tx.EthBlockNum,
169	}
170	if tx.DepositAmount != nil {
171		lf := new(big.Float).SetInt(tx.DepositAmount)
172		depositAmountFloat, _ := lf.Float64()
173		genericTx.DepositAmountFloat = &depositAmountFloat
174	}
175	return genericTx
176}
177
178// TxCompressedData spec:
179// [ 1 bits  ] empty (toBJJSign) // 1 byte
180// [ 8 bits  ] empty (userFee) // 1 byte
181// [ 40 bits ] empty (nonce) // 5 bytes
182// [ 32 bits ] tokenID // 4 bytes
183// [ 48 bits ] toIdx // 6 bytes
184// [ 48 bits ] fromIdx // 6 bytes
185// [ 16 bits ] chainId // 2 bytes
186// [ 32 bits ] empty (signatureConstant) // 4 bytes
187// Total bits compressed data:  225 bits // 29 bytes in *big.Int representation
188func (tx L1Tx) TxCompressedData(chainID uint16) (*big.Int, error) {
189	var b [29]byte
190	// b[0:7] empty: no ToBJJSign, no fee, no nonce
191	copy(b[7:11], tx.TokenID.Bytes())
192	toIdxBytes, err := tx.ToIdx.Bytes()
193	if err != nil {
194		return nil, tracerr.Wrap(err)
195	}
196	copy(b[11:17], toIdxBytes[:])
197	fromIdxBytes, err := tx.FromIdx.Bytes()
198	if err != nil {
199		return nil, tracerr.Wrap(err)
200	}
201	copy(b[17:23], fromIdxBytes[:])
202	binary.BigEndian.PutUint16(b[23:25], chainID)
203	copy(b[25:29], SignatureConstantBytes[:])
204
205	bi := new(big.Int).SetBytes(b[:])
206	return bi, nil
207}
208
209// BytesDataAvailability encodes a L1Tx into []byte for the Data Availability
210// [ fromIdx | toIdx | amountFloat40 | Fee ]
211func (tx *L1Tx) BytesDataAvailability(nLevels uint32) ([]byte, error) {
212	idxLen := nLevels / 8 //nolint:gomnd
213
214	b := make([]byte, ((nLevels*2)+40+8)/8) //nolint:gomnd
215
216	fromIdxBytes, err := tx.FromIdx.Bytes()
217	if err != nil {
218		return nil, tracerr.Wrap(err)
219	}
220	copy(b[0:idxLen], fromIdxBytes[6-idxLen:])
221	toIdxBytes, err := tx.ToIdx.Bytes()
222	if err != nil {
223		return nil, tracerr.Wrap(err)
224	}
225	copy(b[idxLen:idxLen*2], toIdxBytes[6-idxLen:])
226
227	if tx.EffectiveAmount != nil {
228		amountFloat40, err := NewFloat40(tx.EffectiveAmount)
229		if err != nil {
230			return nil, tracerr.Wrap(err)
231		}
232		amountFloat40Bytes, err := amountFloat40.Bytes()
233		if err != nil {
234			return nil, tracerr.Wrap(err)
235		}
236		copy(b[idxLen*2:idxLen*2+Float40BytesLength], amountFloat40Bytes)
237	}
238	// fee = 0 (as is L1Tx)
239	return b[:], nil
240}
241
242// L1TxFromDataAvailability decodes a L1Tx from []byte (Data Availability)
243func L1TxFromDataAvailability(b []byte, nLevels uint32) (*L1Tx, error) {
244	idxLen := nLevels / 8 //nolint:gomnd
245
246	fromIdxBytes := b[0:idxLen]
247	toIdxBytes := b[idxLen : idxLen*2]
248	amountBytes := b[idxLen*2 : idxLen*2+Float40BytesLength]
249
250	l1tx := L1Tx{}
251	fromIdx, err := IdxFromBytes(ethCommon.LeftPadBytes(fromIdxBytes, 6))
252	if err != nil {
253		return nil, tracerr.Wrap(err)
254	}
255	l1tx.FromIdx = fromIdx
256	toIdx, err := IdxFromBytes(ethCommon.LeftPadBytes(toIdxBytes, 6))
257	if err != nil {
258		return nil, tracerr.Wrap(err)
259	}
260	l1tx.ToIdx = toIdx
261	l1tx.EffectiveAmount, err = Float40FromBytes(amountBytes).BigInt()
262	return &l1tx, tracerr.Wrap(err)
263}
264
265// BytesGeneric returns the generic representation of a L1Tx. This method is
266// used to compute the []byte representation of a L1UserTx, and also to compute
267// the L1TxData for the ZKInputs (at the HashGlobalInputs), using this method
268// for L1CoordinatorTxs & L1UserTxs (for the ZKInputs case).
269func (tx *L1Tx) BytesGeneric() ([]byte, error) {
270	var b [RollupConstL1UserTotalBytes]byte
271	copy(b[0:20], tx.FromEthAddr.Bytes())
272	if tx.FromBJJ != EmptyBJJComp {
273		pkCompL := tx.FromBJJ
274		pkCompB := SwapEndianness(pkCompL[:])
275		copy(b[20:52], pkCompB[:])
276	}
277	fromIdxBytes, err := tx.FromIdx.Bytes()
278	if err != nil {
279		return nil, tracerr.Wrap(err)
280	}
281	copy(b[52:58], fromIdxBytes[:])
282
283	depositAmountFloat40, err := NewFloat40(tx.DepositAmount)
284	if err != nil {
285		return nil, tracerr.Wrap(err)
286	}
287	depositAmountFloat40Bytes, err := depositAmountFloat40.Bytes()
288	if err != nil {
289		return nil, tracerr.Wrap(err)
290	}
291	copy(b[58:63], depositAmountFloat40Bytes)
292
293	amountFloat40, err := NewFloat40(tx.Amount)
294	if err != nil {
295		return nil, tracerr.Wrap(err)
296	}
297	amountFloat40Bytes, err := amountFloat40.Bytes()
298	if err != nil {
299		return nil, tracerr.Wrap(err)
300	}
301	copy(b[63:68], amountFloat40Bytes)
302
303	copy(b[68:72], tx.TokenID.Bytes())
304	toIdxBytes, err := tx.ToIdx.Bytes()
305	if err != nil {
306		return nil, tracerr.Wrap(err)
307	}
308	copy(b[72:78], toIdxBytes[:])
309	return b[:], nil
310}
311
312// BytesUser encodes a L1UserTx into []byte
313func (tx *L1Tx) BytesUser() ([]byte, error) {
314	if !tx.UserOrigin {
315		return nil, tracerr.Wrap(fmt.Errorf("Can not calculate BytesUser() for a L1CoordinatorTx"))
316	}
317	return tx.BytesGeneric()
318}
319
320// BytesCoordinatorTx encodes a L1CoordinatorTx into []byte
321func (tx *L1Tx) BytesCoordinatorTx(compressedSignatureBytes []byte) ([]byte, error) {
322	if tx.UserOrigin {
323		return nil, tracerr.Wrap(fmt.Errorf("Can not calculate BytesCoordinatorTx() for a L1UserTx"))
324	}
325	var b [RollupConstL1CoordinatorTotalBytes]byte
326	v := compressedSignatureBytes[64]
327	s := compressedSignatureBytes[32:64]
328	r := compressedSignatureBytes[0:32]
329	b[0] = v
330	copy(b[1:33], s)
331	copy(b[33:65], r)
332	pkCompL := tx.FromBJJ
333	pkCompB := SwapEndianness(pkCompL[:])
334	copy(b[65:97], pkCompB[:])
335	copy(b[97:101], tx.TokenID.Bytes())
336	return b[:], nil
337}
338
339// L1UserTxFromBytes decodes a L1Tx from []byte
340func L1UserTxFromBytes(b []byte) (*L1Tx, error) {
341	if len(b) != RollupConstL1UserTotalBytes {
342		return nil,
343			tracerr.Wrap(fmt.Errorf("Can not parse L1Tx bytes, expected length %d, current: %d",
344				68, len(b)))
345	}
346
347	tx := &L1Tx{
348		UserOrigin: true,
349	}
350	var err error
351	tx.FromEthAddr = ethCommon.BytesToAddress(b[0:20])
352
353	pkCompB := b[20:52]
354	pkCompL := SwapEndianness(pkCompB)
355	copy(tx.FromBJJ[:], pkCompL)
356	fromIdx, err := IdxFromBytes(b[52:58])
357	if err != nil {
358		return nil, tracerr.Wrap(err)
359	}
360	tx.FromIdx = fromIdx
361	tx.DepositAmount, err = Float40FromBytes(b[58:63]).BigInt()
362	if err != nil {
363		return nil, tracerr.Wrap(err)
364	}
365	tx.Amount, err = Float40FromBytes(b[63:68]).BigInt()
366	if err != nil {
367		return nil, tracerr.Wrap(err)
368	}
369	tx.TokenID, err = TokenIDFromBytes(b[68:72])
370	if err != nil {
371		return nil, tracerr.Wrap(err)
372	}
373	tx.ToIdx, err = IdxFromBytes(b[72:78])
374	if err != nil {
375		return nil, tracerr.Wrap(err)
376	}
377
378	return tx, nil
379}
380
381// L1CoordinatorTxFromBytes decodes a L1Tx from []byte
382func L1CoordinatorTxFromBytes(b []byte, chainID *big.Int, hermezAddress ethCommon.Address) (*L1Tx,
383	error) {
384	if len(b) != RollupConstL1CoordinatorTotalBytes {
385		return nil, tracerr.Wrap(
386			fmt.Errorf("Can not parse L1CoordinatorTx bytes, expected length %d, current: %d",
387				101, len(b)))
388	}
389
390	tx := &L1Tx{
391		UserOrigin: false,
392	}
393	var err error
394	v := b[0]
395	s := b[1:33]
396	r := b[33:65]
397	pkCompB := b[65:97]
398	pkCompL := SwapEndianness(pkCompB)
399	copy(tx.FromBJJ[:], pkCompL)
400	tx.TokenID, err = TokenIDFromBytes(b[97:101])
401	if err != nil {
402		return nil, tracerr.Wrap(err)
403	}
404	tx.Amount = big.NewInt(0)
405	tx.DepositAmount = big.NewInt(0)
406	if int(v) > 0 {
407		// L1CoordinatorTX ETH
408		// Ethereum adds 27 to v
409		v = b[0] - byte(27) //nolint:gomnd
410		var signature []byte
411		signature = append(signature, r[:]...)
412		signature = append(signature, s[:]...)
413		signature = append(signature, v)
414
415		accCreationAuth := AccountCreationAuth{
416			BJJ: tx.FromBJJ,
417		}
418		h, err := accCreationAuth.HashToSign(uint16(chainID.Uint64()), hermezAddress)
419		if err != nil {
420			return nil, tracerr.Wrap(err)
421		}
422
423		pubKeyBytes, err := ethCrypto.Ecrecover(h, signature)
424		if err != nil {
425			return nil, tracerr.Wrap(err)
426		}
427		pubKey, err := ethCrypto.UnmarshalPubkey(pubKeyBytes)
428		if err != nil {
429			return nil, tracerr.Wrap(err)
430		}
431		tx.FromEthAddr = ethCrypto.PubkeyToAddress(*pubKey)
432	} else {
433		// L1Coordinator Babyjub
434		tx.FromEthAddr = RollupConstEthAddressInternalOnly
435	}
436	return tx, nil
437}