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