arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/coordinator/pipeline_test.go
coordinator/pipeline_test.goRaw
  1package coordinator
  2
  3import (
  4	"context"
  5	"fmt"
  6	"io/ioutil"
  7	"math/big"
  8	"os"
  9	"testing"
 10
 11	ethKeystore "github.com/ethereum/go-ethereum/accounts/keystore"
 12	ethCommon "github.com/ethereum/go-ethereum/common"
 13	"github.com/ethereum/go-ethereum/crypto"
 14	"github.com/ethereum/go-ethereum/ethclient"
 15	"github.com/hermeznetwork/hermez-node/common"
 16	"github.com/hermeznetwork/hermez-node/db/historydb"
 17	"github.com/hermeznetwork/hermez-node/db/statedb"
 18	"github.com/hermeznetwork/hermez-node/eth"
 19	"github.com/hermeznetwork/hermez-node/prover"
 20	"github.com/hermeznetwork/hermez-node/synchronizer"
 21	"github.com/hermeznetwork/hermez-node/test"
 22	"github.com/hermeznetwork/hermez-node/test/til"
 23	"github.com/iden3/go-merkletree"
 24	"github.com/stretchr/testify/assert"
 25	"github.com/stretchr/testify/require"
 26)
 27
 28func newBigInt(s string) *big.Int {
 29	v, ok := new(big.Int).SetString(s, 10)
 30	if !ok {
 31		panic(fmt.Errorf("Can't set big.Int from %s", s))
 32	}
 33	return v
 34}
 35
 36func TestPipelineShouldL1L2Batch(t *testing.T) {
 37	ethClientSetup := test.NewClientSetupExample()
 38	ethClientSetup.ChainID = big.NewInt(int64(chainID))
 39
 40	var timer timer
 41	ctx := context.Background()
 42	ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
 43	modules := newTestModules(t)
 44	var stats synchronizer.Stats
 45	coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
 46	pipeline, err := coord.newPipeline(ctx)
 47	require.NoError(t, err)
 48	pipeline.vars = coord.vars
 49
 50	// Check that the parameters are the ones we expect and use in this test
 51	require.Equal(t, 0.5, pipeline.cfg.L1BatchTimeoutPerc)
 52	require.Equal(t, int64(10), ethClientSetup.RollupVariables.ForgeL1L2BatchTimeout)
 53	l1BatchTimeoutPerc := pipeline.cfg.L1BatchTimeoutPerc
 54	l1BatchTimeout := ethClientSetup.RollupVariables.ForgeL1L2BatchTimeout
 55
 56	startBlock := int64(100)
 57	// Empty batchInfo to pass to shouldL1L2Batch() which sets debug information
 58	batchInfo := BatchInfo{}
 59
 60	//
 61	// No scheduled L1Batch
 62	//
 63
 64	// Last L1Batch was a long time ago
 65	stats.Eth.LastBlock.Num = startBlock
 66	stats.Sync.LastBlock = stats.Eth.LastBlock
 67	stats.Sync.LastL1BatchBlock = 0
 68	pipeline.stats = stats
 69	assert.Equal(t, true, pipeline.shouldL1L2Batch(&batchInfo))
 70
 71	stats.Sync.LastL1BatchBlock = startBlock
 72
 73	// We are are one block before the timeout range * 0.5
 74	stats.Eth.LastBlock.Num = startBlock - 1 + int64(float64(l1BatchTimeout-1)*l1BatchTimeoutPerc) - 1
 75	stats.Sync.LastBlock = stats.Eth.LastBlock
 76	pipeline.stats = stats
 77	assert.Equal(t, false, pipeline.shouldL1L2Batch(&batchInfo))
 78
 79	// We are are at timeout range * 0.5
 80	stats.Eth.LastBlock.Num = startBlock - 1 + int64(float64(l1BatchTimeout-1)*l1BatchTimeoutPerc)
 81	stats.Sync.LastBlock = stats.Eth.LastBlock
 82	pipeline.stats = stats
 83	assert.Equal(t, true, pipeline.shouldL1L2Batch(&batchInfo))
 84
 85	//
 86	// Scheduled L1Batch
 87	//
 88	pipeline.state.lastScheduledL1BatchBlockNum = startBlock
 89	stats.Sync.LastL1BatchBlock = startBlock - 10
 90
 91	// We are are one block before the timeout range * 0.5
 92	stats.Eth.LastBlock.Num = startBlock - 1 + int64(float64(l1BatchTimeout-1)*l1BatchTimeoutPerc) - 1
 93	stats.Sync.LastBlock = stats.Eth.LastBlock
 94	pipeline.stats = stats
 95	assert.Equal(t, false, pipeline.shouldL1L2Batch(&batchInfo))
 96
 97	// We are are at timeout range * 0.5
 98	stats.Eth.LastBlock.Num = startBlock - 1 + int64(float64(l1BatchTimeout-1)*l1BatchTimeoutPerc)
 99	stats.Sync.LastBlock = stats.Eth.LastBlock
100	pipeline.stats = stats
101	assert.Equal(t, true, pipeline.shouldL1L2Batch(&batchInfo))
102}
103
104const testTokensLen = 3
105const testUsersLen = 4
106
107func preloadSync(t *testing.T, ethClient *test.Client, sync *synchronizer.Synchronizer,
108	historyDB *historydb.HistoryDB, stateDB *statedb.StateDB) *til.Context {
109	// Create a set with `testTokensLen` tokens and for each token
110	// `testUsersLen` accounts.
111	var set []til.Instruction
112	// set = append(set, til.Instruction{Typ: "Blockchain"})
113	for tokenID := 1; tokenID < testTokensLen; tokenID++ {
114		set = append(set, til.Instruction{
115			Typ:     til.TypeAddToken,
116			TokenID: common.TokenID(tokenID),
117		})
118	}
119	depositAmount, ok := new(big.Int).SetString("10225000000000000000000000000000000", 10)
120	require.True(t, ok)
121	for tokenID := 0; tokenID < testTokensLen; tokenID++ {
122		for user := 0; user < testUsersLen; user++ {
123			set = append(set, til.Instruction{
124				Typ:           common.TxTypeCreateAccountDeposit,
125				TokenID:       common.TokenID(tokenID),
126				DepositAmount: depositAmount,
127				From:          fmt.Sprintf("User%d", user),
128			})
129		}
130	}
131	set = append(set, til.Instruction{Typ: til.TypeNewBatchL1})
132	set = append(set, til.Instruction{Typ: til.TypeNewBatchL1})
133	set = append(set, til.Instruction{Typ: til.TypeNewBlock})
134
135	tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
136	blocks, err := tc.GenerateBlocksFromInstructions(set)
137	require.NoError(t, err)
138	require.NotNil(t, blocks)
139	// Set StateRoots for batches manually (til doesn't set it)
140	blocks[0].Rollup.Batches[0].Batch.StateRoot =
141		newBigInt("0")
142	blocks[0].Rollup.Batches[1].Batch.StateRoot =
143		newBigInt("10941365282189107056349764238909072001483688090878331371699519307087372995595")
144
145	ethAddTokens(blocks, ethClient)
146	err = ethClient.CtlAddBlocks(blocks)
147	require.NoError(t, err)
148
149	ctx := context.Background()
150	for {
151		syncBlock, discards, err := sync.Sync(ctx, nil)
152		require.NoError(t, err)
153		require.Nil(t, discards)
154		if syncBlock == nil {
155			break
156		}
157	}
158	dbTokens, err := historyDB.GetAllTokens()
159	require.Nil(t, err)
160	require.Equal(t, testTokensLen, len(dbTokens))
161
162	dbAccounts, err := historyDB.GetAllAccounts()
163	require.Nil(t, err)
164	require.Equal(t, testTokensLen*testUsersLen, len(dbAccounts))
165
166	sdbAccounts, err := stateDB.TestGetAccounts()
167	require.Nil(t, err)
168	require.Equal(t, testTokensLen*testUsersLen, len(sdbAccounts))
169
170	return tc
171}
172
173func TestPipelineForgeBatchWithTxs(t *testing.T) {
174	ethClientSetup := test.NewClientSetupExample()
175	ethClientSetup.ChainID = big.NewInt(int64(chainID))
176
177	var timer timer
178	ctx := context.Background()
179	ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
180	modules := newTestModules(t)
181	coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
182	sync := newTestSynchronizer(t, ethClient, ethClientSetup, modules)
183
184	// preload the synchronier (via the test ethClient) some tokens and
185	// users with positive balances
186	tilCtx := preloadSync(t, ethClient, sync, modules.historyDB, modules.stateDB)
187	syncStats := sync.Stats()
188	batchNum := syncStats.Sync.LastBatch.BatchNum
189	syncSCVars := sync.SCVars()
190
191	pipeline, err := coord.newPipeline(ctx)
192	require.NoError(t, err)
193
194	// Insert some l2txs in the Pool
195	setPool := `
196Type: PoolL2
197
198PoolTransfer(0) User0-User1: 100 (126)
199PoolTransfer(0) User1-User2: 200 (126)
200PoolTransfer(0) User2-User3: 300 (126)
201	`
202	l2txs, err := tilCtx.GeneratePoolL2Txs(setPool)
203	require.NoError(t, err)
204	for _, tx := range l2txs {
205		err := modules.l2DB.AddTxTest(&tx) //nolint:gosec
206		require.NoError(t, err)
207	}
208
209	err = pipeline.reset(batchNum, syncStats, &synchronizer.SCVariables{
210		Rollup:   *syncSCVars.Rollup,
211		Auction:  *syncSCVars.Auction,
212		WDelayer: *syncSCVars.WDelayer,
213	})
214	require.NoError(t, err)
215	// Sanity check
216	sdbAccounts, err := pipeline.txSelector.LocalAccountsDB().TestGetAccounts()
217	require.Nil(t, err)
218	require.Equal(t, testTokensLen*testUsersLen, len(sdbAccounts))
219
220	// Sanity check
221	sdbAccounts, err = pipeline.batchBuilder.LocalStateDB().TestGetAccounts()
222	require.Nil(t, err)
223	require.Equal(t, testTokensLen*testUsersLen, len(sdbAccounts))
224
225	// Sanity check
226	require.Equal(t, modules.stateDB.MT.Root(),
227		pipeline.batchBuilder.LocalStateDB().MT.Root())
228
229	batchNum++
230
231	batchInfo, err := pipeline.forgeBatch(batchNum)
232	require.NoError(t, err)
233	assert.Equal(t, 3, len(batchInfo.L2Txs))
234
235	batchNum++
236	batchInfo, err = pipeline.forgeBatch(batchNum)
237	require.NoError(t, err)
238	assert.Equal(t, 0, len(batchInfo.L2Txs))
239}
240
241func TestEthRollupForgeBatch(t *testing.T) {
242	if os.Getenv("TEST_ROLLUP_FORGE_BATCH") == "" {
243		return
244	}
245	const web3URL = "http://localhost:8545"
246	const password = "test"
247	addr := ethCommon.HexToAddress("0xb4124ceb3451635dacedd11767f004d8a28c6ee7")
248	sk, err := crypto.HexToECDSA(
249		"a8a54b2d8197bc0b19bb8a084031be71835580a01e70a45a13babd16c9bc1563")
250	require.NoError(t, err)
251	rollupAddr := ethCommon.HexToAddress("0x8EEaea23686c319133a7cC110b840d1591d9AeE0")
252	pathKeystore, err := ioutil.TempDir("", "tmpKeystore")
253	require.NoError(t, err)
254	deleteme = append(deleteme, pathKeystore)
255	ctx := context.Background()
256	batchInfo := &BatchInfo{}
257	proofClient := &prover.MockClient{}
258	chainID := uint16(0)
259
260	ethClient, err := ethclient.Dial(web3URL)
261	require.NoError(t, err)
262	ethCfg := eth.EthereumConfig{
263		CallGasLimit: 300000,
264		GasPriceDiv:  100,
265	}
266	scryptN := ethKeystore.LightScryptN
267	scryptP := ethKeystore.LightScryptP
268	keyStore := ethKeystore.NewKeyStore(pathKeystore,
269		scryptN, scryptP)
270	account, err := keyStore.ImportECDSA(sk, password)
271	require.NoError(t, err)
272	require.Equal(t, account.Address, addr)
273	err = keyStore.Unlock(account, password)
274	require.NoError(t, err)
275
276	client, err := eth.NewClient(ethClient, &account, keyStore, &eth.ClientConfig{
277		Ethereum: ethCfg,
278		Rollup: eth.RollupConfig{
279			Address: rollupAddr,
280		},
281		Auction: eth.AuctionConfig{
282			Address: ethCommon.Address{},
283			TokenHEZ: eth.TokenConfig{
284				Address: ethCommon.Address{},
285				Name:    "HEZ",
286			},
287		},
288		WDelayer: eth.WDelayerConfig{
289			Address: ethCommon.Address{},
290		},
291	})
292	require.NoError(t, err)
293
294	zkInputs := common.NewZKInputs(chainID, 100, 24, 512, 32, big.NewInt(1))
295	zkInputs.Metadata.NewStateRootRaw = &merkletree.Hash{1}
296	zkInputs.Metadata.NewExitRootRaw = &merkletree.Hash{2}
297	batchInfo.ZKInputs = zkInputs
298	err = proofClient.CalculateProof(ctx, batchInfo.ZKInputs)
299	require.NoError(t, err)
300
301	proof, pubInputs, err := proofClient.GetProof(ctx)
302	require.NoError(t, err)
303	batchInfo.Proof = proof
304	batchInfo.PublicInputs = pubInputs
305
306	batchInfo.ForgeBatchArgs = prepareForgeBatchArgs(batchInfo)
307	auth, err := client.NewAuth()
308	require.NoError(t, err)
309	_, err = client.RollupForgeBatch(batchInfo.ForgeBatchArgs, auth)
310	require.NoError(t, err)
311	batchInfo.Proof = proof
312}