|
package txselector
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
ethCommon "github.com/ethereum/go-ethereum/common"
|
|
ethCrypto "github.com/ethereum/go-ethereum/crypto"
|
|
"github.com/hermeznetwork/hermez-node/common"
|
|
dbUtils "github.com/hermeznetwork/hermez-node/db"
|
|
"github.com/hermeznetwork/hermez-node/db/historydb"
|
|
"github.com/hermeznetwork/hermez-node/db/l2db"
|
|
"github.com/hermeznetwork/hermez-node/db/statedb"
|
|
"github.com/hermeznetwork/hermez-node/log"
|
|
"github.com/hermeznetwork/hermez-node/test"
|
|
"github.com/hermeznetwork/hermez-node/test/til"
|
|
"github.com/hermeznetwork/hermez-node/test/txsets"
|
|
"github.com/hermeznetwork/hermez-node/txprocessor"
|
|
"github.com/jmoiron/sqlx"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func initTest(t *testing.T, chainID uint16, hermezContractAddr ethCommon.Address,
|
|
coordUser *til.User) (*TxSelector, *historydb.HistoryDB) {
|
|
pass := os.Getenv("POSTGRES_PASS")
|
|
db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
|
|
require.NoError(t, err)
|
|
l2DB := l2db.NewL2DB(db, db, 10, 100, 0.0, 1000.0, 24*time.Hour, nil)
|
|
|
|
historyDB := historydb.NewHistoryDB(db, db, nil)
|
|
|
|
dir, err := ioutil.TempDir("", "tmpdb")
|
|
require.NoError(t, err)
|
|
defer assert.NoError(t, os.RemoveAll(dir))
|
|
syncStateDB, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128,
|
|
Type: statedb.TypeTxSelector, NLevels: 0})
|
|
require.NoError(t, err)
|
|
|
|
txselDir, err := ioutil.TempDir("", "tmpTxSelDB")
|
|
require.NoError(t, err)
|
|
defer assert.NoError(t, os.RemoveAll(dir))
|
|
|
|
// use Til Coord keys for tests compatibility
|
|
coordAccount := &CoordAccount{
|
|
Addr: coordUser.Addr,
|
|
BJJ: coordUser.BJJ.Public().Compress(),
|
|
AccountCreationAuth: nil,
|
|
}
|
|
// fmt.Printf("%v\n", coordAccount)
|
|
auth := common.AccountCreationAuth{
|
|
EthAddr: coordUser.Addr,
|
|
BJJ: coordUser.BJJ.Public().Compress(),
|
|
}
|
|
err = auth.Sign(func(hash []byte) ([]byte, error) {
|
|
return ethCrypto.Sign(hash, coordUser.EthSk)
|
|
}, chainID, hermezContractAddr)
|
|
assert.NoError(t, err)
|
|
coordAccount.AccountCreationAuth = auth.Signature
|
|
|
|
txsel, err := NewTxSelector(coordAccount, txselDir, syncStateDB, l2DB)
|
|
require.NoError(t, err)
|
|
|
|
test.WipeDB(txsel.l2db.DB())
|
|
|
|
return txsel, historyDB
|
|
}
|
|
|
|
func addAccCreationAuth(t *testing.T, tc *til.Context, txsel *TxSelector, chainID uint16,
|
|
hermezContractAddr ethCommon.Address, username string) []byte {
|
|
user := tc.Users[username]
|
|
auth := &common.AccountCreationAuth{
|
|
EthAddr: user.Addr,
|
|
BJJ: user.BJJ.Public().Compress(),
|
|
}
|
|
err := auth.Sign(func(hash []byte) ([]byte, error) {
|
|
return ethCrypto.Sign(hash, user.EthSk)
|
|
}, chainID, hermezContractAddr)
|
|
assert.NoError(t, err)
|
|
|
|
err = txsel.l2db.AddAccountCreationAuth(auth)
|
|
assert.NoError(t, err)
|
|
return auth.Signature
|
|
}
|
|
|
|
func addL2Txs(t *testing.T, txsel *TxSelector, poolL2Txs []common.PoolL2Tx) {
|
|
for i := 0; i < len(poolL2Txs); i++ {
|
|
err := txsel.l2db.AddTxTest(&poolL2Txs[i])
|
|
if err != nil {
|
|
log.Error(err)
|
|
}
|
|
require.NoError(t, err)
|
|
}
|
|
}
|
|
|
|
func addTokens(t *testing.T, tc *til.Context, db *sqlx.DB) {
|
|
var tokens []common.Token
|
|
for i := 0; i < int(tc.LastRegisteredTokenID); i++ {
|
|
tokens = append(tokens, common.Token{
|
|
TokenID: common.TokenID(i + 1),
|
|
EthBlockNum: 1,
|
|
EthAddr: ethCommon.BytesToAddress([]byte{byte(i + 1)}),
|
|
Name: strconv.Itoa(i),
|
|
Symbol: strconv.Itoa(i),
|
|
Decimals: 18,
|
|
})
|
|
}
|
|
|
|
hdb := historydb.NewHistoryDB(db, db, nil)
|
|
assert.NoError(t, hdb.AddBlock(&common.Block{
|
|
Num: 1,
|
|
}))
|
|
assert.NoError(t, hdb.AddTokens(tokens))
|
|
}
|
|
|
|
func checkBalance(t *testing.T, tc *til.Context, txsel *TxSelector, username string,
|
|
tokenid int, expected string) {
|
|
// Accounts.Idx does not match with the TxSelector tests as we are not
|
|
// using the Til L1CoordinatorTxs (as are generated by the TxSelector
|
|
// itself when processing the txs, so the Idxs does not match the Til
|
|
// idxs). But the Idx is obtained through StateDB.GetIdxByEthAddrBJJ
|
|
user := tc.Users[username]
|
|
idx, err := txsel.localAccountsDB.GetIdxByEthAddrBJJ(user.Addr, user.BJJ.Public().Compress(),
|
|
common.TokenID(tokenid))
|
|
require.NoError(t, err)
|
|
checkBalanceByIdx(t, txsel, idx, expected)
|
|
}
|
|
|
|
func checkBalanceByIdx(t *testing.T, txsel *TxSelector, idx common.Idx, expected string) {
|
|
acc, err := txsel.localAccountsDB.GetAccount(idx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, expected, acc.Balance.String())
|
|
}
|
|
|
|
// checkSortedByNonce takes as input testAccNonces map, and the array of
|
|
// common.PoolL2Txs, and checks if the nonces correspond to the accumulated
|
|
// values of the map. Also increases the Nonces computed on the map.
|
|
func checkSortedByNonce(t *testing.T, testAccNonces map[common.Idx]common.Nonce,
|
|
txs []common.PoolL2Tx) {
|
|
for _, tx := range txs {
|
|
assert.True(t, testAccNonces[tx.FromIdx] == tx.Nonce,
|
|
fmt.Sprintf("Idx: %d, expected: %d, tx.Nonce: %d",
|
|
tx.FromIdx, testAccNonces[tx.FromIdx], tx.Nonce))
|
|
testAccNonces[tx.FromIdx] = testAccNonces[tx.FromIdx] + 1
|
|
}
|
|
}
|
|
|
|
func TestGetL2TxSelectionMinimumFlow0(t *testing.T) {
|
|
chainID := uint16(0)
|
|
tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
|
|
// generate test transactions, the L1CoordinatorTxs generated by Til
|
|
// will be ignored at this test, as will be the TxSelector who
|
|
// generates them when needed
|
|
blocks, err := tc.GenerateBlocks(txsets.SetBlockchainMinimumFlow0)
|
|
assert.NoError(t, err)
|
|
|
|
hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
|
|
txsel, _ := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
|
|
|
|
// restart nonces of TilContext, as will be set by generating directly
|
|
// the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
|
|
tc.RestartNonces()
|
|
testAccNonces := make(map[common.Idx]common.Nonce)
|
|
|
|
// add tokens to HistoryDB to avoid breaking FK constrains
|
|
addTokens(t, tc, txsel.l2db.DB())
|
|
|
|
tpc := txprocessor.Config{
|
|
NLevels: 16,
|
|
MaxFeeTx: 10,
|
|
MaxTx: 20,
|
|
MaxL1Tx: 10,
|
|
ChainID: chainID,
|
|
}
|
|
|
|
// coordIdxs, accAuths, l1UserTxs, l1CoordTxs, l2Txs, err
|
|
|
|
log.Debug("block:0 batch:1")
|
|
l1UserTxs := []common.L1Tx{}
|
|
_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err :=
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 0, len(oL1UserTxs))
|
|
assert.Equal(t, 0, len(oL1CoordTxs))
|
|
assert.Equal(t, 0, len(oL2Txs))
|
|
assert.Equal(t, common.BatchNum(1), txsel.localAccountsDB.CurrentBatch())
|
|
assert.Equal(t, common.Idx(255), txsel.localAccountsDB.CurrentIdx())
|
|
|
|
log.Debug("block:0 batch:2")
|
|
l1UserTxs = []common.L1Tx{}
|
|
_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err =
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 0, len(oL1UserTxs))
|
|
assert.Equal(t, 0, len(oL1CoordTxs))
|
|
assert.Equal(t, 0, len(oL2Txs))
|
|
assert.Equal(t, common.BatchNum(2), txsel.localAccountsDB.CurrentBatch())
|
|
assert.Equal(t, common.Idx(255), txsel.localAccountsDB.CurrentIdx())
|
|
|
|
log.Debug("block:0 batch:3")
|
|
l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[2].Batch.ForgeL1TxsNum])
|
|
_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err =
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 2, len(oL1UserTxs))
|
|
assert.Equal(t, 0, len(oL1CoordTxs))
|
|
assert.Equal(t, 0, len(oL2Txs))
|
|
assert.Equal(t, common.BatchNum(3), txsel.localAccountsDB.CurrentBatch())
|
|
assert.Equal(t, common.Idx(257), txsel.localAccountsDB.CurrentIdx())
|
|
checkBalance(t, tc, txsel, "A", 0, "500")
|
|
checkBalance(t, tc, txsel, "C", 1, "0")
|
|
|
|
log.Debug("block:0 batch:4")
|
|
l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[3].Batch.ForgeL1TxsNum])
|
|
_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err =
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, len(oL1UserTxs))
|
|
assert.Equal(t, 0, len(oL1CoordTxs))
|
|
assert.Equal(t, 0, len(oL2Txs))
|
|
assert.Equal(t, common.BatchNum(4), txsel.localAccountsDB.CurrentBatch())
|
|
assert.Equal(t, common.Idx(258), txsel.localAccountsDB.CurrentIdx())
|
|
checkBalance(t, tc, txsel, "A", 0, "500")
|
|
checkBalance(t, tc, txsel, "A", 1, "500")
|
|
checkBalance(t, tc, txsel, "C", 1, "0")
|
|
|
|
log.Debug("block:0 batch:5")
|
|
l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[4].Batch.ForgeL1TxsNum])
|
|
_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err =
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 0, len(oL1UserTxs))
|
|
assert.Equal(t, 0, len(oL1CoordTxs))
|
|
assert.Equal(t, 0, len(oL2Txs))
|
|
assert.Equal(t, common.BatchNum(5), txsel.localAccountsDB.CurrentBatch())
|
|
assert.Equal(t, common.Idx(258), txsel.localAccountsDB.CurrentIdx())
|
|
checkBalance(t, tc, txsel, "A", 0, "500")
|
|
checkBalance(t, tc, txsel, "A", 1, "500")
|
|
checkBalance(t, tc, txsel, "C", 1, "0")
|
|
|
|
log.Debug("block:0 batch:6")
|
|
l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[5].Batch.ForgeL1TxsNum])
|
|
_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err =
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, len(oL1UserTxs))
|
|
assert.Equal(t, 0, len(oL1CoordTxs))
|
|
assert.Equal(t, 0, len(oL2Txs))
|
|
assert.Equal(t, common.BatchNum(6), txsel.localAccountsDB.CurrentBatch())
|
|
assert.Equal(t, common.Idx(259), txsel.localAccountsDB.CurrentIdx())
|
|
checkBalance(t, tc, txsel, "A", 0, "600")
|
|
checkBalance(t, tc, txsel, "A", 1, "500")
|
|
checkBalance(t, tc, txsel, "B", 0, "400")
|
|
checkBalance(t, tc, txsel, "C", 1, "0")
|
|
|
|
log.Debug("block:0 batch:7")
|
|
// simulate the PoolL2Txs of the batch7
|
|
batchPoolL2 := `
|
|
Type: PoolL2
|
|
PoolTransferToEthAddr(1) A-B: 200 (126)
|
|
PoolTransferToEthAddr(0) B-C: 100 (126)`
|
|
poolL2Txs, err := tc.GeneratePoolL2Txs(batchPoolL2)
|
|
require.NoError(t, err)
|
|
// add AccountCreationAuths that will be used at the next batch
|
|
accAuthSig0 := addAccCreationAuth(t, tc, txsel, chainID, hermezContractAddr, "B")
|
|
accAuthSig1 := addAccCreationAuth(t, tc, txsel, chainID, hermezContractAddr, "C")
|
|
// add the PoolL2Txs to the l2DB
|
|
addL2Txs(t, txsel, poolL2Txs)
|
|
// check signatures of L2Txs from the L2DB (to check that the
|
|
// parameters of the PoolL2Tx match the original parameters signed
|
|
// before inserting it to the L2DB)
|
|
l2TxsFromDB, err := txsel.l2db.GetPendingTxs()
|
|
require.NoError(t, err)
|
|
assert.True(t, l2TxsFromDB[0].VerifySignature(chainID, tc.Users["A"].BJJ.Public().Compress()))
|
|
assert.True(t, l2TxsFromDB[1].VerifySignature(chainID, tc.Users["B"].BJJ.Public().Compress()))
|
|
l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[6].Batch.ForgeL1TxsNum])
|
|
coordIdxs, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err :=
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []common.Idx{261, 263}, coordIdxs)
|
|
assert.Equal(t, txsel.coordAccount.AccountCreationAuth, accAuths[0])
|
|
assert.Equal(t, txsel.coordAccount.AccountCreationAuth, accAuths[2])
|
|
assert.Equal(t, accAuthSig0, accAuths[1])
|
|
assert.Equal(t, accAuthSig1, accAuths[3])
|
|
assert.Equal(t, 1, len(oL1UserTxs))
|
|
assert.Equal(t, 4, len(oL1CoordTxs))
|
|
assert.Equal(t, 2, len(oL2Txs))
|
|
assert.Equal(t, 0, len(discardedL2Txs))
|
|
assert.Equal(t, len(oL1CoordTxs), len(accAuths))
|
|
assert.Equal(t, common.BatchNum(7), txsel.localAccountsDB.CurrentBatch())
|
|
assert.Equal(t, common.Idx(264), txsel.localAccountsDB.CurrentIdx())
|
|
checkBalance(t, tc, txsel, "Coord", 1, "20") // CoordIdx for TokenID=1
|
|
checkBalance(t, tc, txsel, "Coord", 0, "10") // CoordIdx for TokenID=1
|
|
checkBalance(t, tc, txsel, "A", 0, "600")
|
|
checkBalance(t, tc, txsel, "A", 1, "280")
|
|
checkBalance(t, tc, txsel, "B", 0, "290")
|
|
checkBalance(t, tc, txsel, "B", 1, "200")
|
|
checkBalance(t, tc, txsel, "C", 0, "100")
|
|
checkBalance(t, tc, txsel, "D", 0, "800")
|
|
checkSortedByNonce(t, testAccNonces, oL2Txs)
|
|
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(poolL2Txs),
|
|
txsel.localAccountsDB.CurrentBatch())
|
|
require.NoError(t, err)
|
|
|
|
log.Debug("block:0 batch:8")
|
|
// simulate the PoolL2Txs of the batch8
|
|
batchPoolL2 = `
|
|
Type: PoolL2
|
|
PoolTransfer(0) A-B: 100 (126)
|
|
PoolTransfer(0) C-A: 50 (126)
|
|
PoolTransfer(1) B-C: 100 (126)
|
|
PoolExit(0) A: 100 (126)`
|
|
poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
|
|
require.NoError(t, err)
|
|
addL2Txs(t, txsel, poolL2Txs)
|
|
// check signatures of L2Txs from the L2DB (to check that the
|
|
// parameters of the PoolL2Tx match the original parameters signed
|
|
// before inserting it to the L2DB)
|
|
l2TxsFromDB, err = txsel.l2db.GetPendingTxs()
|
|
require.NoError(t, err)
|
|
assert.True(t, l2TxsFromDB[0].VerifySignature(chainID, tc.Users["A"].BJJ.Public().Compress()))
|
|
assert.True(t, l2TxsFromDB[1].VerifySignature(chainID, tc.Users["C"].BJJ.Public().Compress()))
|
|
assert.True(t, l2TxsFromDB[2].VerifySignature(chainID, tc.Users["B"].BJJ.Public().Compress()))
|
|
assert.True(t, l2TxsFromDB[3].VerifySignature(chainID, tc.Users["A"].BJJ.Public().Compress()))
|
|
l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[7].Batch.ForgeL1TxsNum])
|
|
coordIdxs, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err =
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []common.Idx{261, 263}, coordIdxs)
|
|
assert.Equal(t, 0, len(accAuths))
|
|
assert.Equal(t, 0, len(oL1UserTxs))
|
|
assert.Equal(t, 0, len(oL1CoordTxs))
|
|
assert.Equal(t, 4, len(oL2Txs))
|
|
assert.Equal(t, 0, len(discardedL2Txs))
|
|
assert.Equal(t, len(oL1CoordTxs), len(accAuths))
|
|
assert.Equal(t, common.BatchNum(8), txsel.localAccountsDB.CurrentBatch())
|
|
assert.Equal(t, common.Idx(264), txsel.localAccountsDB.CurrentIdx())
|
|
checkBalance(t, tc, txsel, "Coord", 1, "30") // CoordIdx for TokenID=1
|
|
checkBalance(t, tc, txsel, "Coord", 0, "35") // CoordIdx for TokenID=1
|
|
checkBalance(t, tc, txsel, "A", 0, "430")
|
|
checkBalance(t, tc, txsel, "A", 1, "280")
|
|
checkBalance(t, tc, txsel, "B", 0, "390")
|
|
checkBalance(t, tc, txsel, "B", 1, "90")
|
|
checkBalance(t, tc, txsel, "C", 0, "45")
|
|
checkBalance(t, tc, txsel, "C", 1, "100")
|
|
checkBalance(t, tc, txsel, "D", 0, "800")
|
|
checkSortedByNonce(t, testAccNonces, oL2Txs)
|
|
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(poolL2Txs),
|
|
txsel.localAccountsDB.CurrentBatch())
|
|
require.NoError(t, err)
|
|
|
|
log.Debug("(batch9)block:1 batch:1")
|
|
// simulate the PoolL2Txs of the batch9
|
|
batchPoolL2 = `
|
|
Type: PoolL2
|
|
PoolTransfer(0) D-A: 300 (126)
|
|
PoolTransfer(0) B-D: 100 (126)
|
|
`
|
|
poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
|
|
require.NoError(t, err)
|
|
addL2Txs(t, txsel, poolL2Txs)
|
|
// check signatures of L2Txs from the L2DB (to check that the
|
|
// parameters of the PoolL2Tx match the original parameters signed
|
|
// before inserting it to the L2DB)
|
|
l2TxsFromDB, err = txsel.l2db.GetPendingTxs()
|
|
require.NoError(t, err)
|
|
assert.True(t, l2TxsFromDB[0].VerifySignature(chainID, tc.Users["D"].BJJ.Public().Compress()))
|
|
assert.True(t, l2TxsFromDB[1].VerifySignature(chainID, tc.Users["B"].BJJ.Public().Compress()))
|
|
l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[1].Rollup.Batches[0].Batch.ForgeL1TxsNum])
|
|
coordIdxs, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err =
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []common.Idx{263}, coordIdxs)
|
|
assert.Equal(t, 0, len(accAuths))
|
|
assert.Equal(t, 4, len(oL1UserTxs))
|
|
assert.Equal(t, 0, len(oL1CoordTxs))
|
|
assert.Equal(t, 2, len(oL2Txs))
|
|
assert.Equal(t, len(oL1CoordTxs), len(accAuths))
|
|
assert.Equal(t, common.BatchNum(9), txsel.localAccountsDB.CurrentBatch())
|
|
assert.Equal(t, common.Idx(264), txsel.localAccountsDB.CurrentIdx())
|
|
checkBalanceByIdx(t, txsel, 261, "30")
|
|
checkBalanceByIdx(t, txsel, 263, "75")
|
|
checkBalance(t, tc, txsel, "A", 0, "730")
|
|
checkBalance(t, tc, txsel, "A", 1, "280")
|
|
checkBalance(t, tc, txsel, "B", 0, "380")
|
|
checkBalance(t, tc, txsel, "B", 1, "90")
|
|
checkBalance(t, tc, txsel, "C", 0, "845")
|
|
checkBalance(t, tc, txsel, "C", 1, "100")
|
|
checkBalance(t, tc, txsel, "D", 0, "470")
|
|
checkSortedByNonce(t, testAccNonces, oL2Txs)
|
|
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(poolL2Txs),
|
|
txsel.localAccountsDB.CurrentBatch())
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestPoolL2TxsWithoutEnoughBalance(t *testing.T) {
|
|
set := `
|
|
Type: Blockchain
|
|
|
|
CreateAccountDeposit(0) Coord: 0
|
|
CreateAccountDeposit(0) A: 100
|
|
CreateAccountDeposit(0) B: 100
|
|
|
|
> batchL1 // freeze L1User{3}
|
|
> batchL1 // forge L1User{3}
|
|
> block
|
|
`
|
|
|
|
chainID := uint16(0)
|
|
tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
|
|
// generate test transactions, the L1CoordinatorTxs generated by Til
|
|
// will be ignored at this test, as will be the TxSelector who
|
|
// generates them when needed
|
|
blocks, err := tc.GenerateBlocks(set)
|
|
assert.NoError(t, err)
|
|
|
|
hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
|
|
txsel, _ := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
|
|
|
|
// restart nonces of TilContext, as will be set by generating directly
|
|
// the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
|
|
tc.RestartNonces()
|
|
|
|
tpc := txprocessor.Config{
|
|
NLevels: 16,
|
|
MaxFeeTx: 10,
|
|
MaxTx: 20,
|
|
MaxL1Tx: 10,
|
|
ChainID: chainID,
|
|
}
|
|
// batch1
|
|
l1UserTxs := []common.L1Tx{}
|
|
_, _, _, _, _, _, err = txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
// 1st TransferToEthAddr
|
|
expectedTxID0 := "0x028847b86613c0b70be18c8622119ed045b42e4e47d7938fa90bb3d1dc14928965"
|
|
// 1st Exit
|
|
expectedTxID1 := "0x0200b18773dcf56f770d65870fb02041cb59a088fd35b7c3f3df69f8a250b99a42"
|
|
// 2nd TransferToEthAddr
|
|
expectedTxID2 := "0x029720ff506153f970f120ac638cd7ee759eeff2c2012e7634a78e4fdc05c04a90"
|
|
|
|
// batch2
|
|
// prepare the PoolL2Txs
|
|
batchPoolL2 := `
|
|
Type: PoolL2
|
|
PoolTransferToEthAddr(0) A-B: 100 (126)
|
|
PoolExit(0) B: 100 (126)`
|
|
poolL2Txs, err := tc.GeneratePoolL2Txs(batchPoolL2)
|
|
require.NoError(t, err)
|
|
// add the PoolL2Txs to the l2DB
|
|
addL2Txs(t, txsel, poolL2Txs)
|
|
|
|
l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
|
|
_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err :=
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 3, len(oL1UserTxs))
|
|
assert.Equal(t, 0, len(oL1CoordTxs))
|
|
assert.Equal(t, 0, len(oL2Txs)) // should be 0 as the 2 PoolL2Txs does not have enough funds
|
|
assert.Equal(t, 2, len(discardedL2Txs))
|
|
assert.Equal(t, expectedTxID0, discardedL2Txs[0].TxID.String())
|
|
assert.Equal(t, expectedTxID1, discardedL2Txs[1].TxID.String())
|
|
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
|
|
txsel.localAccountsDB.CurrentBatch())
|
|
require.NoError(t, err)
|
|
|
|
// as the PoolL2Txs have not been really processed, restart nonces
|
|
tc.RestartNonces()
|
|
|
|
// batch3
|
|
batchPoolL2 = `
|
|
Type: PoolL2
|
|
PoolTransferToEthAddr(0) A-B: 50 (126)`
|
|
poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
|
|
require.NoError(t, err)
|
|
addL2Txs(t, txsel, poolL2Txs)
|
|
|
|
l1UserTxs = []common.L1Tx{}
|
|
_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err =
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 0, len(oL1UserTxs))
|
|
assert.Equal(t, 0, len(oL1CoordTxs))
|
|
assert.Equal(t, 2, len(oL2Txs))
|
|
assert.Equal(t, 1, len(discardedL2Txs))
|
|
assert.Equal(t, expectedTxID2, oL2Txs[0].TxID.String())
|
|
assert.Equal(t, expectedTxID1, oL2Txs[1].TxID.String())
|
|
assert.Equal(t, expectedTxID0, discardedL2Txs[0].TxID.String())
|
|
assert.Equal(t, common.TxTypeTransferToEthAddr, oL2Txs[0].Type)
|
|
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
|
|
txsel.localAccountsDB.CurrentBatch())
|
|
require.NoError(t, err)
|
|
|
|
// batch4
|
|
// make the selection of another batch, which should include the
|
|
// initial PoolExit, which now is valid as B has enough Balance
|
|
l1UserTxs = []common.L1Tx{}
|
|
_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err =
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 0, len(oL1UserTxs))
|
|
assert.Equal(t, 0, len(oL1CoordTxs))
|
|
assert.Equal(t, 0, len(oL2Txs))
|
|
assert.Equal(t, 1, len(discardedL2Txs))
|
|
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
|
|
txsel.localAccountsDB.CurrentBatch())
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestTransferToBjj(t *testing.T) {
|
|
set := `
|
|
Type: Blockchain
|
|
AddToken(1)
|
|
|
|
CreateAccountDeposit(0) Coord: 0
|
|
CreateAccountDeposit(0) A: 1000
|
|
CreateAccountDeposit(0) B: 1000
|
|
CreateAccountDeposit(1) B: 1000
|
|
|
|
> batchL1 // freeze L1User{4}
|
|
> batchL1 // forge L1User{4}
|
|
> block
|
|
`
|
|
|
|
chainID := uint16(0)
|
|
tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
|
|
blocks, err := tc.GenerateBlocks(set)
|
|
assert.NoError(t, err)
|
|
|
|
hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
|
|
txsel, _ := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
|
|
|
|
// restart nonces of TilContext, as will be set by generating directly
|
|
// the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
|
|
tc.RestartNonces()
|
|
|
|
addTokens(t, tc, txsel.l2db.DB())
|
|
|
|
tpc := txprocessor.Config{
|
|
NLevels: 16,
|
|
MaxFeeTx: 10,
|
|
MaxTx: 20,
|
|
MaxL1Tx: 10,
|
|
ChainID: chainID,
|
|
}
|
|
// batch1 to freeze L1UserTxs that will create some accounts with
|
|
// positive balance
|
|
l1UserTxs := []common.L1Tx{}
|
|
_, _, _, _, _, _, err = txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
|
|
// Transfer is ToBJJ to a BJJ-only account that doesn't exist
|
|
// and the coordinator will create it via L1CoordTx.
|
|
|
|
batchPoolL2 := `
|
|
Type: PoolL2
|
|
PoolTransferToBJJ(0) A-B: 50 (126)
|
|
`
|
|
poolL2Txs, err := tc.GeneratePoolL2Txs(batchPoolL2)
|
|
require.NoError(t, err)
|
|
|
|
// add the PoolL2Txs to the l2DB
|
|
addL2Txs(t, txsel, poolL2Txs)
|
|
|
|
l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
|
|
_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err :=
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 4, len(oL1UserTxs))
|
|
// We expect the coordinator to add an L1CoordTx to create an account for the recipient of the l2tx
|
|
require.Equal(t, 1, len(oL1CoordTxs))
|
|
assert.Equal(t, poolL2Txs[0].ToEthAddr, oL1CoordTxs[0].FromEthAddr)
|
|
assert.Equal(t, poolL2Txs[0].ToBJJ, oL1CoordTxs[0].FromBJJ)
|
|
assert.Equal(t, 1, len(oL2Txs))
|
|
assert.Equal(t, 0, len(discardedL2Txs))
|
|
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
|
|
txsel.localAccountsDB.CurrentBatch())
|
|
require.NoError(t, err)
|
|
|
|
// Now the BJJ-only account for B is already created, so the transfer
|
|
// happens without an L1CoordTx that creates the user account.
|
|
|
|
batchPoolL2 = `
|
|
Type: PoolL2
|
|
PoolTransferToBJJ(0) A-B: 50 (126)
|
|
`
|
|
|
|
poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
|
|
require.NoError(t, err)
|
|
addL2Txs(t, txsel, poolL2Txs)
|
|
|
|
l1UserTxs = []common.L1Tx{}
|
|
_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err =
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 0, len(oL1UserTxs))
|
|
// Since the BJJ-only account B already exists, the coordinator doesn't add any L1CoordTxs
|
|
assert.Equal(t, 0, len(oL1CoordTxs))
|
|
assert.Equal(t, 1, len(oL2Txs))
|
|
assert.Equal(t, 0, len(discardedL2Txs))
|
|
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
|
|
txsel.localAccountsDB.CurrentBatch())
|
|
require.NoError(t, err)
|
|
|
|
// The transfer now is ToBJJ to a BJJ-only account that doesn't exist
|
|
// and the coordinator will create it via L1CoordTx. Since it's a
|
|
// transfer of a token for which the coordinator doesn't have a fee
|
|
// account, another L1CoordTx will be created for the coordinator to
|
|
// receive the fees.
|
|
|
|
batchPoolL2 = `
|
|
Type: PoolL2
|
|
PoolTransferToBJJ(1) B-A: 50 (126)
|
|
`
|
|
|
|
poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
|
|
require.NoError(t, err)
|
|
addL2Txs(t, txsel, poolL2Txs)
|
|
|
|
l1UserTxs = []common.L1Tx{}
|
|
_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err =
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 0, len(oL1UserTxs))
|
|
// We expect the coordinator to add an L1CoordTx to create an account
|
|
// to receive the fees by the coordinator and another one for the
|
|
// recipient of the l2tx
|
|
assert.Equal(t, 2, len(oL1CoordTxs))
|
|
// [0] Coordinator account cration for token 1
|
|
assert.Equal(t, tc.Users["Coord"].Addr, oL1CoordTxs[0].FromEthAddr)
|
|
// [1] User A BJJ-only account creation for token 1
|
|
assert.Equal(t, poolL2Txs[0].ToEthAddr, oL1CoordTxs[1].FromEthAddr)
|
|
assert.Equal(t, poolL2Txs[0].ToBJJ, oL1CoordTxs[1].FromBJJ)
|
|
assert.Equal(t, common.TokenID(1), oL1CoordTxs[1].TokenID)
|
|
|
|
assert.Equal(t, 1, len(oL2Txs))
|
|
assert.Equal(t, 0, len(discardedL2Txs))
|
|
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
|
|
txsel.localAccountsDB.CurrentBatch())
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestTransferManyFromSameAccount(t *testing.T) {
|
|
set := `
|
|
Type: Blockchain
|
|
|
|
CreateAccountDeposit(0) Coord: 0
|
|
CreateAccountDeposit(0) A: 1000
|
|
CreateAccountDeposit(0) B: 1000
|
|
|
|
> batchL1 // freeze L1User{3}
|
|
> batchL1 // forge L1User{3}
|
|
> block
|
|
`
|
|
|
|
chainID := uint16(0)
|
|
tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
|
|
blocks, err := tc.GenerateBlocks(set)
|
|
assert.NoError(t, err)
|
|
|
|
hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
|
|
txsel, _ := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
|
|
|
|
// restart nonces of TilContext, as will be set by generating directly
|
|
// the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
|
|
tc.RestartNonces()
|
|
|
|
tpc := txprocessor.Config{
|
|
NLevels: 16,
|
|
MaxFeeTx: 10,
|
|
MaxTx: 10,
|
|
MaxL1Tx: 10,
|
|
ChainID: chainID,
|
|
}
|
|
// batch1 to freeze L1UserTxs
|
|
l1UserTxs := []common.L1Tx{}
|
|
_, _, _, _, _, _, err = txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
|
|
// 8 transfers from the same account
|
|
batchPoolL2 := `
|
|
Type: PoolL2
|
|
PoolTransfer(0) A-B: 10 (126) // 1
|
|
PoolTransfer(0) A-B: 10 (126) // 2
|
|
PoolTransfer(0) A-B: 10 (126) // 3
|
|
PoolTransfer(0) A-B: 10 (126) // 4
|
|
PoolTransfer(0) A-B: 10 (126) // 5
|
|
PoolTransfer(0) A-B: 10 (126) // 6
|
|
PoolTransfer(0) A-B: 10 (126) // 7
|
|
PoolTransfer(0) A-B: 10 (126) // 8
|
|
PoolTransfer(0) A-B: 10 (126) // 9
|
|
PoolTransfer(0) A-B: 10 (126) // 10
|
|
PoolTransfer(0) A-B: 10 (126) // 11
|
|
`
|
|
poolL2Txs, err := tc.GeneratePoolL2Txs(batchPoolL2)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 11, len(poolL2Txs))
|
|
|
|
// reorder poolL2Txs so that nonces are not sorted
|
|
poolL2Txs[0], poolL2Txs[7] = poolL2Txs[7], poolL2Txs[0]
|
|
poolL2Txs[1], poolL2Txs[10] = poolL2Txs[10], poolL2Txs[1]
|
|
|
|
// add the PoolL2Txs to the l2DB
|
|
addL2Txs(t, txsel, poolL2Txs)
|
|
// batch 2 to crate some accounts with positive balance, and do 8 L2Tx
|
|
// transfers from account A
|
|
l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
|
|
_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err :=
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 3, len(oL1UserTxs))
|
|
require.Equal(t, 0, len(oL1CoordTxs))
|
|
assert.Equal(t, 7, len(oL2Txs))
|
|
assert.Equal(t, 4, len(discardedL2Txs))
|
|
|
|
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
|
|
txsel.localAccountsDB.CurrentBatch())
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestPoolL2TxInvalidNonces(t *testing.T) {
|
|
// This test recreates the case where L2Txs of type TransferToEthAddr
|
|
// for non existing accounts are not selected due not enough space for
|
|
// the needed L1CoordinatorTxs (of account creation).
|
|
|
|
set := `
|
|
Type: Blockchain
|
|
|
|
CreateAccountDeposit(0) Coord: 0
|
|
CreateAccountDeposit(0) A: 100000
|
|
CreateAccountDeposit(0) B: 10000
|
|
|
|
> batchL1 // Batch1: freeze L1User{3}
|
|
> batchL1 // Batch2: forge L1User{3}
|
|
> block
|
|
`
|
|
|
|
chainID := uint16(0)
|
|
tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
|
|
blocks, err := tc.GenerateBlocks(set)
|
|
assert.NoError(t, err)
|
|
|
|
hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
|
|
txsel, _ := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
|
|
|
|
// restart nonces of TilContext, as will be set by generating directly
|
|
// the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
|
|
tc.RestartNonces()
|
|
|
|
tpc := txprocessor.Config{
|
|
NLevels: 16,
|
|
MaxFeeTx: 10,
|
|
MaxTx: 20,
|
|
MaxL1Tx: 3,
|
|
ChainID: chainID,
|
|
}
|
|
// batch1 to freeze L1UserTxs
|
|
l1UserTxs := []common.L1Tx{}
|
|
_, _, _, _, _, _, err = txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
|
|
batchPoolL2 := `
|
|
Type: PoolL2
|
|
PoolTransferToEthAddr(0) B-C: 10 (126)
|
|
PoolTransfer(0) B-A: 10 (126)
|
|
PoolTransferToEthAddr(0) B-D: 10 (126)
|
|
PoolTransfer(0) B-A: 10 (126)
|
|
PoolTransferToEthAddr(0) B-E: 10 (126)
|
|
PoolTransfer(0) B-A: 10 (126)
|
|
PoolTransferToEthAddr(0) B-F: 10 (126)
|
|
PoolTransfer(0) B-A: 10 (126)
|
|
PoolTransferToEthAddr(0) B-G: 10 (126)
|
|
PoolTransfer(0) B-A: 10 (126)
|
|
`
|
|
poolL2Txs, err := tc.GeneratePoolL2Txs(batchPoolL2)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 10, len(poolL2Txs))
|
|
|
|
// add AccountCreationAuths that will be used at the next batch
|
|
_ = addAccCreationAuth(t, tc, txsel, chainID, hermezContractAddr, "C")
|
|
_ = addAccCreationAuth(t, tc, txsel, chainID, hermezContractAddr, "D")
|
|
_ = addAccCreationAuth(t, tc, txsel, chainID, hermezContractAddr, "E")
|
|
_ = addAccCreationAuth(t, tc, txsel, chainID, hermezContractAddr, "F")
|
|
_ = addAccCreationAuth(t, tc, txsel, chainID, hermezContractAddr, "G")
|
|
|
|
// add the PoolL2Txs to the l2DB
|
|
addL2Txs(t, txsel, poolL2Txs)
|
|
// batch 2 to crate the accounts (from L1UserTxs)
|
|
l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
|
|
|
|
// select L1 & L2 txs
|
|
_, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err :=
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 3, len(oL1UserTxs))
|
|
require.Equal(t, 0, len(oL1CoordTxs))
|
|
require.Equal(t, 0, len(oL2Txs))
|
|
require.Equal(t, 10, len(discardedL2Txs))
|
|
require.Equal(t, 0, len(accAuths))
|
|
|
|
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
|
|
txsel.localAccountsDB.CurrentBatch())
|
|
require.NoError(t, err)
|
|
|
|
// batch 3
|
|
l1UserTxs = []common.L1Tx{}
|
|
_, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err =
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 0, len(oL1UserTxs))
|
|
require.Equal(t, 3, len(oL1CoordTxs))
|
|
require.Equal(t, 8, len(oL2Txs))
|
|
require.Equal(t, 2, len(discardedL2Txs))
|
|
require.Equal(t, 3, len(accAuths))
|
|
|
|
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
|
|
txsel.localAccountsDB.CurrentBatch())
|
|
require.NoError(t, err)
|
|
|
|
// batch 4
|
|
l1UserTxs = []common.L1Tx{}
|
|
_, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err =
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 0, len(oL1UserTxs))
|
|
require.Equal(t, 1, len(oL1CoordTxs))
|
|
require.Equal(t, 2, len(oL2Txs))
|
|
require.Equal(t, 0, len(discardedL2Txs))
|
|
require.Equal(t, 1, len(accAuths))
|
|
|
|
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
|
|
txsel.localAccountsDB.CurrentBatch())
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestProcessL2Selection(t *testing.T) {
|
|
set := `
|
|
Type: Blockchain
|
|
|
|
CreateAccountDeposit(0) Coord: 0
|
|
CreateAccountDeposit(0) A: 18
|
|
CreateAccountDeposit(0) B: 0
|
|
|
|
> batchL1 // freeze L1User{3}
|
|
> batchL1 // forge L1User{3}
|
|
> block
|
|
`
|
|
|
|
chainID := uint16(0)
|
|
tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
|
|
blocks, err := tc.GenerateBlocks(set)
|
|
assert.NoError(t, err)
|
|
|
|
hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
|
|
txsel, _ := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
|
|
|
|
// restart nonces of TilContext, as will be set by generating directly
|
|
// the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
|
|
tc.RestartNonces()
|
|
|
|
tpc := txprocessor.Config{
|
|
NLevels: 16,
|
|
MaxFeeTx: 10,
|
|
MaxTx: 10,
|
|
MaxL1Tx: 10,
|
|
ChainID: chainID,
|
|
}
|
|
// batch1 to freeze L1UserTxs
|
|
l1UserTxs := []common.L1Tx{}
|
|
_, _, _, _, _, _, err = txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
|
|
// 3 transfers from the same account
|
|
batchPoolL2 := `
|
|
Type: PoolL2
|
|
PoolTransfer(0) A-B: 10 (126)
|
|
PoolTransfer(0) A-B: 10 (126) // not enough funds
|
|
PoolTransfer(0) A-B: 5 (126) // enough funds
|
|
`
|
|
poolL2Txs, err := tc.GeneratePoolL2Txs(batchPoolL2)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 3, len(poolL2Txs))
|
|
|
|
// add the PoolL2Txs to the l2DB
|
|
addL2Txs(t, txsel, poolL2Txs)
|
|
// batch 2 to crate some accounts with positive balance, and do 3 L2Tx transfers from account A
|
|
l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
|
|
_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err :=
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 3, len(oL1UserTxs))
|
|
require.Equal(t, 0, len(oL1CoordTxs))
|
|
// only 1st L2Tx should be accepted, as:
|
|
// - 2nd will not be selected as has not enough funds
|
|
// - 3rd will not be selected as has Nonce=2, and the account Nonce==1
|
|
// (due the 2nd txs not being accepted)
|
|
assert.Equal(t, 1, len(oL2Txs))
|
|
assert.Equal(t, 2, len(discardedL2Txs))
|
|
assert.Equal(t, common.Nonce(0), oL2Txs[0].Nonce)
|
|
assert.Equal(t, common.Nonce(1), discardedL2Txs[0].Nonce)
|
|
assert.Equal(t, common.Nonce(2), discardedL2Txs[1].Nonce)
|
|
assert.Equal(t, "Tx not selected due to not enough Balance at the sender. "+
|
|
"Current sender account Balance: 7, Amount+Fee: 11", discardedL2Txs[0].Info)
|
|
assert.Equal(t, "Tx not selected due to not current Nonce. Tx.Nonce: 2, "+
|
|
"Account.Nonce: 1", discardedL2Txs[1].Info)
|
|
|
|
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
|
|
txsel.localAccountsDB.CurrentBatch())
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestValidTxsWithLowFeeAndInvalidTxsWithHighFee(t *testing.T) {
|
|
// This test recreates the case where there are
|
|
|
|
set := `
|
|
Type: Blockchain
|
|
|
|
CreateAccountDeposit(0) Coord: 0
|
|
CreateAccountDeposit(0) A: 100
|
|
CreateAccountDeposit(0) B: 0
|
|
|
|
> batchL1 // Batch1: freeze L1User{3}
|
|
> batchL1 // Batch2: forge L1User{3}
|
|
> block
|
|
`
|
|
|
|
chainID := uint16(0)
|
|
tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
|
|
tilCfgExtra := til.ConfigExtra{
|
|
BootCoordAddr: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"),
|
|
CoordUser: "Coord",
|
|
}
|
|
blocks, err := tc.GenerateBlocks(set)
|
|
require.NoError(t, err)
|
|
err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
|
|
require.NoError(t, err)
|
|
err = tc.FillBlocksForgedL1UserTxs(blocks)
|
|
require.NoError(t, err)
|
|
|
|
hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
|
|
txsel, historyDB := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
|
|
|
|
// Insert blocks into DB
|
|
for i := range blocks {
|
|
err = historyDB.AddBlockSCData(&blocks[i])
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
err = historyDB.UpdateTokenValue(common.EmptyAddr, 1000)
|
|
require.NoError(t, err)
|
|
|
|
// restart nonces of TilContext, as will be set by generating directly
|
|
// the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
|
|
tc.RestartNonces()
|
|
|
|
tpc := txprocessor.Config{
|
|
NLevels: 16,
|
|
MaxFeeTx: 5,
|
|
MaxTx: 5,
|
|
MaxL1Tx: 3,
|
|
ChainID: chainID,
|
|
}
|
|
// batch1 to freeze L1UserTxs
|
|
l1UserTxs := []common.L1Tx{}
|
|
_, _, _, _, _, _, err = txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
|
|
// batch 2 to crate the accounts (from L1UserTxs)
|
|
l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
|
|
|
|
// select L1 & L2 txs
|
|
_, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err :=
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 3, len(oL1UserTxs))
|
|
require.Equal(t, 0, len(oL1CoordTxs))
|
|
require.Equal(t, 0, len(oL2Txs))
|
|
require.Equal(t, 0, len(discardedL2Txs))
|
|
require.Equal(t, 0, len(accAuths))
|
|
|
|
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
|
|
txsel.localAccountsDB.CurrentBatch())
|
|
require.NoError(t, err)
|
|
|
|
// batch 3. The A-B txs have lower fee, but are the only ones possible
|
|
// with the current Accounts Balances, as the B-A tx of amount 40 will
|
|
// not be included as will be processed first when there is not enough
|
|
// balance at B (processed first as the TxSelector sorts by Fee and then
|
|
// by Nonce).
|
|
batchPoolL2 := `
|
|
Type: PoolL2
|
|
PoolTransfer(0) B-A: 40 (130) // B-A txs are only possible once A-B txs are processed
|
|
PoolTransfer(0) B-A: 1 (126)
|
|
PoolTransfer(0) B-A: 1 (126)
|
|
PoolTransfer(0) B-A: 1 (126)
|
|
PoolTransfer(0) B-A: 1 (126)
|
|
PoolTransfer(0) B-A: 1 (126)
|
|
PoolTransfer(0) B-A: 1 (126)
|
|
PoolTransfer(0) B-A: 1 (126)
|
|
PoolTransfer(0) A-B: 20 (20)
|
|
PoolTransfer(0) A-B: 25 (150)
|
|
PoolTransfer(0) A-B: 20 (20)
|
|
`
|
|
poolL2Txs, err := tc.GeneratePoolL2Txs(batchPoolL2)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 11, len(poolL2Txs))
|
|
// add the PoolL2Txs to the l2DB
|
|
addL2Txs(t, txsel, poolL2Txs)
|
|
l1UserTxs = []common.L1Tx{}
|
|
_, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err =
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 0, len(oL1UserTxs))
|
|
require.Equal(t, 0, len(oL1CoordTxs))
|
|
require.Equal(t, 3, len(oL2Txs)) // the 3 txs A-B
|
|
require.Equal(t, 8, len(discardedL2Txs)) // the 8 txs B-A
|
|
require.Equal(t, 0, len(accAuths))
|
|
|
|
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
|
|
txsel.localAccountsDB.CurrentBatch())
|
|
require.NoError(t, err)
|
|
|
|
// batch 4. In this Batch, account B has enough balance to send the txs
|
|
_, accAuths, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err =
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, nil)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 0, len(oL1UserTxs))
|
|
require.Equal(t, 0, len(oL1CoordTxs))
|
|
require.Equal(t, 5, len(oL2Txs))
|
|
require.Equal(t, 3, len(discardedL2Txs))
|
|
require.Equal(t, 0, len(accAuths))
|
|
}
|
|
|
|
func TestL1UserFutureTxs(t *testing.T) {
|
|
set := `
|
|
Type: Blockchain
|
|
|
|
CreateAccountDeposit(0) Coord: 0
|
|
CreateAccountDeposit(0) A: 100
|
|
|
|
> batchL1 // freeze L1User{2}
|
|
CreateAccountDeposit(0) B: 18
|
|
> batchL1 // forge L1User{2}, freeze L1User{1}
|
|
> batchL1 // forge L1User{1}
|
|
> block
|
|
`
|
|
|
|
chainID := uint16(0)
|
|
tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
|
|
blocks, err := tc.GenerateBlocks(set)
|
|
assert.NoError(t, err)
|
|
|
|
hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
|
|
txsel, _ := initTest(t, chainID, hermezContractAddr, tc.Users["Coord"])
|
|
|
|
// restart nonces of TilContext, as will be set by generating directly
|
|
// the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs
|
|
tc.RestartNonces()
|
|
|
|
tpc := txprocessor.Config{
|
|
NLevels: 16,
|
|
MaxFeeTx: 10,
|
|
MaxTx: 10,
|
|
MaxL1Tx: 10,
|
|
ChainID: chainID,
|
|
}
|
|
// batch1 to freeze L1UserTxs
|
|
l1UserTxs := []common.L1Tx{}
|
|
l1UserFutureTxs := []common.L1Tx{}
|
|
_, _, _, _, _, _, err = txsel.GetL1L2TxSelection(tpc, l1UserTxs, l1UserFutureTxs)
|
|
require.NoError(t, err)
|
|
|
|
batchPoolL2 := `
|
|
Type: PoolL2
|
|
PoolTransferToEthAddr(0) A-B: 10 (126)
|
|
`
|
|
poolL2Txs, err := tc.GeneratePoolL2Txs(batchPoolL2)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(poolL2Txs))
|
|
|
|
// add AccountCreationAuth for B
|
|
_ = addAccCreationAuth(t, tc, txsel, chainID, hermezContractAddr, "B")
|
|
// add the PoolL2Txs to the l2DB
|
|
addL2Txs(t, txsel, poolL2Txs)
|
|
// batch 2 to crate some accounts with positive balance, and do 1 L2Tx transfer from account A
|
|
l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum])
|
|
l1UserFutureTxs =
|
|
til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[2].Batch.ForgeL1TxsNum])
|
|
require.Equal(t, 2, len(l1UserTxs))
|
|
require.Equal(t, 1, len(l1UserFutureTxs))
|
|
_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err :=
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, l1UserFutureTxs)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 2, len(oL1UserTxs))
|
|
require.Equal(t, 0, len(oL1CoordTxs))
|
|
// no L2Tx selected due the L1UserFutureTx, the L2Tx will be processed
|
|
// at the next batch once the L1UserTx of CreateAccount B is processed,
|
|
// despite that there is an AccountCreationAuth for Account B.
|
|
assert.Equal(t, 0, len(oL2Txs))
|
|
assert.Equal(t, 1, len(discardedL2Txs))
|
|
assert.Equal(t, "Tx not selected (in processTxToEthAddrBJJ) due to L2Tx"+
|
|
" discarded at the current batch, as the receiver account does"+
|
|
" not exist yet, and there is a L1UserTx that will create that"+
|
|
" account in a future batch.",
|
|
discardedL2Txs[0].Info)
|
|
|
|
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
|
|
txsel.localAccountsDB.CurrentBatch())
|
|
require.NoError(t, err)
|
|
|
|
l1UserTxs = til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[2].Batch.ForgeL1TxsNum])
|
|
l1UserFutureTxs = []common.L1Tx{}
|
|
_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err =
|
|
txsel.GetL1L2TxSelection(tpc, l1UserTxs, l1UserFutureTxs)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, len(oL1UserTxs))
|
|
require.Equal(t, 0, len(oL1CoordTxs))
|
|
// L2Tx selected as now the L1UserTx of CreateAccount B is processed
|
|
assert.Equal(t, 1, len(oL2Txs))
|
|
assert.Equal(t, 0, len(discardedL2Txs))
|
|
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
|
|
txsel.localAccountsDB.CurrentBatch())
|
|
require.NoError(t, err)
|
|
|
|
// generate a new L2Tx A-B and check that is processed
|
|
poolL2Txs, err = tc.GeneratePoolL2Txs(batchPoolL2)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(poolL2Txs))
|
|
// add the PoolL2Txs to the l2DB
|
|
addL2Txs(t, txsel, poolL2Txs)
|
|
_, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err =
|
|
txsel.GetL1L2TxSelection(tpc, nil, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 0, len(oL1UserTxs))
|
|
require.Equal(t, 0, len(oL1CoordTxs))
|
|
assert.Equal(t, 1, len(oL2Txs))
|
|
assert.Equal(t, 0, len(discardedL2Txs))
|
|
err = txsel.l2db.StartForging(common.TxIDsFromPoolL2Txs(oL2Txs),
|
|
txsel.localAccountsDB.CurrentBatch())
|
|
require.NoError(t, err)
|
|
}
|