arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/common/pooll2tx.go
common/pooll2tx.goRaw
  1package common
  2
  3import (
  4	"fmt"
  5	"math/big"
  6	"time"
  7
  8	ethCommon "github.com/ethereum/go-ethereum/common"
  9	"github.com/iden3/go-iden3-crypto/babyjub"
 10	"github.com/iden3/go-iden3-crypto/poseidon"
 11)
 12
 13// PoolL2Tx is a struct that represents a L2Tx sent by an account to the coordinator hat is waiting to be forged
 14type PoolL2Tx struct {
 15	// Stored in DB: mandatory fileds
 16
 17	// TxID (12 bytes) for L2Tx is:
 18	// bytes:  |  1   |    6    |   5   |
 19	// values: | type | FromIdx | Nonce |
 20	TxID      TxID               `meddler:"tx_id"`
 21	FromIdx   Idx                `meddler:"from_idx"`
 22	ToIdx     Idx                `meddler:"to_idx,zeroisnull"`
 23	AuxToIdx  Idx                `meddler:"-"` // AuxToIdx is only used internally at the StateDB to avoid repeated computation when processing transactions (from Synchronizer, TxSelector, BatchBuilder)
 24	ToEthAddr ethCommon.Address  `meddler:"to_eth_addr"`
 25	ToBJJ     *babyjub.PublicKey `meddler:"to_bjj"`
 26	TokenID   TokenID            `meddler:"token_id"`
 27	Amount    *big.Int           `meddler:"amount,bigint"` // TODO: change to float16
 28	Fee       FeeSelector        `meddler:"fee"`
 29	Nonce     Nonce              `meddler:"nonce"` // effective 40 bits used
 30	State     PoolL2TxState      `meddler:"state"`
 31	Signature *babyjub.Signature `meddler:"signature"`         // tx signature
 32	Timestamp time.Time          `meddler:"timestamp,utctime"` // time when added to the tx pool
 33	// Stored in DB: optional fileds, may be uninitialized
 34	RqFromIdx         Idx                `meddler:"rq_from_idx,zeroisnull"` // FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.LoadAmount (deposit)
 35	RqToIdx           Idx                `meddler:"rq_to_idx,zeroisnull"`   // FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.LoadAmount (deposit)
 36	RqToEthAddr       ethCommon.Address  `meddler:"rq_to_eth_addr"`
 37	RqToBJJ           *babyjub.PublicKey `meddler:"rq_to_bjj"` // TODO: stop using json, use scanner/valuer
 38	RqTokenID         TokenID            `meddler:"rq_token_id,zeroisnull"`
 39	RqAmount          *big.Int           `meddler:"rq_amount,bigintnull"` // TODO: change to float16
 40	RqFee             FeeSelector        `meddler:"rq_fee,zeroisnull"`
 41	RqNonce           uint64             `meddler:"rq_nonce,zeroisnull"` // effective 48 bits used
 42	AbsoluteFee       float64            `meddler:"fee_usd,zeroisnull"`
 43	AbsoluteFeeUpdate time.Time          `meddler:"usd_update,utctimez"`
 44	Type              TxType             `meddler:"tx_type"`
 45	// Extra metadata, may be uninitialized
 46	RqTxCompressedData []byte `meddler:"-"` // 253 bits, optional for atomic txs
 47}
 48
 49// NewPoolL2Tx returns the given L2Tx with the TxId & Type parameters calculated
 50// from the L2Tx values
 51func NewPoolL2Tx(poolL2Tx *PoolL2Tx) (*PoolL2Tx, error) {
 52	// calculate TxType
 53	var txType TxType
 54	if poolL2Tx.ToIdx == Idx(0) {
 55		txType = TxTypeTransfer
 56	} else if poolL2Tx.ToIdx == Idx(1) {
 57		txType = TxTypeExit
 58	} else if poolL2Tx.ToIdx >= IdxUserThreshold {
 59		txType = TxTypeTransfer
 60	} else {
 61		return poolL2Tx, fmt.Errorf("Can not determine type of PoolL2Tx, invalid ToIdx value: %d", poolL2Tx.ToIdx)
 62	}
 63
 64	// if TxType!=poolL2Tx.TxType return error
 65	if poolL2Tx.Type != "" && poolL2Tx.Type != txType {
 66		return poolL2Tx, fmt.Errorf("PoolL2Tx.Type: %s, should be: %s", poolL2Tx.Type, txType)
 67	}
 68	poolL2Tx.Type = txType
 69
 70	var txid [TxIDLen]byte
 71	txid[0] = TxIDPrefixL2Tx
 72	fromIdxBytes, err := poolL2Tx.FromIdx.Bytes()
 73	if err != nil {
 74		return poolL2Tx, err
 75	}
 76	copy(txid[1:7], fromIdxBytes[:])
 77	nonceBytes, err := poolL2Tx.Nonce.Bytes()
 78	if err != nil {
 79		return poolL2Tx, err
 80	}
 81	copy(txid[7:12], nonceBytes[:])
 82	poolL2Tx.TxID = TxID(txid)
 83
 84	return poolL2Tx, nil
 85}
 86
 87// TxCompressedData spec:
 88// [ 1 bits  ] toBJJSign // 1 byte
 89// [ 8 bits  ] userFee // 1 byte
 90// [ 40 bits ] nonce // 5 bytes
 91// [ 32 bits ] tokenID // 4 bytes
 92// [ 16 bits ] amountFloat16 // 2 bytes
 93// [ 48 bits ] toIdx // 6 bytes
 94// [ 48 bits ] fromIdx // 6 bytes
 95// [ 16 bits ] chainId // 2 bytes
 96// [ 32 bits ] signatureConstant // 4 bytes
 97// Total bits compressed data:  241 bits // 31 bytes in *big.Int representation
 98func (tx *PoolL2Tx) TxCompressedData() (*big.Int, error) {
 99	// sigconstant
100	sc, ok := new(big.Int).SetString("3322668559", 10)
101	if !ok {
102		return nil, fmt.Errorf("error parsing SignatureConstant")
103	}
104
105	amountFloat16, err := NewFloat16(tx.Amount)
106	if err != nil {
107		return nil, err
108	}
109	var b [31]byte
110	toBJJSign := byte(0)
111	if babyjub.PointCoordSign(tx.ToBJJ.X) {
112		toBJJSign = byte(1)
113	}
114	b[0] = toBJJSign
115	b[1] = byte(tx.Fee)
116	nonceBytes, err := tx.Nonce.Bytes()
117	if err != nil {
118		return nil, err
119	}
120	copy(b[2:7], nonceBytes[:])
121	copy(b[7:11], tx.TokenID.Bytes())
122	copy(b[11:13], amountFloat16.Bytes())
123	toIdxBytes, err := tx.ToIdx.Bytes()
124	if err != nil {
125		return nil, err
126	}
127	copy(b[13:19], toIdxBytes[:])
128	fromIdxBytes, err := tx.FromIdx.Bytes()
129	if err != nil {
130		return nil, err
131	}
132	copy(b[19:25], fromIdxBytes[:])
133	copy(b[25:27], []byte{0, 1, 0, 0}) // TODO check js implementation (unexpected behaviour from test vector generated from js)
134	copy(b[27:31], sc.Bytes())
135
136	bi := new(big.Int).SetBytes(b[:])
137	return bi, nil
138}
139
140// TxCompressedDataV2 spec:
141// [ 1 bits  ] toBJJSign // 1 byte
142// [ 8 bits  ] userFee // 1 byte
143// [ 40 bits ] nonce // 5 bytes
144// [ 32 bits ] tokenID // 4 bytes
145// [ 16 bits ] amountFloat16 // 2 bytes
146// [ 48 bits ] toIdx // 6 bytes
147// [ 48 bits ] fromIdx // 6 bytes
148// Total bits compressed data:  193 bits // 25 bytes in *big.Int representation
149func (tx *PoolL2Tx) TxCompressedDataV2() (*big.Int, error) {
150	amountFloat16, err := NewFloat16(tx.Amount)
151	if err != nil {
152		return nil, err
153	}
154	var b [25]byte
155	toBJJSign := byte(0)
156	if babyjub.PointCoordSign(tx.ToBJJ.X) {
157		toBJJSign = byte(1)
158	}
159	b[0] = toBJJSign
160	b[1] = byte(tx.Fee)
161	nonceBytes, err := tx.Nonce.Bytes()
162	if err != nil {
163		return nil, err
164	}
165	copy(b[2:7], nonceBytes[:])
166	copy(b[7:11], tx.TokenID.Bytes())
167	copy(b[11:13], amountFloat16.Bytes())
168	toIdxBytes, err := tx.ToIdx.Bytes()
169	if err != nil {
170		return nil, err
171	}
172	copy(b[13:19], toIdxBytes[:])
173	fromIdxBytes, err := tx.FromIdx.Bytes()
174	if err != nil {
175		return nil, err
176	}
177	copy(b[19:25], fromIdxBytes[:])
178
179	bi := new(big.Int).SetBytes(b[:])
180	return bi, nil
181}
182
183// HashToSign returns the computed Poseidon hash from the *PoolL2Tx that will be signed by the sender.
184func (tx *PoolL2Tx) HashToSign() (*big.Int, error) {
185	toCompressedData, err := tx.TxCompressedData()
186	if err != nil {
187		return nil, err
188	}
189	toEthAddr := EthAddrToBigInt(tx.ToEthAddr)
190	rqToEthAddr := EthAddrToBigInt(tx.RqToEthAddr)
191	toBJJAy := tx.ToBJJ.Y
192	rqTxCompressedDataV2, err := tx.TxCompressedDataV2()
193	if err != nil {
194		return nil, err
195	}
196	rqToBJJY := big.NewInt(0)
197	if tx.RqToBJJ != nil {
198		rqToBJJY = tx.RqToBJJ.Y
199	}
200
201	return poseidon.Hash([]*big.Int{toCompressedData, toEthAddr, toBJJAy, rqTxCompressedDataV2, rqToEthAddr, rqToBJJY})
202}
203
204// VerifySignature returns true if the signature verification is correct for the given PublicKey
205func (tx *PoolL2Tx) VerifySignature(pk *babyjub.PublicKey) bool {
206	h, err := tx.HashToSign()
207	if err != nil {
208		return false
209	}
210	return pk.VerifyPoseidon(h, tx.Signature)
211}
212
213// L2Tx returns a *L2Tx from the PoolL2Tx
214func (tx PoolL2Tx) L2Tx() L2Tx {
215	return L2Tx{
216		TxID:    tx.TxID,
217		FromIdx: tx.FromIdx,
218		ToIdx:   tx.ToIdx,
219		Amount:  tx.Amount,
220		Fee:     tx.Fee,
221		Nonce:   tx.Nonce,
222		Type:    tx.Type,
223	}
224}
225
226// Tx returns a *Tx from the PoolL2Tx
227func (tx PoolL2Tx) Tx() Tx {
228	return Tx{
229		TxID:    tx.TxID,
230		FromIdx: tx.FromIdx,
231		ToIdx:   tx.ToIdx,
232		Amount:  tx.Amount,
233		Nonce:   &tx.Nonce,
234		Fee:     &tx.Fee,
235		Type:    tx.Type,
236	}
237}
238
239// PoolL2TxsToL2Txs returns an array of []L2Tx from an array of []PoolL2Tx
240func PoolL2TxsToL2Txs(txs []PoolL2Tx) ([]L2Tx, error) {
241	var r []L2Tx
242	for _, poolTx := range txs {
243		r = append(r, poolTx.L2Tx())
244	}
245	return r, nil
246}
247
248// PoolL2TxState is a struct that represents the status of a L2 transaction
249type PoolL2TxState string
250
251const (
252	// PoolL2TxStatePending represents a valid L2Tx that hasn't started the forging process
253	PoolL2TxStatePending PoolL2TxState = "pend"
254	// PoolL2TxStateForging represents a valid L2Tx that has started the forging process
255	PoolL2TxStateForging PoolL2TxState = "fing"
256	// PoolL2TxStateForged represents a L2Tx that has already been forged
257	PoolL2TxStateForged PoolL2TxState = "fged"
258	// PoolL2TxStateInvalid represents a L2Tx that has been invalidated
259	PoolL2TxStateInvalid PoolL2TxState = "invl"
260)