|
|
|
|
@@ -7,6 +7,7 @@ import (
|
|
|
|
|
"io/ioutil"
|
|
|
|
|
"math/big"
|
|
|
|
|
"os"
|
|
|
|
|
"sort"
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
ethCommon "github.com/ethereum/go-ethereum/common"
|
|
|
|
|
@@ -34,6 +35,10 @@ func (t *timer) Time() int64 {
|
|
|
|
|
return currentTime
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func accountsCmp(accounts []common.Account) func(i, j int) bool {
|
|
|
|
|
return func(i, j int) bool { return accounts[i].Idx < accounts[j].Idx }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check Sync output and HistoryDB state against expected values generated by
|
|
|
|
|
// til
|
|
|
|
|
func checkSyncBlock(t *testing.T, s *Synchronizer, blockNum int, block, syncBlock *common.BlockData) {
|
|
|
|
|
@@ -194,6 +199,80 @@ func checkSyncBlock(t *testing.T, s *Synchronizer, blockNum int, block, syncBloc
|
|
|
|
|
assert.Equal(t, &exit, dbExit) //nolint:gosec
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Compare accounts from HistoryDB with StateDB (they should match)
|
|
|
|
|
dbAccounts, err := s.historyDB.GetAllAccounts()
|
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
sdbAccounts, err := s.stateDB.GetAccounts()
|
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
assertEqualAccountsHistoryDBStateDB(t, dbAccounts, sdbAccounts)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func assertEqualAccountsHistoryDBStateDB(t *testing.T, hdbAccs, sdbAccs []common.Account) {
|
|
|
|
|
assert.Equal(t, len(hdbAccs), len(sdbAccs))
|
|
|
|
|
sort.SliceStable(hdbAccs, accountsCmp(hdbAccs))
|
|
|
|
|
sort.SliceStable(sdbAccs, accountsCmp(sdbAccs))
|
|
|
|
|
for i := range hdbAccs {
|
|
|
|
|
hdbAcc := hdbAccs[i]
|
|
|
|
|
sdbAcc := sdbAccs[i]
|
|
|
|
|
assert.Equal(t, hdbAcc.Idx, sdbAcc.Idx)
|
|
|
|
|
assert.Equal(t, hdbAcc.TokenID, sdbAcc.TokenID)
|
|
|
|
|
assert.Equal(t, hdbAcc.EthAddr, sdbAcc.EthAddr)
|
|
|
|
|
assert.Equal(t, hdbAcc.PublicKey, sdbAcc.PublicKey)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ethAddTokens adds the tokens from the blocks to the blockchain
|
|
|
|
|
func ethAddTokens(blocks []common.BlockData, client *test.Client) {
|
|
|
|
|
for _, block := range blocks {
|
|
|
|
|
for _, token := range block.Rollup.AddedTokens {
|
|
|
|
|
consts := eth.ERC20Consts{
|
|
|
|
|
Name: fmt.Sprintf("Token %d", token.TokenID),
|
|
|
|
|
Symbol: fmt.Sprintf("TK%d", token.TokenID),
|
|
|
|
|
Decimals: 18,
|
|
|
|
|
}
|
|
|
|
|
tokenConsts[token.TokenID] = consts
|
|
|
|
|
client.CtlAddERC20(token.EthAddr, consts)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ethAddBlocks adds block data to the smart contracts
|
|
|
|
|
func ethAddBlocks(t *testing.T, blocks []common.BlockData,
|
|
|
|
|
client *test.Client, clientSetup *test.ClientSetup) {
|
|
|
|
|
for _, block := range blocks {
|
|
|
|
|
for _, token := range block.Rollup.AddedTokens {
|
|
|
|
|
_, err := client.RollupAddTokenSimple(token.EthAddr, clientSetup.RollupVariables.FeeAddToken)
|
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
}
|
|
|
|
|
for _, tx := range block.Rollup.L1UserTxs {
|
|
|
|
|
client.CtlSetAddr(tx.FromEthAddr)
|
|
|
|
|
_, err := client.RollupL1UserTxERC20ETH(tx.FromBJJ, int64(tx.FromIdx), tx.LoadAmount, tx.Amount,
|
|
|
|
|
uint32(tx.TokenID), int64(tx.ToIdx))
|
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
}
|
|
|
|
|
client.CtlSetAddr(clientSetup.AuctionVariables.BootCoordinator)
|
|
|
|
|
for _, batch := range block.Rollup.Batches {
|
|
|
|
|
_, err := client.RollupForgeBatch(ð.RollupForgeBatchArgs{
|
|
|
|
|
NewLastIdx: batch.Batch.LastIdx,
|
|
|
|
|
NewStRoot: batch.Batch.StateRoot,
|
|
|
|
|
NewExitRoot: batch.Batch.ExitRoot,
|
|
|
|
|
L1CoordinatorTxs: batch.L1CoordinatorTxs,
|
|
|
|
|
L1CoordinatorTxsAuths: [][]byte{}, // Intentionally empty
|
|
|
|
|
L2TxsData: batch.L2Txs,
|
|
|
|
|
FeeIdxCoordinator: batch.Batch.FeeIdxsCoordinator,
|
|
|
|
|
// Circuit selector
|
|
|
|
|
VerifierIdx: 0, // Intentionally empty
|
|
|
|
|
L1Batch: batch.L1Batch,
|
|
|
|
|
ProofA: [2]*big.Int{}, // Intentionally empty
|
|
|
|
|
ProofB: [2][2]*big.Int{}, // Intentionally empty
|
|
|
|
|
ProofC: [2]*big.Int{}, // Intentionally empty
|
|
|
|
|
})
|
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
}
|
|
|
|
|
// Mine block and sync
|
|
|
|
|
client.CtlMineBlock()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSync(t *testing.T) {
|
|
|
|
|
@@ -322,62 +401,14 @@ func TestSync(t *testing.T) {
|
|
|
|
|
require.Equal(t, 3, len(blocks[i].Rollup.Batches[0].L2Txs))
|
|
|
|
|
|
|
|
|
|
// Generate extra required data
|
|
|
|
|
for _, block := range blocks {
|
|
|
|
|
for _, token := range block.Rollup.AddedTokens {
|
|
|
|
|
consts := eth.ERC20Consts{
|
|
|
|
|
Name: fmt.Sprintf("Token %d", token.TokenID),
|
|
|
|
|
Symbol: fmt.Sprintf("TK%d", token.TokenID),
|
|
|
|
|
Decimals: 18,
|
|
|
|
|
}
|
|
|
|
|
tokenConsts[token.TokenID] = consts
|
|
|
|
|
client.CtlAddERC20(token.EthAddr, consts)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ethAddTokens(blocks, client)
|
|
|
|
|
|
|
|
|
|
err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
|
|
|
|
|
assert.Nil(t, err)
|
|
|
|
|
tc.FillBlocksL1UserTxsBatchNum(blocks)
|
|
|
|
|
|
|
|
|
|
// Add block data to the smart contracts
|
|
|
|
|
for _, block := range blocks {
|
|
|
|
|
for _, token := range block.Rollup.AddedTokens {
|
|
|
|
|
_, err := client.RollupAddTokenSimple(token.EthAddr, clientSetup.RollupVariables.FeeAddToken)
|
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
}
|
|
|
|
|
for _, tx := range block.Rollup.L1UserTxs {
|
|
|
|
|
client.CtlSetAddr(tx.FromEthAddr)
|
|
|
|
|
_, err := client.RollupL1UserTxERC20ETH(tx.FromBJJ, int64(tx.FromIdx), tx.LoadAmount, tx.Amount,
|
|
|
|
|
uint32(tx.TokenID), int64(tx.ToIdx))
|
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
}
|
|
|
|
|
client.CtlSetAddr(bootCoordAddr)
|
|
|
|
|
// feeIdxCoordinator := []common.Idx{}
|
|
|
|
|
// if block.Block.EthBlockNum > 2 {
|
|
|
|
|
// // After blockNum=2 we have some accounts, use them as
|
|
|
|
|
// // coordinator owned to receive fees.
|
|
|
|
|
// feeIdxCoordinator = []common.Idx{common.Idx(256), common.Idx(259)}
|
|
|
|
|
// }
|
|
|
|
|
for _, batch := range block.Rollup.Batches {
|
|
|
|
|
_, err := client.RollupForgeBatch(ð.RollupForgeBatchArgs{
|
|
|
|
|
NewLastIdx: batch.Batch.LastIdx,
|
|
|
|
|
NewStRoot: batch.Batch.StateRoot,
|
|
|
|
|
NewExitRoot: batch.Batch.ExitRoot,
|
|
|
|
|
L1CoordinatorTxs: batch.L1CoordinatorTxs,
|
|
|
|
|
L1CoordinatorTxsAuths: [][]byte{}, // Intentionally empty
|
|
|
|
|
L2TxsData: batch.L2Txs,
|
|
|
|
|
FeeIdxCoordinator: batch.Batch.FeeIdxsCoordinator,
|
|
|
|
|
// Circuit selector
|
|
|
|
|
VerifierIdx: 0, // Intentionally empty
|
|
|
|
|
L1Batch: batch.L1Batch,
|
|
|
|
|
ProofA: [2]*big.Int{}, // Intentionally empty
|
|
|
|
|
ProofB: [2][2]*big.Int{}, // Intentionally empty
|
|
|
|
|
ProofC: [2]*big.Int{}, // Intentionally empty
|
|
|
|
|
})
|
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
}
|
|
|
|
|
// Mine block and sync
|
|
|
|
|
client.CtlMineBlock()
|
|
|
|
|
}
|
|
|
|
|
ethAddBlocks(t, blocks, client, clientSetup)
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Sync to synchronize the current state from the test smart contracts,
|
|
|
|
|
@@ -467,25 +498,94 @@ func TestSync(t *testing.T) {
|
|
|
|
|
assert.Equal(t, auctionVars, dbAuctionVars)
|
|
|
|
|
assert.Equal(t, wDelayerVars, dbWDelayerVars)
|
|
|
|
|
|
|
|
|
|
// TODO: Reorg will be properly tested once we have the mock ethClient implemented
|
|
|
|
|
/*
|
|
|
|
|
// Force a Reorg
|
|
|
|
|
lastSavedBlock, err := historyDB.GetLastBlock()
|
|
|
|
|
//
|
|
|
|
|
// Reorg test
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
// Redo blocks 2-5 (as a reorg) only leaving:
|
|
|
|
|
// - 2 create account transactions
|
|
|
|
|
// - 2 add tokens
|
|
|
|
|
// We add a 6th block so that the synchronizer can detect the reorg
|
|
|
|
|
set2 := `
|
|
|
|
|
Type: Blockchain
|
|
|
|
|
|
|
|
|
|
AddToken(1)
|
|
|
|
|
AddToken(2)
|
|
|
|
|
|
|
|
|
|
CreateAccountDeposit(1) C: 2000 // Idx=256+1=257
|
|
|
|
|
|
|
|
|
|
CreateAccountCoordinator(1) A // Idx=256+0=256
|
|
|
|
|
|
|
|
|
|
> batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{1}
|
|
|
|
|
> batchL1 // forge defined L1UserTxs{1}, freeze L1UserTxs{nil}
|
|
|
|
|
> block // blockNum=2
|
|
|
|
|
> block // blockNum=3
|
|
|
|
|
> block // blockNum=4
|
|
|
|
|
> block // blockNum=5
|
|
|
|
|
> block // blockNum=6
|
|
|
|
|
`
|
|
|
|
|
tc = til.NewContext(common.RollupConstMaxL1UserTx)
|
|
|
|
|
tilCfgExtra = til.ConfigExtra{
|
|
|
|
|
BootCoordAddr: bootCoordAddr,
|
|
|
|
|
CoordUser: "A",
|
|
|
|
|
}
|
|
|
|
|
blocks, err = tc.GenerateBlocks(set2)
|
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
|
|
|
|
|
for i := 0; i < 4; i++ {
|
|
|
|
|
client.CtlRollback()
|
|
|
|
|
}
|
|
|
|
|
blockNum := client.CtlCurrentBlock()
|
|
|
|
|
require.Equal(t, int64(1), blockNum)
|
|
|
|
|
|
|
|
|
|
// Generate extra required data
|
|
|
|
|
ethAddTokens(blocks, client)
|
|
|
|
|
|
|
|
|
|
err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
|
|
|
|
|
assert.Nil(t, err)
|
|
|
|
|
tc.FillBlocksL1UserTxsBatchNum(blocks)
|
|
|
|
|
|
|
|
|
|
// Add block data to the smart contracts
|
|
|
|
|
ethAddBlocks(t, blocks, client, clientSetup)
|
|
|
|
|
|
|
|
|
|
// First sync detects the reorg and discards 4 blocks
|
|
|
|
|
syncBlock, discards, err = s.Sync2(ctx, nil)
|
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
expetedDiscards := int64(4)
|
|
|
|
|
require.Equal(t, &expetedDiscards, discards)
|
|
|
|
|
require.Nil(t, syncBlock)
|
|
|
|
|
|
|
|
|
|
// At this point, the DB only has data up to block 1
|
|
|
|
|
dbBlock, err := s.historyDB.GetLastBlock()
|
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
assert.Equal(t, int64(1), dbBlock.EthBlockNum)
|
|
|
|
|
|
|
|
|
|
// Accounts in HistoryDB and StateDB must be empty
|
|
|
|
|
dbAccounts, err := s.historyDB.GetAllAccounts()
|
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
sdbAccounts, err := s.stateDB.GetAccounts()
|
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
assert.Equal(t, 0, len(dbAccounts))
|
|
|
|
|
assertEqualAccountsHistoryDBStateDB(t, dbAccounts, sdbAccounts)
|
|
|
|
|
|
|
|
|
|
// Sync blocks 2-6
|
|
|
|
|
for i := 0; i < 5; i++ {
|
|
|
|
|
syncBlock, discards, err = s.Sync2(ctx, nil)
|
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
require.Nil(t, discards)
|
|
|
|
|
require.NotNil(t, syncBlock)
|
|
|
|
|
assert.Equal(t, int64(2+i), syncBlock.Block.EthBlockNum)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lastSavedBlock.EthBlockNum++
|
|
|
|
|
err = historyDB.AddBlock(lastSavedBlock)
|
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
dbBlock, err = s.historyDB.GetLastBlock()
|
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
assert.Equal(t, int64(6), dbBlock.EthBlockNum)
|
|
|
|
|
|
|
|
|
|
lastSavedBlock.EthBlockNum++
|
|
|
|
|
err = historyDB.AddBlock(lastSavedBlock)
|
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
|
|
|
|
|
log.Debugf("Wait for the blockchain to generate some blocks...")
|
|
|
|
|
time.Sleep(40 * time.Second)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err = s.Sync()
|
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
*/
|
|
|
|
|
// Accounts in HistoryDB and StateDB is only 2 entries
|
|
|
|
|
dbAccounts, err = s.historyDB.GetAllAccounts()
|
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
sdbAccounts, err = s.stateDB.GetAccounts()
|
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
assert.Equal(t, 2, len(dbAccounts))
|
|
|
|
|
assertEqualAccountsHistoryDBStateDB(t, dbAccounts, sdbAccounts)
|
|
|
|
|
}
|
|
|
|
|
|