arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/api/exits_test.go
api/exits_test.goRaw
  1package api
  2
  3import (
  4	"fmt"
  5	"testing"
  6
  7	"github.com/hermeznetwork/hermez-node/apitypes"
  8	"github.com/hermeznetwork/hermez-node/common"
  9	"github.com/hermeznetwork/hermez-node/db/historydb"
 10	"github.com/mitchellh/copystructure"
 11	"github.com/stretchr/testify/assert"
 12	"github.com/stretchr/testify/require"
 13)
 14
 15type testCVP struct {
 16	Root     string
 17	Siblings []string
 18	OldKey   string
 19	OldValue string
 20	IsOld0   bool
 21	Key      string
 22	Value    string
 23	Fnc      int
 24}
 25
 26type testExit struct {
 27	ItemID                 uint64                 `json:"itemId"`
 28	BatchNum               common.BatchNum        `json:"batchNum"`
 29	AccountIdx             string                 `json:"accountIndex"`
 30	BJJ                    apitypes.HezBJJ        `json:"bjj"`
 31	EthAddr                apitypes.HezEthAddr    `json:"hezEthereumAddress"`
 32	MerkleProof            testCVP                `json:"merkleProof"`
 33	Balance                string                 `json:"balance"`
 34	InstantWithdrawn       *int64                 `json:"instantWithdrawn"`
 35	DelayedWithdrawRequest *int64                 `json:"delayedWithdrawRequest"`
 36	DelayedWithdrawn       *int64                 `json:"delayedWithdrawn"`
 37	Token                  historydb.TokenWithUSD `json:"token"`
 38}
 39
 40type testExitsResponse struct {
 41	Exits        []testExit `json:"exits"`
 42	PendingItems uint64     `json:"pendingItems"`
 43}
 44
 45func (t testExitsResponse) GetPending() (pendingItems, lastItemID uint64) {
 46	pendingItems = t.PendingItems
 47	lastItemID = t.Exits[len(t.Exits)-1].ItemID
 48	return pendingItems, lastItemID
 49}
 50
 51func (t testExitsResponse) New() Pendinger { return &testExitsResponse{} }
 52
 53func (t *testExitsResponse) Len() int {
 54	return len(t.Exits)
 55}
 56
 57func genTestExits(
 58	commonExits []common.ExitInfo,
 59	tokens []historydb.TokenWithUSD,
 60	accs []common.Account,
 61) []testExit {
 62	allExits := []testExit{}
 63	for _, exit := range commonExits {
 64		token := getTokenByIdx(exit.AccountIdx, tokens, accs)
 65		siblings := []string{}
 66		for i := 0; i < len(exit.MerkleProof.Siblings); i++ {
 67			siblings = append(siblings, exit.MerkleProof.Siblings[i].String())
 68		}
 69		acc := getAccountByIdx(exit.AccountIdx, accs)
 70		allExits = append(allExits, testExit{
 71			BatchNum:   exit.BatchNum,
 72			AccountIdx: idxToHez(exit.AccountIdx, token.Symbol),
 73			BJJ:        apitypes.NewHezBJJ(acc.PublicKey),
 74			EthAddr:    apitypes.NewHezEthAddr(acc.EthAddr),
 75			MerkleProof: testCVP{
 76				Root:     exit.MerkleProof.Root.String(),
 77				Siblings: siblings,
 78				OldKey:   exit.MerkleProof.OldKey.String(),
 79				OldValue: exit.MerkleProof.OldValue.String(),
 80				IsOld0:   exit.MerkleProof.IsOld0,
 81				Key:      exit.MerkleProof.Key.String(),
 82				Value:    exit.MerkleProof.Value.String(),
 83				Fnc:      exit.MerkleProof.Fnc,
 84			},
 85			Balance:                exit.Balance.String(),
 86			InstantWithdrawn:       exit.InstantWithdrawn,
 87			DelayedWithdrawRequest: exit.DelayedWithdrawRequest,
 88			DelayedWithdrawn:       exit.DelayedWithdrawn,
 89			Token:                  token,
 90		})
 91	}
 92	return allExits
 93}
 94
 95func TestGetExits(t *testing.T) {
 96	endpoint := apiURL + "exits"
 97	fetchedExits := []testExit{}
 98	appendIter := func(intr interface{}) {
 99		for i := 0; i < len(intr.(*testExitsResponse).Exits); i++ {
100			tmp, err := copystructure.Copy(intr.(*testExitsResponse).Exits[i])
101			if err != nil {
102				panic(err)
103			}
104			fetchedExits = append(fetchedExits, tmp.(testExit))
105		}
106	}
107	// Get all (no filters)
108	limit := 8
109	path := fmt.Sprintf("%s?limit=%d", endpoint, limit)
110	err := doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
111	assert.NoError(t, err)
112	assertExitAPIs(t, tc.exits, fetchedExits)
113
114	// Get by ethAddr
115	fetchedExits = []testExit{}
116	limit = 7
117	var account testAccount
118	for _, tx := range tc.txs {
119		found := false
120		if tx.Type == common.TxTypeExit {
121			for i := 0; i < len(tc.accounts); i++ {
122				if tx.FromIdx != nil && string(tc.accounts[i].Idx) == *tx.FromIdx {
123					account = tc.accounts[i]
124					break
125				}
126			}
127		}
128		if found {
129			break
130		}
131	}
132	path = fmt.Sprintf(
133		"%s?hezEthereumAddress=%s&limit=%d",
134		endpoint, account.EthAddr, limit,
135	)
136	err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
137	assert.NoError(t, err)
138	var accountExits []testExit
139	for i := range tc.exits {
140		for _, acc := range tc.accounts {
141			if string(acc.Idx) == tc.exits[i].AccountIdx {
142				if acc.EthAddr == account.EthAddr {
143					accountExits = append(accountExits, tc.exits[i])
144				}
145			}
146		}
147	}
148	assertExitAPIs(t, accountExits, fetchedExits)
149	// Get by bjj
150	fetchedExits = []testExit{}
151	limit = 6
152	path = fmt.Sprintf(
153		"%s?BJJ=%s&limit=%d",
154		endpoint, account.PublicKey, limit,
155	)
156	err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
157	assert.NoError(t, err)
158	assertExitAPIs(t, accountExits, fetchedExits)
159	// Get by tokenID
160	fetchedExits = []testExit{}
161	limit = 5
162	tokenID := tc.exits[0].Token.TokenID
163	path = fmt.Sprintf(
164		"%s?tokenId=%d&limit=%d",
165		endpoint, tokenID, limit,
166	)
167	err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
168	assert.NoError(t, err)
169	tokenIDExits := []testExit{}
170	for i := 0; i < len(tc.exits); i++ {
171		if tc.exits[i].Token.TokenID == tokenID {
172			tokenIDExits = append(tokenIDExits, tc.exits[i])
173		}
174	}
175	assertExitAPIs(t, tokenIDExits, fetchedExits)
176	// idx
177	fetchedExits = []testExit{}
178	limit = 4
179	idx := tc.exits[0].AccountIdx
180	path = fmt.Sprintf(
181		"%s?accountIndex=%s&limit=%d",
182		endpoint, idx, limit,
183	)
184	err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
185	assert.NoError(t, err)
186	idxExits := []testExit{}
187	for i := 0; i < len(tc.exits); i++ {
188		if tc.exits[i].AccountIdx[6:] == idx[6:] {
189			idxExits = append(idxExits, tc.exits[i])
190		}
191	}
192	assertExitAPIs(t, idxExits, fetchedExits)
193	// batchNum
194	fetchedExits = []testExit{}
195	limit = 3
196	batchNum := tc.exits[0].BatchNum
197	path = fmt.Sprintf(
198		"%s?batchNum=%d&limit=%d",
199		endpoint, batchNum, limit,
200	)
201	err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
202	assert.NoError(t, err)
203	batchNumExits := []testExit{}
204	for i := 0; i < len(tc.exits); i++ {
205		if tc.exits[i].BatchNum == batchNum {
206			batchNumExits = append(batchNumExits, tc.exits[i])
207		}
208	}
209	assertExitAPIs(t, batchNumExits, fetchedExits)
210	// OnlyPendingWithdraws
211	fetchedExits = []testExit{}
212	limit = 7
213	path = fmt.Sprintf(
214		"%s?&onlyPendingWithdraws=%t&limit=%d",
215		endpoint, true, limit,
216	)
217	err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
218	assert.NoError(t, err)
219	pendingExits := []testExit{}
220	for i := 0; i < len(tc.exits); i++ {
221		if tc.exits[i].InstantWithdrawn == nil && tc.exits[i].DelayedWithdrawn == nil {
222			pendingExits = append(pendingExits, tc.exits[i])
223		}
224	}
225	assertExitAPIs(t, pendingExits, fetchedExits)
226	// Multiple filters
227	fetchedExits = []testExit{}
228	limit = 1
229	path = fmt.Sprintf(
230		"%s?batchNum=%d&tokeId=%d&limit=%d",
231		endpoint, batchNum, tokenID, limit,
232	)
233	err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
234	assert.NoError(t, err)
235	mixedExits := []testExit{}
236	flipedExits := []testExit{}
237	for i := 0; i < len(tc.exits); i++ {
238		if tc.exits[i].BatchNum == batchNum && tc.exits[i].Token.TokenID == tokenID {
239			mixedExits = append(mixedExits, tc.exits[i])
240		}
241		flipedExits = append(flipedExits, tc.exits[len(tc.exits)-1-i])
242	}
243	assertExitAPIs(t, mixedExits, fetchedExits)
244	// All, in reverse order
245	fetchedExits = []testExit{}
246	limit = 5
247	path = fmt.Sprintf("%s?limit=%d", endpoint, limit)
248	err = doGoodReqPaginated(path, historydb.OrderDesc, &testExitsResponse{}, appendIter)
249	assert.NoError(t, err)
250	assertExitAPIs(t, flipedExits, fetchedExits)
251	// 400
252	path = fmt.Sprintf(
253		"%s?accountIndex=%s&hezEthereumAddress=%s",
254		endpoint, idx, account.EthAddr,
255	)
256	err = doBadReq("GET", path, nil, 400)
257	assert.NoError(t, err)
258	path = fmt.Sprintf("%s?tokenId=X", endpoint)
259	err = doBadReq("GET", path, nil, 400)
260	assert.NoError(t, err)
261	// 404
262	path = fmt.Sprintf("%s?batchNum=999999", endpoint)
263	err = doBadReq("GET", path, nil, 404)
264	assert.NoError(t, err)
265	path = fmt.Sprintf("%s?fromItem=1000999999", endpoint)
266	err = doBadReq("GET", path, nil, 404)
267	assert.NoError(t, err)
268}
269
270func TestGetExit(t *testing.T) {
271	// Get all txs by their ID
272	endpoint := apiURL + "exits/"
273	fetchedExits := []testExit{}
274	for _, exit := range tc.exits {
275		fetchedExit := testExit{}
276		assert.NoError(
277			t, doGoodReq(
278				"GET",
279				fmt.Sprintf("%s%d/%s", endpoint, exit.BatchNum, exit.AccountIdx),
280				nil, &fetchedExit,
281			),
282		)
283		fetchedExits = append(fetchedExits, fetchedExit)
284	}
285	assertExitAPIs(t, tc.exits, fetchedExits)
286	// 400
287	err := doBadReq("GET", endpoint+"1/haz:BOOM:1", nil, 400)
288	assert.NoError(t, err)
289	err = doBadReq("GET", endpoint+"-1/hez:BOOM:1", nil, 400)
290	assert.NoError(t, err)
291	// 404
292	err = doBadReq("GET", endpoint+"494/hez:XXX:1", nil, 404)
293	assert.NoError(t, err)
294}
295
296func assertExitAPIs(t *testing.T, expected, actual []testExit) {
297	require.Equal(t, len(expected), len(actual))
298	for i := 0; i < len(actual); i++ { //nolint len(actual) won't change within the loop
299		actual[i].ItemID = 0
300		actual[i].Token.ItemID = 0
301		if expected[i].Token.USDUpdate == nil {
302			assert.Equal(t, expected[i].Token.USDUpdate, actual[i].Token.USDUpdate)
303		} else {
304			assert.Equal(t, expected[i].Token.USDUpdate.Unix(), actual[i].Token.USDUpdate.Unix())
305			expected[i].Token.USDUpdate = actual[i].Token.USDUpdate
306		}
307		assert.Equal(t, expected[i], actual[i])
308	}
309}