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	"testing"
  7	"time"
  8
  9	"github.com/hermeznetwork/hermez-node/common"
 10	"github.com/hermeznetwork/hermez-node/db/historydb"
 11	"github.com/iden3/go-iden3-crypto/babyjub"
 12	"github.com/stretchr/testify/assert"
 13	"github.com/stretchr/testify/require"
 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 genTestPoolTxs(
 70	poolTxs []common.PoolL2Tx,
 71	tokens []historydb.TokenWithUSD,
 72	accs []common.Account,
 73) (poolTxsToSend []testPoolTxSend, poolTxsToReceive []testPoolTxReceive) {
 74	poolTxsToSend = []testPoolTxSend{}
 75	poolTxsToReceive = []testPoolTxReceive{}
 76	for _, poolTx := range poolTxs {
 77		// common.PoolL2Tx ==> testPoolTxSend
 78		token := getTokenByID(poolTx.TokenID, tokens)
 79		genSendTx := testPoolTxSend{
 80			TxID:      poolTx.TxID,
 81			Type:      poolTx.Type,
 82			TokenID:   poolTx.TokenID,
 83			FromIdx:   idxToHez(poolTx.FromIdx, token.Symbol),
 84			Amount:    poolTx.Amount.String(),
 85			Fee:       poolTx.Fee,
 86			Nonce:     poolTx.Nonce,
 87			Signature: poolTx.Signature,
 88			RqFee:     &poolTx.RqFee,
 89			RqNonce:   &poolTx.RqNonce,
 90		}
 91		// common.PoolL2Tx ==> testPoolTxReceive
 92		genReceiveTx := testPoolTxReceive{
 93			TxID:      poolTx.TxID,
 94			Type:      poolTx.Type,
 95			FromIdx:   idxToHez(poolTx.FromIdx, token.Symbol),
 96			Amount:    poolTx.Amount.String(),
 97			Fee:       poolTx.Fee,
 98			Nonce:     poolTx.Nonce,
 99			State:     poolTx.State,
100			Signature: poolTx.Signature,
101			Timestamp: poolTx.Timestamp,
102			// BatchNum:    poolTx.BatchNum,
103			RqFee:   &poolTx.RqFee,
104			RqNonce: &poolTx.RqNonce,
105			Token:   token,
106		}
107		fromAcc := getAccountByIdx(poolTx.FromIdx, accs)
108		fromAddr := ethAddrToHez(fromAcc.EthAddr)
109		genReceiveTx.FromEthAddr = &fromAddr
110		fromBjj := bjjToString(fromAcc.BJJ)
111		genReceiveTx.FromBJJ = &fromBjj
112		if poolTx.ToIdx != 0 {
113			toIdx := idxToHez(poolTx.ToIdx, token.Symbol)
114			genSendTx.ToIdx = &toIdx
115			genReceiveTx.ToIdx = &toIdx
116		}
117		if poolTx.ToEthAddr != common.EmptyAddr {
118			toEth := ethAddrToHez(poolTx.ToEthAddr)
119			genSendTx.ToEthAddr = &toEth
120			genReceiveTx.ToEthAddr = &toEth
121		} else if poolTx.ToIdx > 255 {
122			acc := getAccountByIdx(poolTx.ToIdx, accs)
123			addr := ethAddrToHez(acc.EthAddr)
124			genReceiveTx.ToEthAddr = &addr
125		}
126		if poolTx.ToBJJ != common.EmptyBJJComp {
127			toBJJ := bjjToString(poolTx.ToBJJ)
128			genSendTx.ToBJJ = &toBJJ
129			genReceiveTx.ToBJJ = &toBJJ
130		} else if poolTx.ToIdx > 255 {
131			acc := getAccountByIdx(poolTx.ToIdx, accs)
132			bjj := bjjToString(acc.BJJ)
133			genReceiveTx.ToBJJ = &bjj
134		}
135		if poolTx.RqFromIdx != 0 {
136			rqFromIdx := idxToHez(poolTx.RqFromIdx, token.Symbol)
137			genSendTx.RqFromIdx = &rqFromIdx
138			genReceiveTx.RqFromIdx = &rqFromIdx
139			genSendTx.RqTokenID = &token.TokenID
140			genReceiveTx.RqTokenID = &token.TokenID
141			rqAmount := poolTx.RqAmount.String()
142			genSendTx.RqAmount = &rqAmount
143			genReceiveTx.RqAmount = &rqAmount
144
145			if poolTx.RqToIdx != 0 {
146				rqToIdx := idxToHez(poolTx.RqToIdx, token.Symbol)
147				genSendTx.RqToIdx = &rqToIdx
148				genReceiveTx.RqToIdx = &rqToIdx
149			}
150			if poolTx.RqToEthAddr != common.EmptyAddr {
151				rqToEth := ethAddrToHez(poolTx.RqToEthAddr)
152				genSendTx.RqToEthAddr = &rqToEth
153				genReceiveTx.RqToEthAddr = &rqToEth
154			}
155			if poolTx.RqToBJJ != common.EmptyBJJComp {
156				rqToBJJ := bjjToString(poolTx.RqToBJJ)
157				genSendTx.RqToBJJ = &rqToBJJ
158				genReceiveTx.RqToBJJ = &rqToBJJ
159			}
160		}
161
162		poolTxsToSend = append(poolTxsToSend, genSendTx)
163		poolTxsToReceive = append(poolTxsToReceive, genReceiveTx)
164	}
165	return poolTxsToSend, poolTxsToReceive
166}
167
168func TestPoolTxs(t *testing.T) {
169	// POST
170	endpoint := apiURL + "transactions-pool"
171	fetchedTxID := common.TxID{}
172	for _, tx := range tc.poolTxsToSend {
173		jsonTxBytes, err := json.Marshal(tx)
174		require.NoError(t, err)
175		jsonTxReader := bytes.NewReader(jsonTxBytes)
176		require.NoError(
177			t, doGoodReq(
178				"POST",
179				endpoint,
180				jsonTxReader, &fetchedTxID,
181			),
182		)
183		assert.Equal(t, tx.TxID, fetchedTxID)
184	}
185	// 400
186	// Wrong fee
187	badTx := tc.poolTxsToSend[0]
188	badTx.Amount = "99950000000000000"
189	badTx.Fee = 255
190	jsonTxBytes, err := json.Marshal(badTx)
191	require.NoError(t, err)
192	jsonTxReader := bytes.NewReader(jsonTxBytes)
193	err = doBadReq("POST", endpoint, jsonTxReader, 400)
194	require.NoError(t, err)
195	// Wrong signature
196	badTx = tc.poolTxsToSend[0]
197	badTx.FromIdx = "hez:foo:1000"
198	jsonTxBytes, err = json.Marshal(badTx)
199	require.NoError(t, err)
200	jsonTxReader = bytes.NewReader(jsonTxBytes)
201	err = doBadReq("POST", endpoint, jsonTxReader, 400)
202	require.NoError(t, err)
203	// Wrong to
204	badTx = tc.poolTxsToSend[0]
205	ethAddr := "hez:0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
206	badTx.ToEthAddr = &ethAddr
207	badTx.ToIdx = nil
208	jsonTxBytes, err = json.Marshal(badTx)
209	require.NoError(t, err)
210	jsonTxReader = bytes.NewReader(jsonTxBytes)
211	err = doBadReq("POST", endpoint, jsonTxReader, 400)
212	require.NoError(t, err)
213	// Wrong rq
214	badTx = tc.poolTxsToSend[0]
215	rqFromIdx := "hez:foo:30"
216	badTx.RqFromIdx = &rqFromIdx
217	jsonTxBytes, err = json.Marshal(badTx)
218	require.NoError(t, err)
219	jsonTxReader = bytes.NewReader(jsonTxBytes)
220	err = doBadReq("POST", endpoint, jsonTxReader, 400)
221	require.NoError(t, err)
222	// GET
223	endpoint += "/"
224	for _, tx := range tc.poolTxsToReceive {
225		fetchedTx := testPoolTxReceive{}
226		require.NoError(
227			t, doGoodReq(
228				"GET",
229				endpoint+tx.TxID.String(),
230				nil, &fetchedTx,
231			),
232		)
233		assertPoolTx(t, tx, fetchedTx)
234	}
235	// 400, due invalid TxID
236	err = doBadReq("GET", endpoint+"0xG2241b6f2b1dd772dba391f4a1a3407c7c21f598d86e2585a14e616fb4a255f823", nil, 400)
237	require.NoError(t, err)
238	// 404, due nonexistent TxID in DB
239	err = doBadReq("GET", endpoint+"0x02241b6f2b1dd772dba391f4a1a3407c7c21f598d86e2585a14e616fb4a255f823", nil, 404)
240	require.NoError(t, err)
241}
242
243func assertPoolTx(t *testing.T, expected, actual testPoolTxReceive) {
244	// state should be pending
245	assert.Equal(t, common.PoolL2TxStatePending, actual.State)
246	expected.State = actual.State
247	actual.Token.ItemID = 0
248	// timestamp should be very close to now
249	assert.Less(t, time.Now().UTC().Unix()-3, actual.Timestamp.Unix())
250	expected.Timestamp = actual.Timestamp
251	// token timestamp
252	if expected.Token.USDUpdate == nil {
253		assert.Equal(t, expected.Token.USDUpdate, actual.Token.USDUpdate)
254	} else {
255		assert.Equal(t, expected.Token.USDUpdate.Unix(), actual.Token.USDUpdate.Unix())
256		expected.Token.USDUpdate = actual.Token.USDUpdate
257	}
258	assert.Equal(t, expected, actual)
259}