Add ProcessTxs send fees to Coordinator accounts

This commit is contained in:
arnaucube
2020-10-26 17:35:23 +01:00
parent 382bcfeee1
commit 8f24aa93c9
17 changed files with 195 additions and 65 deletions

View File

@@ -38,7 +38,7 @@ type processedExit struct {
// type==TypeSynchronizer, assumes that the call is done from the Synchronizer,
// returns common.ExitTreeLeaf that is later used by the Synchronizer to update
// the HistoryDB, and adds Nonce & TokenID to the L2Txs.
func (s *StateDB) ProcessTxs(l1usertxs, l1coordinatortxs []common.L1Tx, l2txs []common.PoolL2Tx) (*common.ZKInputs, []common.ExitInfo, error) {
func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs []common.L1Tx, l2txs []common.PoolL2Tx) (*common.ZKInputs, []common.ExitInfo, error) {
var err error
var exitTree *merkletree.MerkleTree
@@ -54,6 +54,12 @@ func (s *StateDB) ProcessTxs(l1usertxs, l1coordinatortxs []common.L1Tx, l2txs []
}
exits := make([]processedExit, nTx)
// get TokenIDs of coordIdxs
coordIdxsMap, err := s.getTokenIDsFromIdxs(coordIdxs)
if err != nil {
return nil, nil, err
}
if s.typ == TypeBatchBuilder {
s.zki = common.NewZKInputs(nTx, 24, 32) // TODO this values will be parameters of the function, taken from config file/coordinator call
s.zki.OldLastIdx = (s.idx - 1).BigInt()
@@ -121,7 +127,7 @@ func (s *StateDB) ProcessTxs(l1usertxs, l1coordinatortxs []common.L1Tx, l2txs []
}
}
for i := 0; i < len(l2txs); i++ {
exitIdx, exitAccount, newExit, err := s.processL2Tx(exitTree, &l2txs[i])
exitIdx, exitAccount, newExit, err := s.processL2Tx(coordIdxsMap, exitTree, &l2txs[i])
if err != nil {
return nil, nil, err
}
@@ -258,10 +264,13 @@ func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx)
}
switch tx.Type {
case common.TxTypeForceTransfer, common.TxTypeTransfer:
case common.TxTypeForceTransfer:
// go to the MT account of sender and receiver, and update balance
// & nonce
err := s.applyTransfer(tx.Tx(), 0) // 0 for the parameter toIdx, as at L1Tx ToIdx can only be 0 in the Deposit type case.
// coordIdxsMap is 'nil', as at L1Txs there is no L2 fees
// 0 for the parameter toIdx, as at L1Tx ToIdx can only be 0 in the Deposit type case.
err := s.applyTransfer(nil, tx.Tx(), 0)
if err != nil {
log.Error(err)
return nil, nil, false, err
@@ -309,9 +318,10 @@ func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx)
s.zki.AuxFromIdx[s.i] = s.idx.BigInt() // last s.idx is the one used for creating the new account
s.zki.NewAccount[s.i] = big.NewInt(1)
}
case common.TxTypeExit:
case common.TxTypeForceExit:
// execute exit flow
exitAccount, newExit, err := s.applyExit(exitTree, tx.Tx())
// coordIdxsMap is 'nil', as at L1Txs there is no L2 fees
exitAccount, newExit, err := s.applyExit(nil, exitTree, tx.Tx())
if err != nil {
log.Error(err)
return nil, nil, false, err
@@ -327,7 +337,7 @@ func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx)
// StateDB depending on the transaction Type. It returns the 3 parameters
// related to the Exit (in case of): Idx, ExitAccount, boolean determining if
// the Exit created a new Leaf in the ExitTree.
func (s *StateDB) processL2Tx(exitTree *merkletree.MerkleTree, tx *common.PoolL2Tx) (*common.Idx, *common.Account, bool, error) {
func (s *StateDB) processL2Tx(coordIdxsMap map[common.TokenID]common.Idx, exitTree *merkletree.MerkleTree, tx *common.PoolL2Tx) (*common.Idx, *common.Account, bool, error) {
var err error
// if tx.ToIdx==0, get toIdx by ToEthAddr or ToBJJ
if tx.ToIdx == common.Idx(0) && tx.AuxToIdx == common.Idx(0) {
@@ -394,14 +404,14 @@ func (s *StateDB) processL2Tx(exitTree *merkletree.MerkleTree, tx *common.PoolL2
case common.TxTypeTransfer, common.TxTypeTransferToEthAddr, common.TxTypeTransferToBJJ:
// go to the MT account of sender and receiver, and update
// balance & nonce
err = s.applyTransfer(tx.Tx(), tx.AuxToIdx)
err = s.applyTransfer(coordIdxsMap, tx.Tx(), tx.AuxToIdx)
if err != nil {
log.Error(err)
return nil, nil, false, err
}
case common.TxTypeExit:
// execute exit flow
exitAccount, newExit, err := s.applyExit(exitTree, tx.Tx())
exitAccount, newExit, err := s.applyExit(coordIdxsMap, exitTree, tx.Tx())
if err != nil {
log.Error(err)
return nil, nil, false, err
@@ -519,7 +529,7 @@ func (s *StateDB) applyDeposit(tx *common.L1Tx, transfer bool) error {
// tx.ToIdx==0, then toIdx!=0, and will be used the toIdx parameter as Idx of
// the receiver. This parameter is used when the tx.ToIdx is not specified and
// the real ToIdx is found trhrough the ToEthAddr or ToBJJ.
func (s *StateDB) applyTransfer(tx common.Tx, auxToIdx common.Idx) error {
func (s *StateDB) applyTransfer(coordIdxsMap map[common.TokenID]common.Idx, tx common.Tx, auxToIdx common.Idx) error {
if auxToIdx == common.Idx(0) {
auxToIdx = tx.ToIdx
}
@@ -543,8 +553,18 @@ func (s *StateDB) applyTransfer(tx common.Tx, auxToIdx common.Idx) error {
fee := common.CalcFeeAmount(tx.Amount, *tx.Fee)
feeAndAmount := new(big.Int).Add(tx.Amount, fee)
accSender.Balance = new(big.Int).Sub(accSender.Balance, feeAndAmount)
// TODO send the fee to the Fee Idx of the Coordinator for the
// TokenID
// send the fee to the Idx of the Coordinator for the TokenID
accCoord, err := s.GetAccount(coordIdxsMap[tx.TokenID])
if err != nil {
log.Errorf("applyTransfer error: Tx=%s, error: %s", tx.String(), err)
return err
}
accCoord.Balance = new(big.Int).Add(accCoord.Balance, fee)
_, err = s.UpdateAccount(coordIdxsMap[tx.TokenID], accCoord)
if err != nil {
log.Error(err)
return err
}
}
// add amount-feeAmount to the receiver
@@ -653,7 +673,7 @@ func (s *StateDB) applyCreateAccountDepositTransfer(tx *common.L1Tx) error {
// It returns the ExitAccount and a boolean determining if the Exit created a
// new Leaf in the ExitTree.
func (s *StateDB) applyExit(exitTree *merkletree.MerkleTree, tx common.Tx) (*common.Account, bool, error) {
func (s *StateDB) applyExit(coordIdxsMap map[common.TokenID]common.Idx, exitTree *merkletree.MerkleTree, tx common.Tx) (*common.Account, bool, error) {
// 0. subtract tx.Amount from current Account in StateMT
// add the tx.Amount into the Account (tx.FromIdx) in the ExitMT
acc, err := s.GetAccount(tx.FromIdx)
@@ -666,8 +686,19 @@ func (s *StateDB) applyExit(exitTree *merkletree.MerkleTree, tx common.Tx) (*com
fee := common.CalcFeeAmount(tx.Amount, *tx.Fee)
feeAndAmount := new(big.Int).Add(tx.Amount, fee)
acc.Balance = new(big.Int).Sub(acc.Balance, feeAndAmount)
// TODO send the fee to the Fee Idx of the Coordinator for the
// TokenID
// send the fee to the Idx of the Coordinator for the TokenID
accCoord, err := s.GetAccount(coordIdxsMap[tx.TokenID])
if err != nil {
log.Errorf("applyExit error: Tx=%s, error: %s", tx.String(), err)
return nil, false, err
}
accCoord.Balance = new(big.Int).Add(accCoord.Balance, fee)
_, err = s.UpdateAccount(coordIdxsMap[tx.TokenID], accCoord)
if err != nil {
log.Error(err)
return nil, false, err
}
}
p, err := s.UpdateAccount(tx.FromIdx, acc)

View File

@@ -7,6 +7,7 @@ import (
"github.com/hermeznetwork/hermez-node/common"
"github.com/hermeznetwork/hermez-node/eth"
"github.com/hermeznetwork/hermez-node/log"
"github.com/hermeznetwork/hermez-node/test/til"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -28,53 +29,74 @@ func TestProcessTxsSynchronizer(t *testing.T) {
blocks, err := tc.GenerateBlocks(til.SetBlockchain0)
require.Nil(t, err)
assert.Equal(t, 29, len(blocks[0].L1UserTxs))
assert.Equal(t, 0, len(blocks[0].Batches[0].L1CoordinatorTxs))
assert.Equal(t, 21, len(blocks[0].Batches[1].L2Txs))
assert.Equal(t, 31, len(blocks[0].L1UserTxs))
assert.Equal(t, 4, len(blocks[0].Batches[0].L1CoordinatorTxs))
assert.Equal(t, 0, len(blocks[0].Batches[1].L1CoordinatorTxs))
assert.Equal(t, 22, len(blocks[0].Batches[2].L2Txs))
assert.Equal(t, 1, len(blocks[1].Batches[0].L1CoordinatorTxs))
assert.Equal(t, 59, len(blocks[1].Batches[0].L2Txs))
assert.Equal(t, 1, len(blocks[1].Batches[1].L1CoordinatorTxs))
assert.Equal(t, 8, len(blocks[1].Batches[1].L2Txs))
// use first batch
l2Txs := common.L2TxsToPoolL2Txs(blocks[0].Batches[0].L2Txs)
_, exitInfos, err := sdb.ProcessTxs(blocks[0].L1UserTxs, blocks[0].Batches[0].L1CoordinatorTxs, l2Txs)
// Coordinator Idx where to send the fees
coordIdxs := []common.Idx{256, 257, 258, 259}
// Idx of user 'A'
idxA1 := tc.Users["A"].Accounts[common.TokenID(1)].Idx
log.Debug("1st batch, 1st block, only L1CoordinatorTxs")
_, _, err = sdb.ProcessTxs(nil, nil, blocks[0].Batches[0].L1CoordinatorTxs, nil)
require.Nil(t, err)
log.Debug("2nd batch, 1st block")
l2Txs := common.L2TxsToPoolL2Txs(blocks[0].Batches[1].L2Txs)
_, exitInfos, err := sdb.ProcessTxs(coordIdxs, blocks[0].L1UserTxs, blocks[0].Batches[1].L1CoordinatorTxs, l2Txs)
require.Nil(t, err)
assert.Equal(t, 0, len(exitInfos))
acc, err := sdb.GetAccount(common.Idx(256))
acc, err := sdb.GetAccount(idxA1)
require.Nil(t, err)
assert.Equal(t, "50", acc.Balance.String())
// second batch of first block
l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Batches[1].L2Txs)
_, exitInfos, err = sdb.ProcessTxs(nil, blocks[0].Batches[1].L1CoordinatorTxs, l2Txs)
log.Debug("3rd batch, 1st block")
l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Batches[2].L2Txs)
_, exitInfos, err = sdb.ProcessTxs(coordIdxs, nil, blocks[0].Batches[2].L1CoordinatorTxs, l2Txs)
require.Nil(t, err)
// TODO once TTGL is updated, add a check that a input poolL2Tx with
// Nonce & TokenID =0, after ProcessTxs call has the expected value
assert.Equal(t, 0, len(exitInfos))
acc, err = sdb.GetAccount(common.Idx(256))
acc, err = sdb.GetAccount(idxA1)
require.Nil(t, err)
assert.Equal(t, "28", acc.Balance.String())
// use second batch
log.Debug("1st batch, 2nd block")
l2Txs = common.L2TxsToPoolL2Txs(blocks[1].Batches[0].L2Txs)
_, exitInfos, err = sdb.ProcessTxs(nil, blocks[1].Batches[0].L1CoordinatorTxs, l2Txs)
_, exitInfos, err = sdb.ProcessTxs(coordIdxs, nil, blocks[1].Batches[0].L1CoordinatorTxs, l2Txs)
require.Nil(t, err)
assert.Equal(t, 4, len(exitInfos)) // the 'ForceExit(1)' is not computed yet, as the batch is without L1UserTxs
acc, err = sdb.GetAccount(common.Idx(256))
acc, err = sdb.GetAccount(idxA1)
require.Nil(t, err)
assert.Equal(t, "53", acc.Balance.String())
// use third batch
log.Debug("2nd batch, 2nd block")
l2Txs = common.L2TxsToPoolL2Txs(blocks[1].Batches[1].L2Txs)
_, exitInfos, err = sdb.ProcessTxs(blocks[1].L1UserTxs, blocks[1].Batches[1].L1CoordinatorTxs, l2Txs)
_, exitInfos, err = sdb.ProcessTxs(coordIdxs, blocks[1].L1UserTxs, blocks[1].Batches[1].L1CoordinatorTxs, l2Txs)
require.Nil(t, err)
assert.Equal(t, 2, len(exitInfos)) // 2, as previous batch was without L1UserTxs, and has pending the 'ForceExit(1) A: 5'
acc, err = sdb.GetAccount(common.Idx(256))
acc, err = sdb.GetAccount(idxA1)
assert.Nil(t, err)
assert.Equal(t, "78", acc.Balance.String())
idxB0 := tc.Users["C"].Accounts[common.TokenID(0)].Idx
acc, err = sdb.GetAccount(idxB0)
require.Nil(t, err)
assert.Equal(t, "51", acc.Balance.String())
// get balance of Coordinator account for TokenID==0
acc, err = sdb.GetAccount(common.Idx(256))
require.Nil(t, err)
assert.Equal(t, "2", acc.Balance.String())
}
/*
@@ -102,30 +124,33 @@ func TestProcessTxsBatchBuilder(t *testing.T) {
assert.Equal(t, 0, len(blocks[0].Batches[2].L1CoordinatorTxs))
assert.Equal(t, 8, len(blocks[0].Batches[2].L2Txs))
// Idx of user 'A'
idxA1 := tc.Users["A"].Accounts[common.TokenID(1)].Idx
// use first batch
l2Txs := common.L2TxsToPoolL2Txs(blocks[0].Batches[0].L2Txs)
_, exitInfos, err := sdb.ProcessTxs(blocks[0].Batches[0].L1UserTxs, blocks[0].Batches[0].L1CoordinatorTxs, l2Txs)
_, exitInfos, err := sdb.ProcessTxs(coordIdxs, blocks[0].Batches[0].L1UserTxs, blocks[0].Batches[0].L1CoordinatorTxs, l2Txs)
require.Nil(t, err)
assert.Equal(t, 0, len(exitInfos))
acc, err := sdb.GetAccount(common.Idx(256))
acc, err := sdb.GetAccount(idxA1)
assert.Nil(t, err)
assert.Equal(t, "28", acc.Balance.String())
// use second batch
l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Batches[1].L2Txs)
_, exitInfos, err = sdb.ProcessTxs(blocks[0].Batches[1].L1UserTxs, blocks[0].Batches[1].L1CoordinatorTxs, l2Txs)
_, exitInfos, err = sdb.ProcessTxs(coordIdxs, blocks[0].Batches[1].L1UserTxs, blocks[0].Batches[1].L1CoordinatorTxs, l2Txs)
require.Nil(t, err)
assert.Equal(t, 5, len(exitInfos))
acc, err = sdb.GetAccount(common.Idx(256))
acc, err = sdb.GetAccount(idxA1)
require.Nil(t, err)
assert.Equal(t, "48", acc.Balance.String())
// use third batch
l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Batches[2].L2Txs)
_, exitInfos, err = sdb.ProcessTxs(blocks[0].Batches[2].L1UserTxs, blocks[0].Batches[2].L1CoordinatorTxs, l2Txs)
_, exitInfos, err = sdb.ProcessTxs(coordIdxs, blocks[0].Batches[2].L1UserTxs, blocks[0].Batches[2].L1CoordinatorTxs, l2Txs)
require.Nil(t, err)
assert.Equal(t, 1, len(exitInfos))
acc, err = sdb.GetAccount(common.Idx(256))
acc, err = sdb.GetAccount(idxA1)
assert.Nil(t, err)
assert.Equal(t, "23", acc.Balance.String())
}
@@ -146,7 +171,7 @@ func TestZKInputsGeneration(t *testing.T) {
assert.Equal(t, 21, len(blocks[0].Batches[0].L2Txs))
l2Txs := common.L2TxsToPoolL2Txs(blocks[0].Batches[0].L2Txs)
zki, _, err := sdb.ProcessTxs(blocks[0].Batches[0].L1UserTxs, blocks[0].Batches[0].L1CoordinatorTxs, l2Txs)
zki, _, err := sdb.ProcessTxs(coordIdxs, blocks[0].Batches[0].L1UserTxs, blocks[0].Batches[0].L1CoordinatorTxs, l2Txs)
require.Nil(t, err)
s, err := json.Marshal(zki)

View File

@@ -2,6 +2,7 @@ package statedb
import (
"bytes"
"fmt"
"math/big"
ethCommon "github.com/ethereum/go-ethereum/common"
@@ -108,6 +109,18 @@ func (s *StateDB) GetIdxByEthAddrBJJ(addr ethCommon.Address, pk *babyjub.PublicK
return common.Idx(0), ErrToIdxNotFound
}
func (s *StateDB) getTokenIDsFromIdxs(idxs []common.Idx) (map[common.TokenID]common.Idx, error) {
m := make(map[common.TokenID]common.Idx)
for i := 0; i < len(idxs); i++ {
a, err := s.GetAccount(idxs[i])
if err != nil {
return nil, fmt.Errorf("getTokenIDsFromIdxs error on GetAccount with Idx==%d: %s", idxs[i], err.Error())
}
m[a.TokenID] = idxs[i]
}
return m, nil
}
func siblingsToZKInputFormat(s []*merkletree.Hash) []*big.Int {
b := make([]*big.Int, len(s))
for i := 0; i < len(s); i++ {