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