arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/api/txspool_test.go
api/txspool_test.goRaw
  1package api
  2
  3import (
  4	"bytes"
  5	"encoding/json"
  6	"math/big"
  7	"testing"
  8	"time"
  9
 10	"github.com/hermeznetwork/hermez-node/common"
 11	"github.com/hermeznetwork/hermez-node/db/historydb"
 12	"github.com/iden3/go-iden3-crypto/babyjub"
 13	"github.com/stretchr/testify/assert"
 14)
 15
 16// testPoolTxReceive is a struct to be used to assert the response
 17// of GET /transactions-pool/:id
 18type testPoolTxReceive struct {
 19	TxID        common.TxID            `json:"id"`
 20	Type        common.TxType          `json:"type"`
 21	FromIdx     string                 `json:"fromAccountIndex"`
 22	FromEthAddr *string                `json:"fromHezEthereumAddress"`
 23	FromBJJ     *string                `json:"fromBJJ"`
 24	ToIdx       *string                `json:"toAccountIndex"`
 25	ToEthAddr   *string                `json:"toHezEthereumAddress"`
 26	ToBJJ       *string                `json:"toBjj"`
 27	Amount      string                 `json:"amount"`
 28	Fee         common.FeeSelector     `json:"fee"`
 29	Nonce       common.Nonce           `json:"nonce"`
 30	State       common.PoolL2TxState   `json:"state"`
 31	Signature   babyjub.SignatureComp  `json:"signature"`
 32	RqFromIdx   *string                `json:"requestFromAccountIndex"`
 33	RqToIdx     *string                `json:"requestToAccountIndex"`
 34	RqToEthAddr *string                `json:"requestToHezEthereumAddress"`
 35	RqToBJJ     *string                `json:"requestToBJJ"`
 36	RqTokenID   *common.TokenID        `json:"requestTokenId"`
 37	RqAmount    *string                `json:"requestAmount"`
 38	RqFee       *common.FeeSelector    `json:"requestFee"`
 39	RqNonce     *common.Nonce          `json:"requestNonce"`
 40	BatchNum    *common.BatchNum       `json:"batchNum"`
 41	Timestamp   time.Time              `json:"timestamp"`
 42	Token       historydb.TokenWithUSD `json:"token"`
 43}
 44
 45// testPoolTxSend is a struct to be used as a JSON body
 46// when testing POST /transactions-pool
 47type testPoolTxSend struct {
 48	TxID        common.TxID           `json:"id" binding:"required"`
 49	Type        common.TxType         `json:"type" binding:"required"`
 50	TokenID     common.TokenID        `json:"tokenId"`
 51	FromIdx     string                `json:"fromAccountIndex" binding:"required"`
 52	ToIdx       *string               `json:"toAccountIndex"`
 53	ToEthAddr   *string               `json:"toHezEthereumAddress"`
 54	ToBJJ       *string               `json:"toBjj"`
 55	Amount      string                `json:"amount" binding:"required"`
 56	Fee         common.FeeSelector    `json:"fee"`
 57	Nonce       common.Nonce          `json:"nonce"`
 58	Signature   babyjub.SignatureComp `json:"signature" binding:"required"`
 59	RqFromIdx   *string               `json:"requestFromAccountIndex"`
 60	RqToIdx     *string               `json:"requestToAccountIndex"`
 61	RqToEthAddr *string               `json:"requestToHezEthereumAddress"`
 62	RqToBJJ     *string               `json:"requestToBjj"`
 63	RqTokenID   *common.TokenID       `json:"requestTokenId"`
 64	RqAmount    *string               `json:"requestAmount"`
 65	RqFee       *common.FeeSelector   `json:"requestFee"`
 66	RqNonce     *common.Nonce         `json:"requestNonce"`
 67}
 68
 69func genTestPoolTx(accs []common.Account, privKs []babyjub.PrivateKey, tokens []historydb.TokenWithUSD) (poolTxsToSend []testPoolTxSend, poolTxsToReceive []testPoolTxReceive) {
 70	// Generate common.PoolL2Tx
 71	// WARNING: this should be replaced once til is ready
 72	poolTxs := []common.PoolL2Tx{}
 73	amount := new(big.Int)
 74	amount, ok := amount.SetString("100000000000000", 10)
 75	if !ok {
 76		panic("bad amount")
 77	}
 78	poolTx := common.PoolL2Tx{
 79		FromIdx: accs[0].Idx,
 80		ToIdx:   accs[1].Idx,
 81		Amount:  amount,
 82		TokenID: accs[0].TokenID,
 83		Nonce:   6,
 84	}
 85	if _, err := common.NewPoolL2Tx(&poolTx); err != nil {
 86		panic(err)
 87	}
 88	h, err := poolTx.HashToSign()
 89	if err != nil {
 90		panic(err)
 91	}
 92	poolTx.Signature = privKs[0].SignPoseidon(h).Compress()
 93	poolTxs = append(poolTxs, poolTx)
 94	// Transform to API formats
 95	poolTxsToSend = []testPoolTxSend{}
 96	poolTxsToReceive = []testPoolTxReceive{}
 97	for _, poolTx := range poolTxs {
 98		// common.PoolL2Tx ==> testPoolTxSend
 99		token := getTokenByID(poolTx.TokenID, tokens)
100		genSendTx := testPoolTxSend{
101			TxID:      poolTx.TxID,
102			Type:      poolTx.Type,
103			TokenID:   poolTx.TokenID,
104			FromIdx:   idxToHez(poolTx.FromIdx, token.Symbol),
105			Amount:    poolTx.Amount.String(),
106			Fee:       poolTx.Fee,
107			Nonce:     poolTx.Nonce,
108			Signature: poolTx.Signature,
109			RqFee:     &poolTx.RqFee,
110			RqNonce:   &poolTx.RqNonce,
111		}
112		// common.PoolL2Tx ==> testPoolTxReceive
113		genReceiveTx := testPoolTxReceive{
114			TxID:      poolTx.TxID,
115			Type:      poolTx.Type,
116			FromIdx:   idxToHez(poolTx.FromIdx, token.Symbol),
117			Amount:    poolTx.Amount.String(),
118			Fee:       poolTx.Fee,
119			Nonce:     poolTx.Nonce,
120			State:     poolTx.State,
121			Signature: poolTx.Signature,
122			Timestamp: poolTx.Timestamp,
123			// BatchNum:    poolTx.BatchNum,
124			RqFee:   &poolTx.RqFee,
125			RqNonce: &poolTx.RqNonce,
126			Token:   token,
127		}
128		fromAcc := getAccountByIdx(poolTx.ToIdx, accs)
129		fromAddr := ethAddrToHez(fromAcc.EthAddr)
130		genReceiveTx.FromEthAddr = &fromAddr
131		fromBjj := bjjToString(fromAcc.PublicKey)
132		genReceiveTx.FromBJJ = &fromBjj
133		if poolTx.ToIdx != 0 {
134			toIdx := idxToHez(poolTx.ToIdx, token.Symbol)
135			genSendTx.ToIdx = &toIdx
136			genReceiveTx.ToIdx = &toIdx
137		}
138		if poolTx.ToEthAddr != common.EmptyAddr {
139			toEth := ethAddrToHez(poolTx.ToEthAddr)
140			genSendTx.ToEthAddr = &toEth
141			genReceiveTx.ToEthAddr = &toEth
142		} else if poolTx.ToIdx > 255 {
143			acc := getAccountByIdx(poolTx.ToIdx, accs)
144			addr := ethAddrToHez(acc.EthAddr)
145			genReceiveTx.ToEthAddr = &addr
146		}
147		if poolTx.ToBJJ != nil {
148			toBJJ := bjjToString(poolTx.ToBJJ)
149			genSendTx.ToBJJ = &toBJJ
150			genReceiveTx.ToBJJ = &toBJJ
151		} else if poolTx.ToIdx > 255 {
152			acc := getAccountByIdx(poolTx.ToIdx, accs)
153			bjj := bjjToString(acc.PublicKey)
154			genReceiveTx.ToBJJ = &bjj
155		}
156		if poolTx.RqFromIdx != 0 {
157			rqFromIdx := idxToHez(poolTx.RqFromIdx, token.Symbol)
158			genSendTx.RqFromIdx = &rqFromIdx
159			genReceiveTx.RqFromIdx = &rqFromIdx
160			genSendTx.RqTokenID = &token.TokenID
161			genReceiveTx.RqTokenID = &token.TokenID
162			rqAmount := poolTx.RqAmount.String()
163			genSendTx.RqAmount = &rqAmount
164			genReceiveTx.RqAmount = &rqAmount
165
166			if poolTx.RqToIdx != 0 {
167				rqToIdx := idxToHez(poolTx.RqToIdx, token.Symbol)
168				genSendTx.RqToIdx = &rqToIdx
169				genReceiveTx.RqToIdx = &rqToIdx
170			}
171			if poolTx.RqToEthAddr != common.EmptyAddr {
172				rqToEth := ethAddrToHez(poolTx.RqToEthAddr)
173				genSendTx.RqToEthAddr = &rqToEth
174				genReceiveTx.RqToEthAddr = &rqToEth
175			}
176			if poolTx.RqToBJJ != nil {
177				rqToBJJ := bjjToString(poolTx.RqToBJJ)
178				genSendTx.RqToBJJ = &rqToBJJ
179				genReceiveTx.RqToBJJ = &rqToBJJ
180			}
181		}
182		poolTxsToSend = append(poolTxsToSend, genSendTx)
183		poolTxsToReceive = append(poolTxsToReceive, genReceiveTx)
184	}
185	return poolTxsToSend, poolTxsToReceive
186}
187
188func TestPoolTxs(t *testing.T) {
189	// POST
190	endpoint := apiURL + "transactions-pool"
191	fetchedTxID := common.TxID{}
192	for _, tx := range tc.poolTxsToSend {
193		jsonTxBytes, err := json.Marshal(tx)
194		assert.NoError(t, err)
195		jsonTxReader := bytes.NewReader(jsonTxBytes)
196		assert.NoError(
197			t, doGoodReq(
198				"POST",
199				endpoint,
200				jsonTxReader, &fetchedTxID,
201			),
202		)
203		assert.Equal(t, tx.TxID, fetchedTxID)
204	}
205	// 400
206	// Wrong fee
207	badTx := tc.poolTxsToSend[0]
208	badTx.Amount = "99999999999999999999999"
209	badTx.Fee = 255
210	jsonTxBytes, err := json.Marshal(badTx)
211	assert.NoError(t, err)
212	jsonTxReader := bytes.NewReader(jsonTxBytes)
213	err = doBadReq("POST", endpoint, jsonTxReader, 400)
214	assert.NoError(t, err)
215	// Wrong signature
216	badTx = tc.poolTxsToSend[0]
217	badTx.FromIdx = "hez:foo:1000"
218	jsonTxBytes, err = json.Marshal(badTx)
219	assert.NoError(t, err)
220	jsonTxReader = bytes.NewReader(jsonTxBytes)
221	err = doBadReq("POST", endpoint, jsonTxReader, 400)
222	assert.NoError(t, err)
223	// Wrong to
224	badTx = tc.poolTxsToSend[0]
225	ethAddr := "hez:0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
226	badTx.ToEthAddr = &ethAddr
227	badTx.ToIdx = nil
228	jsonTxBytes, err = json.Marshal(badTx)
229	assert.NoError(t, err)
230	jsonTxReader = bytes.NewReader(jsonTxBytes)
231	err = doBadReq("POST", endpoint, jsonTxReader, 400)
232	assert.NoError(t, err)
233	// Wrong rq
234	badTx = tc.poolTxsToSend[0]
235	rqFromIdx := "hez:foo:30"
236	badTx.RqFromIdx = &rqFromIdx
237	jsonTxBytes, err = json.Marshal(badTx)
238	assert.NoError(t, err)
239	jsonTxReader = bytes.NewReader(jsonTxBytes)
240	err = doBadReq("POST", endpoint, jsonTxReader, 400)
241	assert.NoError(t, err)
242	// GET
243	endpoint += "/"
244	for _, tx := range tc.poolTxsToReceive {
245		fetchedTx := testPoolTxReceive{}
246		assert.NoError(
247			t, doGoodReq(
248				"GET",
249				endpoint+tx.TxID.String(),
250				nil, &fetchedTx,
251			),
252		)
253		assertPoolTx(t, tx, fetchedTx)
254	}
255	// 400
256	err = doBadReq("GET", endpoint+"0xG20000000156660000000090", nil, 400)
257	assert.NoError(t, err)
258	// 404
259	err = doBadReq("GET", endpoint+"0x020000000156660000000090", nil, 404)
260	assert.NoError(t, err)
261}
262
263func assertPoolTx(t *testing.T, expected, actual testPoolTxReceive) {
264	// state should be pending
265	assert.Equal(t, common.PoolL2TxStatePending, actual.State)
266	expected.State = actual.State
267	actual.Token.ItemID = 0
268	// timestamp should be very close to now
269	assert.Less(t, time.Now().UTC().Unix()-3, actual.Timestamp.Unix())
270	expected.Timestamp = actual.Timestamp
271	// token timestamp
272	if expected.Token.USDUpdate == nil {
273		assert.Equal(t, expected.Token.USDUpdate, actual.Token.USDUpdate)
274	} else {
275		assert.Equal(t, expected.Token.USDUpdate.Unix(), actual.Token.USDUpdate.Unix())
276		expected.Token.USDUpdate = actual.Token.USDUpdate
277	}
278	assert.Equal(t, expected, actual)
279}