diff --git a/common/batch.go b/common/batch.go index eba3dea..a6e11ce 100644 --- a/common/batch.go +++ b/common/batch.go @@ -53,9 +53,9 @@ func BatchNumFromBytes(b []byte) (BatchNum, error) { // BatchData contains the information of a Batch type BatchData struct { - // L1UserTxs that were forged in the batch L1Batch bool // TODO: Remove once Batch.ForgeL1TxsNum is a pointer - // L1UserTxs []common.L1Tx + // L1UserTxs that were forged in the batch + L1UserTxs []L1Tx L1CoordinatorTxs []L1Tx L2Txs []L2Tx CreatedAccounts []Account diff --git a/common/block.go b/common/block.go index 6134ba0..7653656 100644 --- a/common/block.go +++ b/common/block.go @@ -86,6 +86,4 @@ type BlockData struct { Rollup RollupData Auction AuctionData WDelayer WDelayerData - // TODO: enable when common.WithdrawalDelayerVars is Merged from Synchronizer PR - // WithdrawalDelayerVars *common.WithdrawalDelayerVars } diff --git a/db/historydb/historydb.go b/db/historydb/historydb.go index c025d24..5d937b1 100644 --- a/db/historydb/historydb.go +++ b/db/historydb/historydb.go @@ -567,7 +567,7 @@ func (hdb *HistoryDB) updateExitTree(d sqlx.Ext, blockNum int64, } // In VALUES we set an initial row of NULLs to set the types of each // variable passed as argument - query := ` + const query string = ` UPDATE exit_tree e SET instant_withdrawn = d.instant_withdrawn, delayed_withdraw_request = CASE @@ -1290,7 +1290,50 @@ func (hdb *HistoryDB) SetInitialSCVars(rollup *common.RollupVariables, return tracerr.Wrap(err) } - return txn.Commit() + return tracerr.Wrap(txn.Commit()) +} + +// setL1UserTxEffectiveAmounts sets the EffectiveAmount and EffectiveLoadAmount +// of the given l1UserTxs (with an UPDATE) +func (hdb *HistoryDB) setL1UserTxEffectiveAmounts(d sqlx.Ext, txs []common.L1Tx) error { + type txUpdate struct { + ID common.TxID `db:"id"` + NullifiedAmount bool `db:"nullified_amount"` + NullifiedLoadAmount bool `db:"nullified_load_amount"` + } + txUpdates := make([]txUpdate, len(txs)) + equal := func(a *big.Int, b *big.Int) bool { + return a.Cmp(b) == 0 + } + for i := range txs { + txUpdates[i] = txUpdate{ + ID: txs[i].TxID, + NullifiedAmount: !equal(txs[i].Amount, txs[i].EffectiveAmount), + NullifiedLoadAmount: !equal(txs[i].LoadAmount, txs[i].EffectiveLoadAmount), + } + } + const query string = ` + UPDATE tx SET + effective_amount = CASE + WHEN tx_update.nullified_amount THEN '\x' + ELSE tx.amount + END, + effective_load_amount = CASE + WHEN tx_update.nullified_load_amount THEN '\x' + ELSE tx.load_amount + END + FROM (VALUES + (NULL::::BYTEA, NULL::::BOOL, NULL::::BOOL), + (:id, :nullified_amount, :nullified_load_amount) + ) as tx_update (id, nullified_amount, nullified_load_amount) + WHERE tx.id = tx_update.id + ` + if len(txs) > 0 { + if _, err := sqlx.NamedQuery(d, query, txUpdates); err != nil { + return tracerr.Wrap(err) + } + } + return nil } // AddBlockSCData stores all the information of a block retrieved by the @@ -1364,6 +1407,15 @@ func (hdb *HistoryDB) AddBlockSCData(blockData *common.BlockData) (err error) { // Add Batches for i := range blockData.Rollup.Batches { batch := &blockData.Rollup.Batches[i] + + // Set the EffectiveAmount and EffectiveLoadAmount of all the + // L1UserTxs that have been forged in this batch + if len(batch.L1UserTxs) > 0 { + if err = hdb.setL1UserTxEffectiveAmounts(txn, batch.L1UserTxs); err != nil { + return tracerr.Wrap(err) + } + } + // Add Batch: this will trigger an update on the DB // that will set the batch num of forged L1 txs in this batch if err = hdb.addBatch(txn, &batch.Batch); err != nil { @@ -1432,7 +1484,7 @@ func (hdb *HistoryDB) AddBlockSCData(blockData *common.BlockData) (err error) { return tracerr.Wrap(err) } - return txn.Commit() + return tracerr.Wrap(txn.Commit()) } // GetCoordinatorAPI returns a coordinator by its bidderAddr diff --git a/db/historydb/historydb_test.go b/db/historydb/historydb_test.go index a34bc48..eb5495e 100644 --- a/db/historydb/historydb_test.go +++ b/db/historydb/historydb_test.go @@ -692,6 +692,72 @@ func TestSetInitialSCVars(t *testing.T) { require.Equal(t, wDelayer, dbWDelayer) } +func TestSetL1UserTxEffectiveAmounts(t *testing.T) { + test.WipeDB(historyDB.DB()) + + set := ` + Type: Blockchain + + AddToken(1) + + CreateAccountDeposit(1) A: 2000 + CreateAccountDeposit(1) B: 500 + CreateAccountDeposit(1) C: 500 + + > batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{*} + > block // blockNum=2 + + > batchL1 // forge defined L1UserTxs{*} + > block // blockNum=3 + ` + + tc := til.NewContext(common.RollupConstMaxL1UserTx) + tilCfgExtra := til.ConfigExtra{ + BootCoordAddr: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"), + CoordUser: "A", + } + blocks, err := tc.GenerateBlocks(set) + require.Nil(t, err) + err = tc.FillBlocksExtra(blocks, &tilCfgExtra) + assert.Nil(t, err) + err = tc.FillBlocksForgedL1UserTxs(blocks) + require.Nil(t, err) + + // Add only first block so that the L1UserTxs are not marked as forged + for i := range blocks[:1] { + err = historyDB.AddBlockSCData(&blocks[i]) + require.Nil(t, err) + } + + // Set the Effective{Amount,LoadAmount} of the L1UserTxs that are forged in the second block + l1Txs := blocks[1].Rollup.Batches[0].L1UserTxs + require.Equal(t, 3, len(l1Txs)) + // Change some values to test all cases + l1Txs[1].EffectiveAmount = big.NewInt(0) + l1Txs[2].EffectiveLoadAmount = big.NewInt(0) + l1Txs[2].EffectiveAmount = big.NewInt(0) + err = historyDB.setL1UserTxEffectiveAmounts(historyDB.db, l1Txs) + require.NoError(t, err) + + dbL1Txs, err := historyDB.GetAllL1UserTxs() + require.NoError(t, err) + for _, tx := range dbL1Txs { + assert.NotNil(t, tx.EffectiveAmount) + assert.NotNil(t, tx.EffectiveLoadAmount) + switch tx.TxID { + case l1Txs[0].TxID: + assert.Equal(t, l1Txs[0].LoadAmount, tx.EffectiveLoadAmount) + assert.Equal(t, l1Txs[0].Amount, tx.EffectiveAmount) + case l1Txs[1].TxID: + assert.Equal(t, l1Txs[1].LoadAmount, tx.EffectiveLoadAmount) + assert.Equal(t, big.NewInt(0), tx.EffectiveAmount) + case l1Txs[2].TxID: + assert.Equal(t, big.NewInt(0), tx.EffectiveLoadAmount) + assert.Equal(t, big.NewInt(0), tx.EffectiveAmount) + } + } +} + func TestUpdateExitTree(t *testing.T) { test.WipeDB(historyDB.DB()) diff --git a/db/l2db/l2db.go b/db/l2db/l2db.go index 6f0f2ae..2af8b9f 100644 --- a/db/l2db/l2db.go +++ b/db/l2db/l2db.go @@ -261,7 +261,7 @@ func (l2db *L2DB) CheckNonces(updatedAccounts []common.Account, batchNum common. return tracerr.Wrap(err) } } - return txn.Commit() + return tracerr.Wrap(txn.Commit()) } // Reorg updates the state of txs that were updated in a batch that has been discarted due to a blockchain reorg. @@ -312,5 +312,5 @@ func (l2db *L2DB) Purge(currentBatchNum common.BatchNum) (err error) { if err != nil { return tracerr.Wrap(err) } - return txn.Commit() + return tracerr.Wrap(txn.Commit()) } diff --git a/test/til/txs.go b/test/til/txs.go index 0db9dfc..f933d33 100644 --- a/test/til/txs.go +++ b/test/til/txs.go @@ -690,6 +690,36 @@ func (tc *Context) FillBlocksL1UserTxsBatchNum(blocks []common.BlockData) { } } +// FillBlocksForgedL1UserTxs fills the L1UserTxs of a batch with the L1UserTxs +// that are forged in that batch. It always sets `EffectiveAmount` = `Amount` +// and `EffectiveLoadAmount` = `LoadAmount`. +func (tc *Context) FillBlocksForgedL1UserTxs(blocks []common.BlockData) error { + for i := range blocks { + block := &blocks[i] + for j := range block.Rollup.Batches { + batch := &block.Rollup.Batches[j] + if batch.L1Batch { + batchNum := batch.Batch.BatchNum + queue := tc.Queues[int(*batch.Batch.ForgeL1TxsNum)] + batch.L1UserTxs = make([]common.L1Tx, len(queue)) + for k := range queue { + tx := &batch.L1UserTxs[k] + *tx = queue[k].L1Tx + tx.EffectiveAmount = tx.Amount + tx.EffectiveLoadAmount = tx.LoadAmount + tx.BatchNum = &batchNum + _tx, err := common.NewL1Tx(tx) + if err != nil { + return tracerr.Wrap(err) + } + *tx = *_tx + } + } + } + } + return nil +} + // FillBlocksExtra fills extra fields not generated by til in each block, so // that the blockData is closer to what the HistoryDB stores. The filled fields are: // - blocks[].Rollup.Batch.EthBlockNum