arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/coordinator/purger_test.go
coordinator/purger_test.goRaw
  1package coordinator
  2
  3import (
  4	"io/ioutil"
  5	"os"
  6	"testing"
  7	"time"
  8
  9	"github.com/hermeznetwork/hermez-node/common"
 10	dbUtils "github.com/hermeznetwork/hermez-node/db"
 11	"github.com/hermeznetwork/hermez-node/db/l2db"
 12	"github.com/hermeznetwork/hermez-node/db/statedb"
 13	"github.com/hermeznetwork/hermez-node/test"
 14	"github.com/hermeznetwork/hermez-node/test/til"
 15	"github.com/stretchr/testify/assert"
 16	"github.com/stretchr/testify/require"
 17)
 18
 19func newL2DB(t *testing.T) *l2db.L2DB {
 20	pass := os.Getenv("POSTGRES_PASS")
 21	db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
 22	require.NoError(t, err)
 23	test.WipeDB(db)
 24	return l2db.NewL2DB(db, db, 10, 100, 0.0, 1000.0, 24*time.Hour, nil)
 25}
 26
 27func newStateDB(t *testing.T) *statedb.LocalStateDB {
 28	syncDBPath, err := ioutil.TempDir("", "tmpSyncDB")
 29	require.NoError(t, err)
 30	deleteme = append(deleteme, syncDBPath)
 31	syncStateDB, err := statedb.NewStateDB(statedb.Config{Path: syncDBPath, Keep: 128,
 32		Type: statedb.TypeSynchronizer, NLevels: 48})
 33	assert.NoError(t, err)
 34	stateDBPath, err := ioutil.TempDir("", "tmpStateDB")
 35	require.NoError(t, err)
 36	deleteme = append(deleteme, stateDBPath)
 37	stateDB, err := statedb.NewLocalStateDB(statedb.Config{Path: stateDBPath, Keep: 128,
 38		Type: statedb.TypeTxSelector, NLevels: 0}, syncStateDB)
 39	require.NoError(t, err)
 40	return stateDB
 41}
 42
 43func TestCanPurgeCanInvalidate(t *testing.T) {
 44	cfg := PurgerCfg{
 45		PurgeBatchDelay:      2,
 46		PurgeBlockDelay:      6,
 47		InvalidateBatchDelay: 4,
 48		InvalidateBlockDelay: 8,
 49	}
 50	p := Purger{
 51		cfg: cfg,
 52	}
 53	startBlockNum := int64(1000)
 54	startBatchNum := int64(10)
 55	blockNum := startBlockNum
 56	batchNum := startBatchNum
 57
 58	assert.True(t, p.CanPurge(blockNum, batchNum))
 59	p.lastPurgeBlock = startBlockNum
 60	p.lastPurgeBatch = startBatchNum
 61	assert.False(t, p.CanPurge(blockNum, batchNum))
 62
 63	blockNum = startBlockNum + cfg.PurgeBlockDelay - 1
 64	batchNum = startBatchNum + cfg.PurgeBatchDelay - 1
 65	assert.False(t, p.CanPurge(blockNum, batchNum))
 66	blockNum = startBlockNum + cfg.PurgeBlockDelay - 1
 67	batchNum = startBatchNum + cfg.PurgeBatchDelay
 68	assert.True(t, p.CanPurge(blockNum, batchNum))
 69	blockNum = startBlockNum + cfg.PurgeBlockDelay
 70	batchNum = startBatchNum + cfg.PurgeBatchDelay - 1
 71	assert.True(t, p.CanPurge(blockNum, batchNum))
 72
 73	assert.True(t, p.CanInvalidate(blockNum, batchNum))
 74	p.lastInvalidateBlock = startBlockNum
 75	p.lastInvalidateBatch = startBatchNum
 76	assert.False(t, p.CanInvalidate(blockNum, batchNum))
 77
 78	blockNum = startBlockNum + cfg.InvalidateBlockDelay - 1
 79	batchNum = startBatchNum + cfg.InvalidateBatchDelay - 1
 80	assert.False(t, p.CanInvalidate(blockNum, batchNum))
 81	blockNum = startBlockNum + cfg.InvalidateBlockDelay - 1
 82	batchNum = startBatchNum + cfg.InvalidateBatchDelay
 83	assert.True(t, p.CanInvalidate(blockNum, batchNum))
 84	blockNum = startBlockNum + cfg.InvalidateBlockDelay
 85	batchNum = startBatchNum + cfg.InvalidateBatchDelay - 1
 86	assert.True(t, p.CanInvalidate(blockNum, batchNum))
 87}
 88
 89func TestPurgeMaybeInvalidateMaybe(t *testing.T) {
 90	cfg := PurgerCfg{
 91		PurgeBatchDelay:      2,
 92		PurgeBlockDelay:      6,
 93		InvalidateBatchDelay: 4,
 94		InvalidateBlockDelay: 8,
 95	}
 96	p := Purger{
 97		cfg: cfg,
 98	}
 99	l2DB := newL2DB(t)
100	stateDB := newStateDB(t)
101
102	startBlockNum := int64(1000)
103	startBatchNum := int64(10)
104
105	p.lastPurgeBlock = startBlockNum
106	p.lastPurgeBatch = startBatchNum
107
108	blockNum := startBlockNum + cfg.PurgeBlockDelay - 1
109	batchNum := startBatchNum + cfg.PurgeBatchDelay - 1
110	ok, err := p.PurgeMaybe(l2DB, blockNum, batchNum)
111	require.NoError(t, err)
112	assert.False(t, ok)
113	// At this point the purger will purge.  The second time it doesn't
114	// because it the first time it has updates the last time it did.
115	blockNum = startBlockNum + cfg.PurgeBlockDelay - 1
116	batchNum = startBatchNum + cfg.PurgeBatchDelay
117	ok, err = p.PurgeMaybe(l2DB, blockNum, batchNum)
118	require.NoError(t, err)
119	assert.True(t, ok)
120	ok, err = p.PurgeMaybe(l2DB, blockNum, batchNum)
121	require.NoError(t, err)
122	assert.False(t, ok)
123
124	p.lastInvalidateBlock = startBlockNum
125	p.lastInvalidateBatch = startBatchNum
126
127	blockNum = startBlockNum + cfg.InvalidateBlockDelay - 1
128	batchNum = startBatchNum + cfg.InvalidateBatchDelay - 1
129	ok, err = p.InvalidateMaybe(l2DB, stateDB, blockNum, batchNum)
130	require.NoError(t, err)
131	assert.False(t, ok)
132	// At this point the purger will invaidate.  The second time it doesn't
133	// because it the first time it has updates the last time it did.
134	blockNum = startBlockNum + cfg.InvalidateBlockDelay - 1
135	batchNum = startBatchNum + cfg.InvalidateBatchDelay
136	ok, err = p.InvalidateMaybe(l2DB, stateDB, blockNum, batchNum)
137	require.NoError(t, err)
138	assert.True(t, ok)
139	ok, err = p.InvalidateMaybe(l2DB, stateDB, blockNum, batchNum)
140	require.NoError(t, err)
141	assert.False(t, ok)
142}
143
144func TestIdxsNonce(t *testing.T) {
145	inputIdxsNonce := []common.IdxNonce{
146		{Idx: 256, Nonce: 1},
147		{Idx: 256, Nonce: 2},
148		{Idx: 257, Nonce: 3},
149		{Idx: 258, Nonce: 5},
150		{Idx: 258, Nonce: 2},
151	}
152	expectedIdxsNonce := map[common.Idx]common.Nonce{
153		common.Idx(256): common.Nonce(2),
154		common.Idx(257): common.Nonce(3),
155		common.Idx(258): common.Nonce(5),
156	}
157
158	l2txs := make([]common.L2Tx, len(inputIdxsNonce))
159	for i, idxNonce := range inputIdxsNonce {
160		l2txs[i].FromIdx = idxNonce.Idx
161		l2txs[i].Nonce = idxNonce.Nonce
162	}
163	idxsNonce := idxsNonceFromL2Txs(l2txs)
164	assert.Equal(t, len(expectedIdxsNonce), len(idxsNonce))
165	for _, idxNonce := range idxsNonce {
166		nonce := expectedIdxsNonce[idxNonce.Idx]
167		assert.Equal(t, nonce, idxNonce.Nonce)
168	}
169
170	pooll2txs := make([]common.PoolL2Tx, len(inputIdxsNonce))
171	for i, idxNonce := range inputIdxsNonce {
172		pooll2txs[i].FromIdx = idxNonce.Idx
173		pooll2txs[i].Nonce = idxNonce.Nonce
174	}
175	idxsNonce = idxsNonceFromPoolL2Txs(pooll2txs)
176	assert.Equal(t, len(expectedIdxsNonce), len(idxsNonce))
177	for _, idxNonce := range idxsNonce {
178		nonce := expectedIdxsNonce[idxNonce.Idx]
179		assert.Equal(t, nonce, idxNonce.Nonce)
180	}
181}
182
183func TestPoolMarkInvalidOldNonces(t *testing.T) {
184	l2DB := newL2DB(t)
185	stateDB := newStateDB(t)
186
187	set0 := `
188		Type: Blockchain
189
190		CreateAccountDeposit(0) A: 1000 // Idx=256
191		CreateAccountDeposit(0) B: 1000 // Idx=257
192		CreateAccountDeposit(0) C: 1000 // Idx=258
193		CreateAccountDeposit(0) D: 1000 // Idx=259
194
195		> batchL1
196		> batchL1
197		> block
198	`
199	tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
200	blocks, err := tc.GenerateBlocks(set0)
201	require.NoError(t, err)
202	tilCfgExtra := til.ConfigExtra{
203		CoordUser: "A",
204	}
205	// Call FillBlocksExtra to fill `Batch.CreatedAccounts`
206	err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
207	require.NoError(t, err)
208	require.Equal(t, 4, len(blocks[0].Rollup.Batches[1].CreatedAccounts)) // sanity check
209
210	for _, acc := range blocks[0].Rollup.Batches[1].CreatedAccounts {
211		_, err := stateDB.CreateAccount(acc.Idx, &acc) //nolint:gosec
212		require.NoError(t, err)
213	}
214
215	setPool0 := `
216		Type: PoolL2
217		PoolTransfer(0) A-B: 10 (1)
218		PoolTransfer(0) A-C: 10 (1)
219		PoolTransfer(0) A-D: 10 (1)
220		PoolTransfer(0) B-A: 10 (1)
221		PoolTransfer(0) B-C: 10 (1)
222		PoolTransfer(0) C-A: 10 (1)
223	`
224	// We expect the following nonces
225	nonces0 := map[string]int64{"A": 3, "B": 2, "C": 1, "D": 0}
226	l2txs0, err := tc.GeneratePoolL2Txs(setPool0)
227	assert.Nil(t, err)
228	assert.Equal(t, 6, len(l2txs0))
229	for _, tx := range l2txs0 {
230		require.NoError(t, l2DB.AddTxTest(&tx)) //nolint:gosec
231	}
232
233	// Update the accounts in the StateDB, making the txs in the setPool0
234	// invalid
235	for name, user := range tc.Users {
236		for _, _acc := range user.Accounts {
237			require.Equal(t, common.Nonce(nonces0[name]), _acc.Nonce) // sanity check
238			acc, err := stateDB.GetAccount(_acc.Idx)
239			require.NoError(t, err)
240			require.Equal(t, common.Nonce(0), acc.Nonce) // sanity check
241			acc.Nonce = _acc.Nonce
242			_, err = stateDB.UpdateAccount(acc.Idx, acc)
243			require.NoError(t, err)
244		}
245	}
246
247	setPool1 := `
248		Type: PoolL2
249		PoolTransfer(0) A-B: 10 (1)
250		PoolTransfer(0) A-C: 10 (1)
251		PoolTransfer(0) A-D: 10 (1)
252		PoolTransfer(0) B-A: 10 (1)
253		PoolTransfer(0) B-C: 10 (1)
254		PoolTransfer(0) C-A: 10 (1)
255	`
256	// We expect the following nonces
257	nonces1 := map[string]int64{"A": 6, "B": 4, "C": 2, "D": 0}
258	l2txs1, err := tc.GeneratePoolL2Txs(setPool1)
259	require.NoError(t, err)
260	assert.Equal(t, 6, len(l2txs1))
261	for _, tx := range l2txs1 {
262		require.NoError(t, l2DB.AddTxTest(&tx)) //nolint:gosec
263	}
264
265	for name, user := range tc.Users {
266		for _, _acc := range user.Accounts {
267			require.Equal(t, common.Nonce(nonces1[name]), _acc.Nonce) // sanity check
268			acc, err := stateDB.GetAccount(_acc.Idx)
269			require.NoError(t, err)
270			require.Equal(t, common.Nonce(nonces0[name]), acc.Nonce) // sanity check
271		}
272	}
273
274	// Now we should have 12 txs in the pool, all marked as pending.  Since
275	// we updated the stateDB with the nonces after setPool0, the first 6
276	// txs will be marked as invalid
277
278	pendingTxs, err := l2DB.GetPendingTxs()
279	require.NoError(t, err)
280	assert.Equal(t, 12, len(pendingTxs))
281
282	batchNum := common.BatchNum(1)
283	err = poolMarkInvalidOldNonces(l2DB, stateDB, batchNum)
284	require.NoError(t, err)
285
286	pendingTxs, err = l2DB.GetPendingTxs()
287	require.NoError(t, err)
288	assert.Equal(t, 6, len(pendingTxs))
289}