|
|
@ -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() |
|
|
|
require.Nil(t, err) |
|
|
|
//
|
|
|
|
// Reorg test
|
|
|
|
//
|
|
|
|
|
|
|
|
lastSavedBlock.EthBlockNum++ |
|
|
|
err = historyDB.AddBlock(lastSavedBlock) |
|
|
|
require.Nil(t, err) |
|
|
|
// 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 |
|
|
|
|
|
|
|
lastSavedBlock.EthBlockNum++ |
|
|
|
err = historyDB.AddBlock(lastSavedBlock) |
|
|
|
require.Nil(t, err) |
|
|
|
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) |
|
|
|
|
|
|
|
log.Debugf("Wait for the blockchain to generate some blocks...") |
|
|
|
time.Sleep(40 * time.Second) |
|
|
|
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) |
|
|
|
|
|
|
|
err = s.Sync() |
|
|
|
// 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) |
|
|
|
} |
|
|
|
|
|
|
|
dbBlock, err = s.historyDB.GetLastBlock() |
|
|
|
require.Nil(t, err) |
|
|
|
assert.Equal(t, int64(6), dbBlock.EthBlockNum) |
|
|
|
|
|
|
|
// 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) |
|
|
|
} |