From 49366e3fa41cadc85acc9102fb37418fab896886 Mon Sep 17 00:00:00 2001 From: a_bennassar Date: Tue, 6 Oct 2020 10:34:49 +0200 Subject: [PATCH] Feature/null refactor (#173) * WIP: rebase * Make nullable fields use pointers --- api/api_test.go | 10 ++--- api/dbtoapistructs.go | 14 ++++--- api/parsers.go | 3 -- api/parsers_test.go | 4 -- api/swagger.yml | 60 +++++++++++++++++++++------- common/batch.go | 3 +- common/l1tx.go | 32 ++++++++++----- common/l1tx_test.go | 12 ++++-- common/l2tx.go | 16 ++++++-- common/pooll2tx.go | 71 +++++++++++++++++++++------------ common/pooll2tx_test.go | 27 ++++++++----- common/tx.go | 29 +++++++------- db/historydb/historydb.go | 3 +- db/historydb/historydb_test.go | 2 +- db/historydb/views.go | 8 ++-- db/l2db/l2db.go | 28 +++++++------ db/l2db/l2db_test.go | 25 ++++++------ db/migrations/0001.sql | 13 +++--- db/statedb/statedb.go | 29 ++++++++++---- db/statedb/statedb_test.go | 27 ++++++++----- db/statedb/txprocessors.go | 48 +++++++++++++++------- db/statedb/txprocessors_test.go | 17 ++++---- db/statedb/utils.go | 15 +++++-- db/statedb/utils_test.go | 22 +++++----- synchronizer/synchronizer.go | 21 ++++++---- test/ethclient.go | 2 +- test/ethclient_test.go | 2 +- test/historydb.go | 25 +++++++----- test/l2db.go | 37 ++++++++++++----- test/txs.go | 19 ++++++--- test/txs_test.go | 2 +- txselector/txselector.go | 36 ++++++++++------- 32 files changed, 422 insertions(+), 240 deletions(-) diff --git a/api/api_test.go b/api/api_test.go index 1ea3d4d..6ffd044 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -223,7 +223,7 @@ func TestMain(m *testing.M) { panic("Token not found") } } else { - token = test.GetToken(genericTx.FromIdx, accs, tokens) + token = test.GetToken(*genericTx.FromIdx, accs, tokens) } var usd, loadUSD, feeUSD *float64 if token.USD != nil { @@ -244,7 +244,7 @@ func TestMain(m *testing.M) { Type: genericTx.Type, Position: genericTx.Position, FromIdx: genericTx.FromIdx, - ToIdx: genericTx.ToIdx, + ToIdx: *genericTx.ToIdx, Amount: genericTx.Amount, AmountFloat: genericTx.AmountFloat, HistoricUSD: usd, @@ -361,7 +361,7 @@ func TestGetHistoryTxs(t *testing.T) { // idx fetchedTxs = historyTxAPIs{} limit = 4 - idx := tc.allTxs[0].FromIdx + idx := tc.allTxs[0].ToIdx path = fmt.Sprintf( "%s?accountIndex=%s&limit=%d&offset=", endpoint, idx, limit, @@ -370,7 +370,8 @@ func TestGetHistoryTxs(t *testing.T) { assert.NoError(t, err) idxTxs := historyTxAPIs{} for i := 0; i < len(tc.allTxs); i++ { - if tc.allTxs[i].FromIdx[6:] == idx[6:] { + if (tc.allTxs[i].FromIdx != nil && (*tc.allTxs[i].FromIdx)[6:] == idx[6:]) || + tc.allTxs[i].ToIdx[6:] == idx[6:] { idxTxs = append(idxTxs, tc.allTxs[i]) } } @@ -396,7 +397,6 @@ func TestGetHistoryTxs(t *testing.T) { // type txTypes := []common.TxType{ common.TxTypeExit, - common.TxTypeWithdrawn, common.TxTypeTransfer, common.TxTypeDeposit, common.TxTypeCreateAccountDeposit, diff --git a/api/dbtoapistructs.go b/api/dbtoapistructs.go index 0e62abc..51d5f13 100644 --- a/api/dbtoapistructs.go +++ b/api/dbtoapistructs.go @@ -38,7 +38,7 @@ func (htx *historyTxsAPI) GetPagination() pagination { return htx.Pagination } func (htx *historyTxsAPI) Len() int { return len(htx.Txs) } type l1Info struct { - ToForgeL1TxsNum int64 `json:"toForgeL1TransactionsNum"` + ToForgeL1TxsNum *int64 `json:"toForgeL1TransactionsNum"` UserOrigin bool `json:"userOrigin"` FromEthAddr string `json:"fromHezEthereumAddress"` FromBJJ string `json:"fromBJJ"` @@ -58,7 +58,7 @@ type historyTxAPI struct { TxID string `json:"id"` Type common.TxType `json:"type"` Position int `json:"position"` - FromIdx string `json:"fromAccountIndex"` + FromIdx *string `json:"fromAccountIndex"` ToIdx string `json:"toAccountIndex"` Amount string `json:"amount"` BatchNum *common.BatchNum `json:"batchNum"` @@ -76,7 +76,6 @@ func historyTxsToAPI(dbTxs []*historydb.HistoryTx) []historyTxAPI { TxID: dbTxs[i].TxID.String(), Type: dbTxs[i].Type, Position: dbTxs[i].Position, - FromIdx: idxToHez(dbTxs[i].FromIdx, dbTxs[i].TokenSymbol), ToIdx: idxToHez(dbTxs[i].ToIdx, dbTxs[i].TokenSymbol), Amount: dbTxs[i].Amount.String(), HistoricUSD: dbTxs[i].HistoricUSD, @@ -95,12 +94,17 @@ func historyTxsToAPI(dbTxs []*historydb.HistoryTx) []historyTxAPI { L1Info: nil, L2Info: nil, } + if dbTxs[i].FromIdx != nil { + fromIdx := new(string) + *fromIdx = idxToHez(*dbTxs[i].FromIdx, dbTxs[i].TokenSymbol) + apiTx.FromIdx = fromIdx + } if dbTxs[i].IsL1 { apiTx.IsL1 = "L1" apiTx.L1Info = &l1Info{ ToForgeL1TxsNum: dbTxs[i].ToForgeL1TxsNum, - UserOrigin: dbTxs[i].UserOrigin, - FromEthAddr: ethAddrToHez(dbTxs[i].FromEthAddr), + UserOrigin: *dbTxs[i].UserOrigin, + FromEthAddr: ethAddrToHez(*dbTxs[i].FromEthAddr), FromBJJ: bjjToString(dbTxs[i].FromBJJ), LoadAmount: dbTxs[i].LoadAmount.String(), HistoricLoadAmountUSD: dbTxs[i].HistoricLoadAmountUSD, diff --git a/api/parsers.go b/api/parsers.go index 395dde8..5d93320 100644 --- a/api/parsers.go +++ b/api/parsers.go @@ -149,9 +149,6 @@ func parseQueryTxType(c querier) (*common.TxType, error) { case common.TxTypeExit: ret := common.TxTypeExit return &ret, nil - case common.TxTypeWithdrawn: - ret := common.TxTypeWithdrawn - return &ret, nil case common.TxTypeTransfer: ret := common.TxTypeTransfer return &ret, nil diff --git a/api/parsers_test.go b/api/parsers_test.go index 3e591d0..779904d 100644 --- a/api/parsers_test.go +++ b/api/parsers_test.go @@ -239,10 +239,6 @@ func TestParseQueryTxType(t *testing.T) { res, err = parseQueryTxType(c) assert.NoError(t, err) assert.Equal(t, common.TxTypeExit, *res) - c.m[name] = string(common.TxTypeWithdrawn) - res, err = parseQueryTxType(c) - assert.NoError(t, err) - assert.Equal(t, common.TxTypeWithdrawn, *res) c.m[name] = string(common.TxTypeTransfer) res, err = parseQueryTxType(c) assert.NoError(t, err) diff --git a/api/swagger.yml b/api/swagger.yml index dc2ded5..e0504bf 100644 --- a/api/swagger.yml +++ b/api/swagger.yml @@ -1240,13 +1240,24 @@ components: fromAccountIndex: $ref: '#/components/schemas/AccountIndex' toAccountIndex: - allOf: - - $ref: '#/components/schemas/AccountIndex' - - example: "hez:DAI:672" + type: string + description: >- + Identifier of the destination account. It references the position where the account is inside the state Merkle tree. + The identifier is built using: `hez:` + `token symbol:` + `index` + example: null + nullable: true toHezEthereumAddress: - $ref: '#/components/schemas/HezEthereumAddress' + type: string + description: "Address of an Etherum account linked to the Hermez network." + pattern: "^hez:0x[a-fA-F0-9]{40}$" + example: "hez:0xaa942cfcd25ad4d90a62358b0dd84f33b398262a" + nullable: true toBjj: - $ref: '#/components/schemas/BJJ' + type: string + description: "BabyJubJub public key, encoded as base64 URL (RFC 4648), which result in 33 bytes. The padding byte is replaced by a sum of the encoded bytes." + pattern: "^hez:[A-Za-z0-9_-]{44}$" + example: null + nullable: true amount: allOf: - $ref: '#/components/schemas/BigInt' @@ -1308,6 +1319,7 @@ components: - type - tokenId - fromAccountIndex + - toAccountIndex - toHezAccountIndex - toHezEthereumAddress - toBjj @@ -1333,13 +1345,24 @@ components: fromAccountIndex: $ref: '#/components/schemas/AccountIndex' toAccountIndex: - allOf: - - $ref: '#/components/schemas/AccountIndex' - - example: "hez:DAI:672" + type: string + description: >- + Identifier of the destination account. It references the position where the account is inside the state Merkle tree. + The identifier is built using: `hez:` + `token symbol:` + `index` + example: "hez:DAI:309" + nullable: true toHezEthereumAddress: - $ref: '#/components/schemas/HezEthereumAddress' + type: string + description: "Address of an Etherum account linked to the Hermez network." + pattern: "^hez:0x[a-fA-F0-9]{40}$" + example: null + nullable: true toBjj: - $ref: '#/components/schemas/BJJ' + type: string + description: "BabyJubJub public key, encoded as base64 URL (RFC 4648), which result in 33 bytes. The padding byte is replaced by a sum of the encoded bytes." + pattern: "^hez:[A-Za-z0-9_-]{44}$" + example: null + nullable: true amount: allOf: - $ref: '#/components/schemas/BigInt' @@ -1448,7 +1471,6 @@ components: description: Type of transaction. enum: - Exit - - Withdrawn - Transfer - Deposit - CreateAccountDeposit @@ -1539,7 +1561,12 @@ components: position: $ref: '#/components/schemas/TransactionPosition' fromAccountIndex: - $ref: '#/components/schemas/AccountIndex' + type: string + description: >- + Identifier of an account. It references the position where the account is inside the state Merkle tree. + The identifier is built using: `hez:` + `token symbol:` + `index` + example: "hez:DAI:4444" + nullable: true toAccountIndex: allOf: - $ref: '#/components/schemas/AccountIndex' @@ -1666,6 +1693,7 @@ components: minimum: 0 maximum: 4294967295 example: 784 + nullable: true TransactionPosition: type: integer description: Position that a transaction occupies in a batch. @@ -1689,8 +1717,8 @@ components: items: type: object properties: - token: - $ref: '#/components/schemas/Token' + tokenId: + $ref: '#/components/schemas/TokenId' amount: allOf: - $ref: '#/components/schemas/BigInt' @@ -1716,6 +1744,9 @@ components: $ref: '#/components/schemas/EthereumAddress' collectedFees: $ref: '#/components/schemas/CollectedFees' + totalCollectedFeesUSD: + type: number + description: Value in USD of the collected tokens by the forger in concept of fees. This is calculated at the moment the batch is forged, with the conversion rates at that time. historicTotalCollectedFeesUSD: type: number description: Sum of the all the fees collected, in USD, at the moment the batch was forged. @@ -1737,7 +1768,6 @@ components: allOf: - $ref: '#/components/schemas/ToForgeL1TransactionsNum' - description: Identifier that corresponds to the group of L1 transactions forged in the current batch. - - nullable: true - example: 5 slotNum: $ref: '#/components/schemas/SlotNum' diff --git a/common/batch.go b/common/batch.go index 5ba7953..035b9fb 100644 --- a/common/batch.go +++ b/common/batch.go @@ -19,8 +19,9 @@ type Batch struct { StateRoot Hash `meddler:"state_root"` NumAccounts int `meddler:"num_accounts"` ExitRoot Hash `meddler:"exit_root"` - ForgeL1TxsNum int64 `meddler:"forge_l1_txs_num"` // optional, Only when the batch forges L1 txs. Identifier that corresponds to the group of L1 txs forged in the current batch. + ForgeL1TxsNum *int64 `meddler:"forge_l1_txs_num"` // optional, Only when the batch forges L1 txs. Identifier that corresponds to the group of L1 txs forged in the current batch. SlotNum SlotNum `meddler:"slot_num"` // Slot in which the batch is forged + TotalFeesUSD *float64 `meddler:"total_fees_usd"` } // BatchNum identifies a batch diff --git a/common/l1tx.go b/common/l1tx.go index e01b169..07fffbc 100644 --- a/common/l1tx.go +++ b/common/l1tx.go @@ -25,10 +25,10 @@ type L1Tx struct { // - L1UserTx: 0 // - L1CoordinatorTx: 1 TxID TxID - ToForgeL1TxsNum int64 // toForgeL1TxsNum in which the tx was forged / will be forged + ToForgeL1TxsNum *int64 // toForgeL1TxsNum in which the tx was forged / will be forged Position int UserOrigin bool // true if the tx was originated by a user, false if it was aoriginated by a coordinator. Note that this differ from the spec for implementation simplification purpposes - FromIdx Idx // FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.LoadAmount (deposit) + FromIdx *Idx // FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.LoadAmount (deposit) FromEthAddr ethCommon.Address FromBJJ *babyjub.PublicKey ToIdx Idx // ToIdx is ignored in L1Tx/Deposit, but used in the L1Tx/DepositAndTransfer @@ -47,7 +47,7 @@ type L1Tx struct { func NewL1Tx(l1Tx *L1Tx) (*L1Tx, error) { // calculate TxType var txType TxType - if l1Tx.FromIdx == Idx(0) { + if l1Tx.FromIdx == nil { if l1Tx.ToIdx == Idx(0) { txType = TxTypeCreateAccountDeposit } else if l1Tx.ToIdx >= IdxUserThreshold { @@ -55,7 +55,7 @@ func NewL1Tx(l1Tx *L1Tx) (*L1Tx, error) { } else { return l1Tx, fmt.Errorf("Can not determine type of L1Tx, invalid ToIdx value: %d", l1Tx.ToIdx) } - } else if l1Tx.FromIdx >= IdxUserThreshold { + } else if *l1Tx.FromIdx >= IdxUserThreshold { if l1Tx.ToIdx == Idx(0) { txType = TxTypeDeposit } else if l1Tx.ToIdx == Idx(1) { @@ -83,7 +83,11 @@ func NewL1Tx(l1Tx *L1Tx) (*L1Tx, error) { txid[0] = TxIDPrefixL1CoordTx } var toForgeL1TxsNumBytes [8]byte - binary.BigEndian.PutUint64(toForgeL1TxsNumBytes[:], uint64(l1Tx.ToForgeL1TxsNum)) + var toForge uint64 = 0 + if l1Tx.ToForgeL1TxsNum != nil { + toForge = uint64(*l1Tx.ToForgeL1TxsNum) + } + binary.BigEndian.PutUint64(toForgeL1TxsNumBytes[:], toForge) copy(txid[1:9], toForgeL1TxsNumBytes[:]) var positionBytes [2]byte @@ -98,19 +102,25 @@ func NewL1Tx(l1Tx *L1Tx) (*L1Tx, error) { func (tx *L1Tx) Tx() *Tx { f := new(big.Float).SetInt(tx.Amount) amountFloat, _ := f.Float64() + userOrigin := new(bool) + *userOrigin = tx.UserOrigin + fromEthAddr := new(ethCommon.Address) + *fromEthAddr = tx.FromEthAddr + toIdx := new(Idx) + *toIdx = tx.ToIdx genericTx := &Tx{ IsL1: true, TxID: tx.TxID, Type: tx.Type, Position: tx.Position, FromIdx: tx.FromIdx, - ToIdx: tx.ToIdx, + ToIdx: toIdx, Amount: tx.Amount, AmountFloat: amountFloat, TokenID: tx.TokenID, ToForgeL1TxsNum: tx.ToForgeL1TxsNum, - UserOrigin: tx.UserOrigin, - FromEthAddr: tx.FromEthAddr, + UserOrigin: userOrigin, + FromEthAddr: fromEthAddr, FromBJJ: tx.FromBJJ, LoadAmount: tx.LoadAmount, EthBlockNum: tx.EthBlockNum, @@ -171,10 +181,14 @@ func L1TxFromBytes(b []byte) (*L1Tx, error) { if err != nil { return nil, err } - tx.FromIdx, err = IdxFromBytes(b[52:58]) + fromIdx, err := IdxFromBytes(b[52:58]) if err != nil { return nil, err } + if fromIdx != 0 { + tx.FromIdx = new(Idx) + *tx.FromIdx = fromIdx + } tx.LoadAmount = Float16FromBytes(b[58:60]).BigInt() tx.Amount = Float16FromBytes(b[60:62]).BigInt() tx.TokenID, err = TokenIDFromBytes(b[62:66]) diff --git a/common/l1tx_test.go b/common/l1tx_test.go index b6527fc..d91b8bd 100644 --- a/common/l1tx_test.go +++ b/common/l1tx_test.go @@ -12,14 +12,18 @@ import ( ) func TestNewL1Tx(t *testing.T) { + toForge := new(int64) + *toForge = 123456 + fromIdx := new(Idx) + *fromIdx = 300 l1Tx := &L1Tx{ - ToForgeL1TxsNum: int64(123456), + ToForgeL1TxsNum: toForge, Position: 71, ToIdx: 301, TokenID: 5, Amount: big.NewInt(1), LoadAmount: big.NewInt(2), - FromIdx: 300, + FromIdx: fromIdx, } l1Tx, err := NewL1Tx(l1Tx) assert.Nil(t, err) @@ -34,12 +38,14 @@ func TestL1TxByteParsers(t *testing.T) { pk, err := pkComp.Decompress() require.Nil(t, err) + fromIdx := new(Idx) + *fromIdx = 2 l1Tx := &L1Tx{ ToIdx: 3, TokenID: 5, Amount: big.NewInt(1), LoadAmount: big.NewInt(2), - FromIdx: 2, + FromIdx: fromIdx, FromBJJ: pk, FromEthAddr: ethCommon.HexToAddress("0xc58d29fA6e86E4FAe04DDcEd660d45BCf3Cb2370"), } diff --git a/common/l2tx.go b/common/l2tx.go index 2750b4d..82d7f35 100644 --- a/common/l2tx.go +++ b/common/l2tx.go @@ -68,13 +68,17 @@ func (tx *L2Tx) Tx() *Tx { *fee = tx.Fee nonce := new(Nonce) *nonce = tx.Nonce + fromIdx := new(Idx) + *fromIdx = tx.FromIdx + toIdx := new(Idx) + *toIdx = tx.ToIdx return &Tx{ IsL1: false, TxID: tx.TxID, Type: tx.Type, Position: tx.Position, - FromIdx: tx.FromIdx, - ToIdx: tx.ToIdx, + FromIdx: fromIdx, + ToIdx: toIdx, Amount: tx.Amount, USD: tx.USD, AmountFloat: amountFloat, @@ -89,11 +93,15 @@ func (tx *L2Tx) Tx() *Tx { // PoolL2Tx returns the data structure of PoolL2Tx with the parameters of a // L2Tx filled func (tx *L2Tx) PoolL2Tx() *PoolL2Tx { + batchNum := new(BatchNum) + *batchNum = tx.BatchNum + toIdx := new(Idx) + *toIdx = tx.ToIdx return &PoolL2Tx{ TxID: tx.TxID, - BatchNum: tx.BatchNum, + BatchNum: batchNum, FromIdx: tx.FromIdx, - ToIdx: tx.ToIdx, + ToIdx: toIdx, Amount: tx.Amount, Fee: tx.Fee, Nonce: tx.Nonce, diff --git a/common/pooll2tx.go b/common/pooll2tx.go index bcf5372..5a6630a 100644 --- a/common/pooll2tx.go +++ b/common/pooll2tx.go @@ -1,6 +1,7 @@ package common import ( + "errors" "fmt" "math/big" "time" @@ -19,8 +20,8 @@ type PoolL2Tx struct { // values: | type | FromIdx | Nonce | TxID TxID `meddler:"tx_id"` FromIdx Idx `meddler:"from_idx"` // FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.LoadAmount (deposit) - ToIdx Idx `meddler:"to_idx"` // ToIdx is ignored in L1Tx/Deposit, but used in the L1Tx/DepositAndTransfer - ToEthAddr ethCommon.Address `meddler:"to_eth_addr"` + ToIdx *Idx `meddler:"to_idx"` // ToIdx is ignored in L1Tx/Deposit, but used in the L1Tx/DepositAndTransfer + ToEthAddr *ethCommon.Address `meddler:"to_eth_addr"` ToBJJ *babyjub.PublicKey `meddler:"to_bjj"` // TODO: stop using json, use scanner/valuer TokenID TokenID `meddler:"token_id"` Amount *big.Int `meddler:"amount,bigint"` // TODO: change to float16 @@ -32,21 +33,20 @@ type PoolL2Tx struct { Signature *babyjub.Signature `meddler:"signature"` // tx signature Timestamp time.Time `meddler:"timestamp,utctime"` // time when added to the tx pool // Stored in DB: optional fileds, may be uninitialized - BatchNum BatchNum `meddler:"batch_num,zeroisnull"` // batchNum in which this tx was forged. Presence indicates "forged" state. - RqFromIdx Idx `meddler:"rq_from_idx,zeroisnull"` // FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.LoadAmount (deposit) - RqToIdx Idx `meddler:"rq_to_idx,zeroisnull"` // FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.LoadAmount (deposit) - RqToEthAddr ethCommon.Address `meddler:"rq_to_eth_addr"` + BatchNum *BatchNum `meddler:"batch_num"` // batchNum in which this tx was forged. Presence indicates "forged" state. + RqFromIdx *Idx `meddler:"rq_from_idx"` // FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.LoadAmount (deposit) + RqToIdx *Idx `meddler:"rq_to_idx"` // FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.LoadAmount (deposit) + RqToEthAddr *ethCommon.Address `meddler:"rq_to_eth_addr"` RqToBJJ *babyjub.PublicKey `meddler:"rq_to_bjj"` // TODO: stop using json, use scanner/valuer - RqTokenID TokenID `meddler:"rq_token_id,zeroisnull"` + RqTokenID *TokenID `meddler:"rq_token_id"` RqAmount *big.Int `meddler:"rq_amount,bigintnull"` // TODO: change to float16 - RqFee FeeSelector `meddler:"rq_fee,zeroisnull"` - RqNonce uint64 `meddler:"rq_nonce,zeroisnull"` // effective 48 bits used + RqFee *FeeSelector `meddler:"rq_fee"` + RqNonce *uint64 `meddler:"rq_nonce"` // effective 48 bits used AbsoluteFee *float64 `meddler:"fee_usd"` AbsoluteFeeUpdate *time.Time `meddler:"usd_update,utctime"` Type TxType `meddler:"tx_type"` // Extra metadata, may be uninitialized RqTxCompressedData []byte `meddler:"-"` // 253 bits, optional for atomic txs - TokenSymbol string `meddler:"token_symbol"` } // NewPoolL2Tx returns the given L2Tx with the TxId & Type parameters calculated @@ -54,11 +54,11 @@ type PoolL2Tx struct { func NewPoolL2Tx(poolL2Tx *PoolL2Tx) (*PoolL2Tx, error) { // calculate TxType var txType TxType - if poolL2Tx.ToIdx == Idx(0) { + if poolL2Tx.ToIdx == nil || *poolL2Tx.ToIdx == Idx(0) { txType = TxTypeTransfer - } else if poolL2Tx.ToIdx == Idx(1) { + } else if *poolL2Tx.ToIdx == Idx(1) { txType = TxTypeExit - } else if poolL2Tx.ToIdx >= IdxUserThreshold { + } else if *poolL2Tx.ToIdx >= IdxUserThreshold { txType = TxTypeTransfer } else { return poolL2Tx, fmt.Errorf("Can not determine type of PoolL2Tx, invalid ToIdx value: %d", poolL2Tx.ToIdx) @@ -189,14 +189,21 @@ func (tx *PoolL2Tx) HashToSign() (*big.Int, error) { if err != nil { return nil, err } - toEthAddr := EthAddrToBigInt(tx.ToEthAddr) + toEthAddr := big.NewInt(0) + if tx.ToEthAddr != nil { + toEthAddr = EthAddrToBigInt(*tx.ToEthAddr) + } + rqToEthAddr := big.NewInt(0) + if tx.RqToEthAddr != nil { + rqToEthAddr = EthAddrToBigInt(*tx.RqToEthAddr) + } toBJJAy := tx.ToBJJ.Y rqTxCompressedDataV2, err := tx.TxCompressedDataV2() if err != nil { return nil, err } - return poseidon.Hash([]*big.Int{toCompressedData, toEthAddr, toBJJAy, rqTxCompressedDataV2, EthAddrToBigInt(tx.RqToEthAddr), tx.RqToBJJ.Y}) + return poseidon.Hash([]*big.Int{toCompressedData, toEthAddr, toBJJAy, rqTxCompressedDataV2, rqToEthAddr, tx.RqToBJJ.Y}) } // VerifySignature returns true if the signature verification is correct for the given PublicKey @@ -209,39 +216,51 @@ func (tx *PoolL2Tx) VerifySignature(pk *babyjub.PublicKey) bool { } // L2Tx returns a *L2Tx from the PoolL2Tx -func (tx *PoolL2Tx) L2Tx() *L2Tx { +func (tx *PoolL2Tx) L2Tx() (*L2Tx, error) { + if tx.ToIdx == nil || tx.BatchNum == nil { + return nil, errors.New("PoolL2Tx must have ToIdx != nil and BatchNum != nil in order to be able to transform to Tx") + } return &L2Tx{ TxID: tx.TxID, - BatchNum: tx.BatchNum, + BatchNum: *tx.BatchNum, FromIdx: tx.FromIdx, - ToIdx: tx.ToIdx, + ToIdx: *tx.ToIdx, Amount: tx.Amount, Fee: tx.Fee, Nonce: tx.Nonce, Type: tx.Type, - } + }, nil } // Tx returns a *Tx from the PoolL2Tx -func (tx *PoolL2Tx) Tx() *Tx { +func (tx *PoolL2Tx) Tx() (*Tx, error) { + if tx.ToIdx == nil { + return nil, errors.New("PoolL2Tx must have ToIdx != nil in order to be able to transform to Tx") + } + fromIdx := new(Idx) + *fromIdx = tx.FromIdx return &Tx{ TxID: tx.TxID, - FromIdx: tx.FromIdx, + FromIdx: fromIdx, ToIdx: tx.ToIdx, Amount: tx.Amount, Nonce: &tx.Nonce, Fee: &tx.Fee, Type: tx.Type, - } + }, nil } // PoolL2TxsToL2Txs returns an array of []*L2Tx from an array of []*PoolL2Tx -func PoolL2TxsToL2Txs(txs []*PoolL2Tx) []*L2Tx { +func PoolL2TxsToL2Txs(txs []*PoolL2Tx) ([]*L2Tx, error) { var r []*L2Tx - for _, tx := range txs { - r = append(r, tx.L2Tx()) + for _, poolTx := range txs { + tx, err := poolTx.L2Tx() + if err != nil { + return nil, err + } + r = append(r, tx) } - return r + return r, nil } // PoolL2TxState is a struct that represents the status of a L2 transaction diff --git a/common/pooll2tx_test.go b/common/pooll2tx_test.go index e118c4f..7e5b63c 100644 --- a/common/pooll2tx_test.go +++ b/common/pooll2tx_test.go @@ -11,9 +11,11 @@ import ( ) func TestNewPoolL2Tx(t *testing.T) { + toIdx := new(Idx) + *toIdx = 300 poolL2Tx := &PoolL2Tx{ FromIdx: 87654, - ToIdx: 300, + ToIdx: toIdx, Amount: big.NewInt(4), TokenID: 5, Nonce: 144, @@ -27,10 +29,11 @@ func TestTxCompressedData(t *testing.T) { var sk babyjub.PrivateKey _, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001")) assert.Nil(t, err) - + toIdx := new(Idx) + *toIdx = 3 tx := PoolL2Tx{ FromIdx: 2, - ToIdx: 3, + ToIdx: toIdx, Amount: big.NewInt(4), TokenID: 5, Nonce: 6, @@ -45,10 +48,10 @@ func TestTxCompressedData(t *testing.T) { assert.True(t, ok) assert.Equal(t, expected.Bytes(), txCompressedData.Bytes()) assert.Equal(t, "10000000000060000000500040000000000030000000000020001c60be60f", hex.EncodeToString(txCompressedData.Bytes())[1:]) - + *toIdx = 8 tx = PoolL2Tx{ FromIdx: 7, - ToIdx: 8, + ToIdx: toIdx, Amount: big.NewInt(9), TokenID: 10, Nonce: 11, @@ -71,15 +74,16 @@ func TestHashToSign(t *testing.T) { _, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001")) assert.Nil(t, err) ethAddr := ethCommon.HexToAddress("0xc58d29fA6e86E4FAe04DDcEd660d45BCf3Cb2370") - + toIdx := new(Idx) + *toIdx = 3 tx := PoolL2Tx{ FromIdx: 2, - ToIdx: 3, + ToIdx: toIdx, Amount: big.NewInt(4), TokenID: 5, Nonce: 6, ToBJJ: sk.Public(), - RqToEthAddr: ethAddr, + RqToEthAddr: ðAddr, RqToBJJ: sk.Public(), } toSign, err := tx.HashToSign() @@ -92,15 +96,16 @@ func TestVerifyTxSignature(t *testing.T) { _, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001")) assert.Nil(t, err) ethAddr := ethCommon.HexToAddress("0xc58d29fA6e86E4FAe04DDcEd660d45BCf3Cb2370") - + toIdx := new(Idx) + *toIdx = 3 tx := PoolL2Tx{ FromIdx: 2, - ToIdx: 3, + ToIdx: toIdx, Amount: big.NewInt(4), TokenID: 5, Nonce: 6, ToBJJ: sk.Public(), - RqToEthAddr: ethAddr, + RqToEthAddr: ðAddr, RqToBJJ: sk.Public(), } toSign, err := tx.HashToSign() diff --git a/common/tx.go b/common/tx.go index 41f0532..48ff523 100644 --- a/common/tx.go +++ b/common/tx.go @@ -3,6 +3,7 @@ package common import ( "database/sql/driver" "encoding/hex" + "errors" "fmt" "math/big" @@ -57,8 +58,6 @@ type TxType string const ( // TxTypeExit represents L2->L1 token transfer. A leaf for this account appears in the exit tree of the block TxTypeExit TxType = "Exit" - // TxTypeWithdrawn represents the balance that was moved from L2->L1 has been widthrawn from the smart contract - TxTypeWithdrawn TxType = "Withdrawn" // TxTypeTransfer represents L2->L2 token transfer TxTypeTransfer TxType = "Transfer" // TxTypeDeposit represents L1->L2 transfer @@ -86,8 +85,8 @@ type Tx struct { TxID TxID `meddler:"id"` Type TxType `meddler:"type"` Position int `meddler:"position"` - FromIdx Idx `meddler:"from_idx"` - ToIdx Idx `meddler:"to_idx"` + FromIdx *Idx `meddler:"from_idx"` + ToIdx *Idx `meddler:"to_idx"` Amount *big.Int `meddler:"amount,bigint"` AmountFloat float64 `meddler:"amount_f"` TokenID TokenID `meddler:"token_id"` @@ -95,9 +94,9 @@ type Tx struct { BatchNum *BatchNum `meddler:"batch_num"` // batchNum in which this tx was forged. If the tx is L2, this must be != 0 EthBlockNum int64 `meddler:"eth_block_num"` // Ethereum Block Number in which this L1Tx was added to the queue // L1 - ToForgeL1TxsNum int64 `meddler:"to_forge_l1_txs_num"` // toForgeL1TxsNum in which the tx was forged / will be forged - UserOrigin bool `meddler:"user_origin"` // true if the tx was originated by a user, false if it was aoriginated by a coordinator. Note that this differ from the spec for implementation simplification purpposes - FromEthAddr ethCommon.Address `meddler:"from_eth_addr"` + ToForgeL1TxsNum *int64 `meddler:"to_forge_l1_txs_num"` // toForgeL1TxsNum in which the tx was forged / will be forged + UserOrigin *bool `meddler:"user_origin"` // true if the tx was originated by a user, false if it was aoriginated by a coordinator. Note that this differ from the spec for implementation simplification purpposes + FromEthAddr *ethCommon.Address `meddler:"from_eth_addr"` FromBJJ *babyjub.PublicKey `meddler:"from_bjj"` LoadAmount *big.Int `meddler:"load_amount,bigintnull"` LoadAmountFloat *float64 `meddler:"load_amount_f"` @@ -109,22 +108,24 @@ type Tx struct { } // L1Tx returns a *L1Tx from the Tx -func (tx *Tx) L1Tx() *L1Tx { - l1Tx := &L1Tx{ +func (tx *Tx) L1Tx() (*L1Tx, error) { + if tx.UserOrigin == nil || tx.FromEthAddr == nil { + return nil, errors.New("Tx must have UserOrigin != nil and FromEthAddr != nil in order to be able to transform to L1Tx") + } + return &L1Tx{ TxID: tx.TxID, ToForgeL1TxsNum: tx.ToForgeL1TxsNum, Position: tx.Position, - UserOrigin: tx.UserOrigin, + UserOrigin: *tx.UserOrigin, FromIdx: tx.FromIdx, - FromEthAddr: tx.FromEthAddr, + FromEthAddr: *tx.FromEthAddr, FromBJJ: tx.FromBJJ, - ToIdx: tx.ToIdx, + ToIdx: *tx.ToIdx, TokenID: tx.TokenID, Amount: tx.Amount, LoadAmount: tx.LoadAmount, EthBlockNum: tx.EthBlockNum, Type: tx.Type, BatchNum: tx.BatchNum, - } - return l1Tx + }, nil } diff --git a/db/historydb/historydb.go b/db/historydb/historydb.go index ad5d290..d0984ac 100644 --- a/db/historydb/historydb.go +++ b/db/historydb/historydb.go @@ -133,7 +133,8 @@ func (hdb *HistoryDB) addBatches(d meddler.DB, batches []common.Batch) error { num_accounts, exit_root, forge_l1_txs_num, - slot_num + slot_num, + total_fees_usd ) VALUES %s;`, batches[:], ) diff --git a/db/historydb/historydb_test.go b/db/historydb/historydb_test.go index 18c0e4b..693a280 100644 --- a/db/historydb/historydb_test.go +++ b/db/historydb/historydb_test.go @@ -109,7 +109,7 @@ func TestBatches(t *testing.T) { // Test GetLastL1TxsNum fetchedLastL1TxsNum, err = historyDB.GetLastL1TxsNum() assert.NoError(t, err) - assert.Equal(t, batches[nBatches-1].ForgeL1TxsNum, *fetchedLastL1TxsNum) + assert.Equal(t, *batches[nBatches-1].ForgeL1TxsNum, *fetchedLastL1TxsNum) } func TestBids(t *testing.T) { diff --git a/db/historydb/views.go b/db/historydb/views.go index 707de85..9a0b643 100644 --- a/db/historydb/views.go +++ b/db/historydb/views.go @@ -17,7 +17,7 @@ type HistoryTx struct { TxID common.TxID `meddler:"id"` Type common.TxType `meddler:"type"` Position int `meddler:"position"` - FromIdx common.Idx `meddler:"from_idx"` + FromIdx *common.Idx `meddler:"from_idx"` ToIdx common.Idx `meddler:"to_idx"` Amount *big.Int `meddler:"amount,bigint"` AmountFloat float64 `meddler:"amount_f"` @@ -25,9 +25,9 @@ type HistoryTx struct { BatchNum *common.BatchNum `meddler:"batch_num"` // batchNum in which this tx was forged. If the tx is L2, this must be != 0 EthBlockNum int64 `meddler:"eth_block_num"` // Ethereum Block Number in which this L1Tx was added to the queue // L1 - ToForgeL1TxsNum int64 `meddler:"to_forge_l1_txs_num"` // toForgeL1TxsNum in which the tx was forged / will be forged - UserOrigin bool `meddler:"user_origin"` // true if the tx was originated by a user, false if it was aoriginated by a coordinator. Note that this differ from the spec for implementation simplification purpposes - FromEthAddr ethCommon.Address `meddler:"from_eth_addr"` + ToForgeL1TxsNum *int64 `meddler:"to_forge_l1_txs_num"` // toForgeL1TxsNum in which the tx was forged / will be forged + UserOrigin *bool `meddler:"user_origin"` // true if the tx was originated by a user, false if it was aoriginated by a coordinator. Note that this differ from the spec for implementation simplification purpposes + FromEthAddr *ethCommon.Address `meddler:"from_eth_addr"` FromBJJ *babyjub.PublicKey `meddler:"from_bjj"` LoadAmount *big.Int `meddler:"load_amount,bigintnull"` LoadAmountFloat *float64 `meddler:"load_amount_f"` diff --git a/db/l2db/l2db.go b/db/l2db/l2db.go index 65a08cd..32d8ba3 100644 --- a/db/l2db/l2db.go +++ b/db/l2db/l2db.go @@ -1,6 +1,7 @@ package l2db import ( + "errors" "math/big" "time" @@ -49,7 +50,10 @@ func (l2db *L2DB) AddAccountCreationAuth(auth *common.AccountCreationAuth) error } // GetAccountCreationAuth returns an account creation authorization into the DB -func (l2db *L2DB) GetAccountCreationAuth(addr ethCommon.Address) (*common.AccountCreationAuth, error) { +func (l2db *L2DB) GetAccountCreationAuth(addr *ethCommon.Address) (*common.AccountCreationAuth, error) { + if addr == nil { + return nil, errors.New("addr cannot be nil") + } auth := new(common.AccountCreationAuth) return auth, meddler.QueryRow( l2db.db, auth, @@ -64,8 +68,8 @@ func (l2db *L2DB) AddTxTest(tx *common.PoolL2Tx) error { type withouUSD struct { TxID common.TxID `meddler:"tx_id"` FromIdx common.Idx `meddler:"from_idx"` - ToIdx common.Idx `meddler:"to_idx"` - ToEthAddr ethCommon.Address `meddler:"to_eth_addr"` + ToIdx *common.Idx `meddler:"to_idx"` + ToEthAddr *ethCommon.Address `meddler:"to_eth_addr"` ToBJJ *babyjub.PublicKey `meddler:"to_bjj"` TokenID common.TokenID `meddler:"token_id"` Amount *big.Int `meddler:"amount,bigint"` @@ -75,15 +79,15 @@ func (l2db *L2DB) AddTxTest(tx *common.PoolL2Tx) error { State common.PoolL2TxState `meddler:"state"` Signature *babyjub.Signature `meddler:"signature"` Timestamp time.Time `meddler:"timestamp,utctime"` - BatchNum common.BatchNum `meddler:"batch_num,zeroisnull"` - RqFromIdx common.Idx `meddler:"rq_from_idx,zeroisnull"` - RqToIdx common.Idx `meddler:"rq_to_idx,zeroisnull"` - RqToEthAddr ethCommon.Address `meddler:"rq_to_eth_addr"` + BatchNum *common.BatchNum `meddler:"batch_num"` + RqFromIdx *common.Idx `meddler:"rq_from_idx"` + RqToIdx *common.Idx `meddler:"rq_to_idx"` + RqToEthAddr *ethCommon.Address `meddler:"rq_to_eth_addr"` RqToBJJ *babyjub.PublicKey `meddler:"rq_to_bjj"` - RqTokenID common.TokenID `meddler:"rq_token_id,zeroisnull"` + RqTokenID *common.TokenID `meddler:"rq_token_id"` RqAmount *big.Int `meddler:"rq_amount,bigintnull"` - RqFee common.FeeSelector `meddler:"rq_fee,zeroisnull"` - RqNonce uint64 `meddler:"rq_nonce,zeroisnull"` + RqFee *common.FeeSelector `meddler:"rq_fee"` + RqNonce *uint64 `meddler:"rq_nonce"` Type common.TxType `meddler:"tx_type"` } return meddler.Insert(l2db.db, "tx_pool", &withouUSD{ @@ -115,8 +119,8 @@ func (l2db *L2DB) AddTxTest(tx *common.PoolL2Tx) error { // selectPoolTx select part of queries to get common.PoolL2Tx const selectPoolTx = `SELECT tx_pool.*, token.usd * tx_pool.amount_f AS value_usd, -fee_percentage(tx_pool.fee::NUMERIC) * token.usd * tx_pool.amount_f AS fee_usd, token.usd_update, -token.symbol AS token_symbol FROM tx_pool INNER JOIN token ON tx_pool.token_id = token.token_id ` +fee_percentage(tx_pool.fee::NUMERIC) * token.usd * tx_pool.amount_f AS fee_usd, token.usd_update +FROM tx_pool INNER JOIN token ON tx_pool.token_id = token.token_id ` // GetTx return the specified Tx func (l2db *L2DB) GetTx(txID common.TxID) (*common.PoolL2Tx, error) { diff --git a/db/l2db/l2db_test.go b/db/l2db/l2db_test.go index b1a342a..48e3426 100644 --- a/db/l2db/l2db_test.go +++ b/db/l2db/l2db_test.go @@ -141,7 +141,7 @@ func TestStartForging(t *testing.T) { fetchedTx, err := l2DB.GetTx(id) assert.NoError(t, err) assert.Equal(t, common.PoolL2TxStateForging, fetchedTx.State) - assert.Equal(t, fakeBatchNum, fetchedTx.BatchNum) + assert.Equal(t, fakeBatchNum, *fetchedTx.BatchNum) } } @@ -170,7 +170,7 @@ func TestDoneForging(t *testing.T) { fetchedTx, err := l2DB.GetTx(id) assert.NoError(t, err) assert.Equal(t, common.PoolL2TxStateForged, fetchedTx.State) - assert.Equal(t, fakeBatchNum, fetchedTx.BatchNum) + assert.Equal(t, fakeBatchNum, *fetchedTx.BatchNum) } } @@ -199,7 +199,7 @@ func TestInvalidate(t *testing.T) { fetchedTx, err := l2DB.GetTx(id) assert.NoError(t, err) assert.Equal(t, common.PoolL2TxStateInvalid, fetchedTx.State) - assert.Equal(t, fakeBatchNum, fetchedTx.BatchNum) + assert.Equal(t, fakeBatchNum, *fetchedTx.BatchNum) } } @@ -242,7 +242,7 @@ func TestCheckNonces(t *testing.T) { fetchedTx, err := l2DB.GetTx(id) assert.NoError(t, err) assert.Equal(t, common.PoolL2TxStateInvalid, fetchedTx.State) - assert.Equal(t, fakeBatchNum, fetchedTx.BatchNum) + assert.Equal(t, fakeBatchNum, *fetchedTx.BatchNum) } } @@ -257,11 +257,12 @@ func TestReorg(t *testing.T) { reorgedTxIDs := []common.TxID{} nonReorgedTxIDs := []common.TxID{} for i := 0; i < len(txs); i++ { + txs[i].BatchNum = new(common.BatchNum) if txs[i].State == common.PoolL2TxStateForged || txs[i].State == common.PoolL2TxStateInvalid { - txs[i].BatchNum = reorgBatch + *txs[i].BatchNum = reorgBatch reorgedTxIDs = append(reorgedTxIDs, txs[i].TxID) } else { - txs[i].BatchNum = lastValidBatch + *txs[i].BatchNum = lastValidBatch nonReorgedTxIDs = append(nonReorgedTxIDs, txs[i].TxID) } err := l2DB.AddTxTest(txs[i]) @@ -269,17 +270,16 @@ func TestReorg(t *testing.T) { } err := l2DB.Reorg(lastValidBatch) assert.NoError(t, err) - var nullBatchNum common.BatchNum for _, id := range reorgedTxIDs { tx, err := l2DB.GetTx(id) assert.NoError(t, err) - assert.Equal(t, nullBatchNum, tx.BatchNum) + assert.Nil(t, tx.BatchNum) assert.Equal(t, common.PoolL2TxStatePending, tx.State) } for _, id := range nonReorgedTxIDs { tx, err := l2DB.GetTx(id) assert.NoError(t, err) - assert.Equal(t, lastValidBatch, tx.BatchNum) + assert.Equal(t, lastValidBatch, *tx.BatchNum) } } @@ -294,11 +294,12 @@ func TestPurge(t *testing.T) { safeBatchNum := toDeleteBatchNum + l2DB.safetyPeriod + 1 // Add txs to the DB for i := 0; i < int(l2DB.maxTxs); i++ { + txs[i].BatchNum = new(common.BatchNum) if i%1 == 0 { // keep tx - txs[i].BatchNum = safeBatchNum + *txs[i].BatchNum = safeBatchNum keepedIDs = append(keepedIDs, txs[i].TxID) } else if i%2 == 0 { // delete after safety period - txs[i].BatchNum = toDeleteBatchNum + *txs[i].BatchNum = toDeleteBatchNum if i%3 == 0 { txs[i].State = common.PoolL2TxStateForged } else { @@ -343,7 +344,7 @@ func TestAuth(t *testing.T) { err := l2DB.AddAccountCreationAuth(auths[i]) assert.NoError(t, err) // Fetch from DB - auth, err := l2DB.GetAccountCreationAuth(auths[i].EthAddr) + auth, err := l2DB.GetAccountCreationAuth(&auths[i].EthAddr) assert.NoError(t, err) // Check fetched vs generated assert.Equal(t, auths[i].EthAddr, auth.EthAddr) diff --git a/db/migrations/0001.sql b/db/migrations/0001.sql index 98ec631..5d020f1 100644 --- a/db/migrations/0001.sql +++ b/db/migrations/0001.sql @@ -17,14 +17,15 @@ CREATE TABLE coordinator ( CREATE TABLE batch ( batch_num BIGINT PRIMARY KEY, - eth_block_num BIGINT REFERENCES block (eth_block_num) ON DELETE CASCADE, + eth_block_num BIGINT NOT NULL REFERENCES block (eth_block_num) ON DELETE CASCADE, forger_addr BYTEA NOT NULL, -- fake foreign key for coordinator fees_collected BYTEA NOT NULL, state_root BYTEA NOT NULL, num_accounts BIGINT NOT NULL, exit_root BYTEA NOT NULL, forge_l1_txs_num BIGINT, - slot_num BIGINT NOT NULL + slot_num BIGINT NOT NULL, + total_fees_usd NUMERIC ); CREATE TABLE exit_tree ( @@ -80,7 +81,7 @@ CREATE TABLE tx ( id BYTEA PRIMARY KEY, type VARCHAR(40) NOT NULL, position INT NOT NULL, - from_idx BIGINT NOT NULL, + from_idx BIGINT, to_idx BIGINT NOT NULL, amount BYTEA NOT NULL, amount_f NUMERIC NOT NULL, @@ -470,9 +471,9 @@ CREATE TABLE consensus_vars ( CREATE TABLE tx_pool ( tx_id BYTEA PRIMARY KEY, from_idx BIGINT NOT NULL, - to_idx BIGINT NOT NULL, - to_eth_addr BYTEA NOT NULL, - to_bjj BYTEA NOT NULL, + to_idx BIGINT, + to_eth_addr BYTEA, + to_bjj BYTEA, token_id INT NOT NULL REFERENCES token (token_id) ON DELETE CASCADE, amount BYTEA NOT NULL, amount_f NUMERIC NOT NULL, diff --git a/db/statedb/statedb.go b/db/statedb/statedb.go index 084ca43..2913d2e 100644 --- a/db/statedb/statedb.go +++ b/db/statedb/statedb.go @@ -247,13 +247,19 @@ func (s *StateDB) Reset(batchNum common.BatchNum) error { } // GetAccount returns the account for the given Idx -func (s *StateDB) GetAccount(idx common.Idx) (*common.Account, error) { +func (s *StateDB) GetAccount(idx *common.Idx) (*common.Account, error) { + if idx == nil { + return nil, errors.New("idx cannot be nil") + } return getAccountInTreeDB(s.db, idx) } // getAccountInTreeDB is abstracted from StateDB to be used from StateDB and // from ExitTree. GetAccount returns the account for the given Idx -func getAccountInTreeDB(sto db.Storage, idx common.Idx) (*common.Account, error) { +func getAccountInTreeDB(sto db.Storage, idx *common.Idx) (*common.Account, error) { + if idx == nil { + return nil, errors.New("idx cannot be nil") + } idxBytes, err := idx.Bytes() if err != nil { return nil, err @@ -275,12 +281,12 @@ func getAccountInTreeDB(sto db.Storage, idx common.Idx) (*common.Account, error) // StateDB.mt==nil, MerkleTree is not affected, otherwise updates the // MerkleTree, returning a CircomProcessorProof. func (s *StateDB) CreateAccount(idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) { - cpp, err := createAccountInTreeDB(s.db, s.mt, idx, account) + cpp, err := createAccountInTreeDB(s.db, s.mt, &idx, account) if err != nil { return cpp, err } // store idx by EthAddr & BJJ - err = s.setIdxByEthAddrBJJ(idx, account.EthAddr, account.PublicKey) + err = s.setIdxByEthAddrBJJ(idx, &account.EthAddr, account.PublicKey) return cpp, err } @@ -288,7 +294,10 @@ func (s *StateDB) CreateAccount(idx common.Idx, account *common.Account) (*merkl // from ExitTree. Creates a new Account in the StateDB for the given Idx. If // StateDB.mt==nil, MerkleTree is not affected, otherwise updates the // MerkleTree, returning a CircomProcessorProof. -func createAccountInTreeDB(sto db.Storage, mt *merkletree.MerkleTree, idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) { +func createAccountInTreeDB(sto db.Storage, mt *merkletree.MerkleTree, idx *common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) { + if idx == nil { + return nil, errors.New("idx cannot be nil") + } // store at the DB the key: v, and value: leaf.Bytes() v, err := account.HashValue() if err != nil { @@ -337,7 +346,10 @@ func createAccountInTreeDB(sto db.Storage, mt *merkletree.MerkleTree, idx common // UpdateAccount updates the Account in the StateDB for the given Idx. If // StateDB.mt==nil, MerkleTree is not affected, otherwise updates the // MerkleTree, returning a CircomProcessorProof. -func (s *StateDB) UpdateAccount(idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) { +func (s *StateDB) UpdateAccount(idx *common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) { + if idx == nil { + return nil, errors.New("idx cannot be nil") + } return updateAccountInTreeDB(s.db, s.mt, idx, account) } @@ -345,7 +357,10 @@ func (s *StateDB) UpdateAccount(idx common.Idx, account *common.Account) (*merkl // from ExitTree. Updates the Account in the StateDB for the given Idx. If // StateDB.mt==nil, MerkleTree is not affected, otherwise updates the // MerkleTree, returning a CircomProcessorProof. -func updateAccountInTreeDB(sto db.Storage, mt *merkletree.MerkleTree, idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) { +func updateAccountInTreeDB(sto db.Storage, mt *merkletree.MerkleTree, idx *common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) { + if idx == nil { + return nil, errors.New("idx cannot be nil") + } // store at the DB the key: v, and value: account.Bytes() v, err := account.HashValue() if err != nil { diff --git a/db/statedb/statedb_test.go b/db/statedb/statedb_test.go index 1950af6..85b2dd5 100644 --- a/db/statedb/statedb_test.go +++ b/db/statedb/statedb_test.go @@ -129,7 +129,8 @@ func TestStateDBWithoutMT(t *testing.T) { } // get non-existing account, expecting an error - _, err = sdb.GetAccount(common.Idx(1)) + unexistingAccount := common.Idx(1) + _, err = sdb.GetAccount(&unexistingAccount) assert.NotNil(t, err) assert.Equal(t, db.ErrNotFound, err) @@ -140,13 +141,15 @@ func TestStateDBWithoutMT(t *testing.T) { } for i := 0; i < len(accounts); i++ { - accGetted, err := sdb.GetAccount(common.Idx(i)) + existingAccount := common.Idx(i) + accGetted, err := sdb.GetAccount(&existingAccount) assert.Nil(t, err) assert.Equal(t, accounts[i], accGetted) } // try already existing idx and get error - _, err = sdb.GetAccount(common.Idx(1)) // check that exist + existingAccount := common.Idx(1) + _, err = sdb.GetAccount(&existingAccount) // check that exist assert.Nil(t, err) _, err = sdb.CreateAccount(common.Idx(1), accounts[1]) // check that can not be created twice assert.NotNil(t, err) @@ -155,7 +158,8 @@ func TestStateDBWithoutMT(t *testing.T) { // update accounts for i := 0; i < len(accounts); i++ { accounts[i].Nonce = accounts[i].Nonce + 1 - _, err = sdb.UpdateAccount(common.Idx(i), accounts[i]) + existingAccount = common.Idx(i) + _, err = sdb.UpdateAccount(&existingAccount, accounts[i]) assert.Nil(t, err) } @@ -178,7 +182,8 @@ func TestStateDBWithMT(t *testing.T) { } // get non-existing account, expecting an error - _, err = sdb.GetAccount(common.Idx(1)) + accountIdx := common.Idx(1) + _, err = sdb.GetAccount(&accountIdx) assert.NotNil(t, err) assert.Equal(t, db.ErrNotFound, err) @@ -189,13 +194,15 @@ func TestStateDBWithMT(t *testing.T) { } for i := 0; i < len(accounts); i++ { - accGetted, err := sdb.GetAccount(common.Idx(i)) + accountIdx = common.Idx(i) + accGetted, err := sdb.GetAccount(&accountIdx) assert.Nil(t, err) assert.Equal(t, accounts[i], accGetted) } // try already existing idx and get error - _, err = sdb.GetAccount(common.Idx(1)) // check that exist + accountIdx = 1 + _, err = sdb.GetAccount(&accountIdx) // check that exist assert.Nil(t, err) _, err = sdb.CreateAccount(common.Idx(1), accounts[1]) // check that can not be created twice assert.NotNil(t, err) @@ -207,10 +214,12 @@ func TestStateDBWithMT(t *testing.T) { // update accounts for i := 0; i < len(accounts); i++ { accounts[i].Nonce = accounts[i].Nonce + 1 - _, err = sdb.UpdateAccount(common.Idx(i), accounts[i]) + accountIdx = common.Idx(i) + _, err = sdb.UpdateAccount(&accountIdx, accounts[i]) assert.Nil(t, err) } - a, err := sdb.GetAccount(common.Idx(1)) // check that account value has been updated + accountIdx = 1 + a, err := sdb.GetAccount(&accountIdx) // check that account value has been updated assert.Nil(t, err) assert.Equal(t, accounts[1].Nonce, a.Nonce) } diff --git a/db/statedb/txprocessors.go b/db/statedb/txprocessors.go index 76c951a..8fd56a8 100644 --- a/db/statedb/txprocessors.go +++ b/db/statedb/txprocessors.go @@ -227,7 +227,11 @@ func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) if s.zki != nil { // Txs // s.zki.TxCompressedData[s.i] = tx.TxCompressedData() // uncomment once L1Tx.TxCompressedData is ready - s.zki.FromIdx[s.i] = tx.FromIdx.BigInt() + if tx.FromIdx != nil { + s.zki.FromIdx[s.i] = tx.FromIdx.BigInt() + } else { + s.zki.FromIdx[s.i] = big.NewInt(0) + } s.zki.ToIdx[s.i] = tx.ToIdx.BigInt() s.zki.OnChain[s.i] = big.NewInt(1) @@ -292,7 +296,7 @@ func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) if err != nil { return nil, nil, false, err } - return &tx.FromIdx, exitAccount, newExit, nil + return tx.FromIdx, exitAccount, newExit, nil default: } @@ -307,7 +311,7 @@ func (s *StateDB) processL2Tx(exitTree *merkletree.MerkleTree, tx *common.PoolL2 var err error var auxToIdx common.Idx // if tx.ToIdx==0, get toIdx by ToEthAddr or ToBJJ - if tx.ToIdx == common.Idx(0) { + if tx.ToIdx == nil || *tx.ToIdx == common.Idx(0) { auxToIdx, err = s.GetIdxByEthAddrBJJ(tx.ToEthAddr, tx.ToBJJ) if err != nil { log.Error(err) @@ -324,7 +328,7 @@ func (s *StateDB) processL2Tx(exitTree *merkletree.MerkleTree, tx *common.PoolL2 s.zki.ToIdx[s.i] = tx.ToIdx.BigInt() // fill AuxToIdx if needed - if tx.ToIdx == common.Idx(0) { + if tx.ToIdx == nil { // use toIdx that can have been filled by tx.ToIdx or // if tx.Idx==0 (this case), toIdx is filled by the Idx // from db by ToEthAddr&ToBJJ @@ -332,7 +336,12 @@ func (s *StateDB) processL2Tx(exitTree *merkletree.MerkleTree, tx *common.PoolL2 } s.zki.ToBJJAy[s.i] = tx.ToBJJ.Y - s.zki.ToEthAddr[s.i] = common.EthAddrToBigInt(tx.ToEthAddr) + if tx.ToEthAddr != nil { + s.zki.ToEthAddr[s.i] = common.EthAddrToBigInt(*tx.ToEthAddr) + } else { + // Not sure if this should throw an error + s.zki.ToEthAddr[s.i] = big.NewInt(0) + } s.zki.OnChain[s.i] = big.NewInt(0) s.zki.NewAccount[s.i] = big.NewInt(0) @@ -362,13 +371,21 @@ func (s *StateDB) processL2Tx(exitTree *merkletree.MerkleTree, tx *common.PoolL2 case common.TxTypeTransfer: // go to the MT account of sender and receiver, and update // balance & nonce - err = s.applyTransfer(tx.Tx(), auxToIdx) + tmpTx, err := tx.Tx() + if err != nil { + return nil, nil, false, err + } + err = s.applyTransfer(tmpTx, auxToIdx) if err != nil { return nil, nil, false, err } case common.TxTypeExit: // execute exit flow - exitAccount, newExit, err := s.applyExit(exitTree, tx.Tx()) + tmpTx, err := tx.Tx() + if err != nil { + return nil, nil, false, err + } + exitAccount, newExit, err := s.applyExit(exitTree, tmpTx) if err != nil { return nil, nil, false, err } @@ -428,7 +445,7 @@ func (s *StateDB) applyDeposit(tx *common.L1Tx, transfer bool) error { // in case that the tx is a L1Tx>DepositTransfer var accReceiver *common.Account if transfer { - accReceiver, err = s.GetAccount(tx.ToIdx) + accReceiver, err = s.GetAccount(&tx.ToIdx) if err != nil { return err } @@ -458,7 +475,7 @@ func (s *StateDB) applyDeposit(tx *common.L1Tx, transfer bool) error { // this is done after updating Sender Account (depositer) if transfer { // update receiver account in localStateDB - p, err := s.UpdateAccount(tx.ToIdx, accReceiver) + p, err := s.UpdateAccount(&tx.ToIdx, accReceiver) if err != nil { return err } @@ -487,14 +504,17 @@ func (s *StateDB) applyDeposit(tx *common.L1Tx, transfer bool) error { // the real ToIdx is found trhrough the ToEthAddr or ToBJJ. func (s *StateDB) applyTransfer(tx *common.Tx, auxToIdx common.Idx) error { if auxToIdx == 0 { - auxToIdx = tx.ToIdx + if tx.ToIdx == nil { + return errors.New("tx.ToIdx cannot be nil if auxToIdx is 0") + } + auxToIdx = *tx.ToIdx } // get sender and receiver accounts from localStateDB accSender, err := s.GetAccount(tx.FromIdx) if err != nil { return err } - accReceiver, err := s.GetAccount(auxToIdx) + accReceiver, err := s.GetAccount(&auxToIdx) if err != nil { return err } @@ -525,7 +545,7 @@ func (s *StateDB) applyTransfer(tx *common.Tx, auxToIdx common.Idx) error { } // update receiver account in localStateDB - pReceiver, err := s.UpdateAccount(auxToIdx, accReceiver) + pReceiver, err := s.UpdateAccount(&auxToIdx, accReceiver) if err != nil { return err } @@ -555,7 +575,7 @@ func (s *StateDB) applyCreateAccountDepositTransfer(tx *common.L1Tx) error { EthAddr: tx.FromEthAddr, } accSender.Balance = new(big.Int).Add(accSender.Balance, tx.LoadAmount) - accReceiver, err := s.GetAccount(tx.ToIdx) + accReceiver, err := s.GetAccount(&tx.ToIdx) if err != nil { return err } @@ -587,7 +607,7 @@ func (s *StateDB) applyCreateAccountDepositTransfer(tx *common.L1Tx) error { } // update receiver account in localStateDB - p, err = s.UpdateAccount(tx.ToIdx, accReceiver) + p, err = s.UpdateAccount(&tx.ToIdx, accReceiver) if err != nil { return err } diff --git a/db/statedb/txprocessors_test.go b/db/statedb/txprocessors_test.go index 05407b6..eb22244 100644 --- a/db/statedb/txprocessors_test.go +++ b/db/statedb/txprocessors_test.go @@ -40,7 +40,8 @@ func TestProcessTxs(t *testing.T) { require.Nil(t, err) } - acc, err := sdb.GetAccount(common.Idx(256)) + accountIdx := common.Idx(256) + acc, err := sdb.GetAccount(&accountIdx) assert.Nil(t, err) assert.Equal(t, "23", acc.Balance.String()) } @@ -79,7 +80,8 @@ func TestProcessTxsSynchronizer(t *testing.T) { // Nonce & TokenID =0, after ProcessTxs call has the expected value assert.Equal(t, 0, len(exitInfos)) - acc, err := sdb.GetAccount(common.Idx(256)) + accountIdx := common.Idx(256) + acc, err := sdb.GetAccount(&accountIdx) assert.Nil(t, err) assert.Equal(t, "28", acc.Balance.String()) @@ -88,7 +90,7 @@ func TestProcessTxsSynchronizer(t *testing.T) { _, exitInfos, err = sdb.ProcessTxs(l1Txs[1], coordinatorL1Txs[1], poolL2Txs[1]) require.Nil(t, err) assert.Equal(t, 5, len(exitInfos)) - acc, err = sdb.GetAccount(common.Idx(256)) + acc, err = sdb.GetAccount(&accountIdx) require.Nil(t, err) assert.Equal(t, "48", acc.Balance.String()) @@ -97,7 +99,7 @@ func TestProcessTxsSynchronizer(t *testing.T) { _, exitInfos, err = sdb.ProcessTxs(l1Txs[2], coordinatorL1Txs[2], poolL2Txs[2]) require.Nil(t, err) assert.Equal(t, 1, len(exitInfos)) - acc, err = sdb.GetAccount(common.Idx(256)) + acc, err = sdb.GetAccount(&accountIdx) assert.Nil(t, err) assert.Equal(t, "23", acc.Balance.String()) } @@ -130,7 +132,8 @@ func TestProcessTxsBatchBuilder(t *testing.T) { _, exitInfos, err := sdb.ProcessTxs(l1Txs[0], coordinatorL1Txs[0], poolL2Txs[0]) require.Nil(t, err) assert.Equal(t, 0, len(exitInfos)) - acc, err := sdb.GetAccount(common.Idx(256)) + accountIdx := common.Idx(256) + acc, err := sdb.GetAccount(&accountIdx) assert.Nil(t, err) assert.Equal(t, "28", acc.Balance.String()) @@ -139,7 +142,7 @@ func TestProcessTxsBatchBuilder(t *testing.T) { _, exitInfos, err = sdb.ProcessTxs(l1Txs[1], coordinatorL1Txs[1], poolL2Txs[1]) require.Nil(t, err) assert.Equal(t, 5, len(exitInfos)) - acc, err = sdb.GetAccount(common.Idx(256)) + acc, err = sdb.GetAccount(&accountIdx) require.Nil(t, err) assert.Equal(t, "48", acc.Balance.String()) @@ -148,7 +151,7 @@ func TestProcessTxsBatchBuilder(t *testing.T) { _, exitInfos, err = sdb.ProcessTxs(l1Txs[2], coordinatorL1Txs[2], poolL2Txs[2]) require.Nil(t, err) assert.Equal(t, 1, len(exitInfos)) - acc, err = sdb.GetAccount(common.Idx(256)) + acc, err = sdb.GetAccount(&accountIdx) assert.Nil(t, err) assert.Equal(t, "23", acc.Balance.String()) } diff --git a/db/statedb/utils.go b/db/statedb/utils.go index 0db064f..01bb962 100644 --- a/db/statedb/utils.go +++ b/db/statedb/utils.go @@ -2,6 +2,7 @@ package statedb import ( "bytes" + "errors" "math/big" ethCommon "github.com/ethereum/go-ethereum/common" @@ -11,7 +12,7 @@ import ( "github.com/iden3/go-merkletree" ) -func concatEthAddrBJJ(addr ethCommon.Address, pk *babyjub.PublicKey) []byte { +func concatEthAddrBJJ(addr *ethCommon.Address, pk *babyjub.PublicKey) []byte { pkComp := pk.Compress() var b []byte b = append(b, addr.Bytes()...) @@ -24,7 +25,7 @@ func concatEthAddrBJJ(addr ethCommon.Address, pk *babyjub.PublicKey) []byte { // - key: EthAddr & BabyJubJub PublicKey Compressed, value: idx // If Idx already exist for the given EthAddr & BJJ, the remaining Idx will be // always the smallest one. -func (s *StateDB) setIdxByEthAddrBJJ(idx common.Idx, addr ethCommon.Address, pk *babyjub.PublicKey) error { +func (s *StateDB) setIdxByEthAddrBJJ(idx common.Idx, addr *ethCommon.Address, pk *babyjub.PublicKey) error { oldIdx, err := s.GetIdxByEthAddrBJJ(addr, pk) if err == nil { // EthAddr & BJJ already have an Idx @@ -70,7 +71,10 @@ func (s *StateDB) setIdxByEthAddrBJJ(idx common.Idx, addr ethCommon.Address, pk // GetIdxByEthAddr returns the smallest Idx in the StateDB for the given // Ethereum Address. Will return common.Idx(0) and error in case that Idx is // not found in the StateDB. -func (s *StateDB) GetIdxByEthAddr(addr ethCommon.Address) (common.Idx, error) { +func (s *StateDB) GetIdxByEthAddr(addr *ethCommon.Address) (common.Idx, error) { + if addr == nil { + return 0, errors.New("addr cannot be nil") + } b, err := s.db.Get(addr.Bytes()) if err != nil { return common.Idx(0), ErrToIdxNotFound @@ -87,7 +91,10 @@ func (s *StateDB) GetIdxByEthAddr(addr ethCommon.Address) (common.Idx, error) { // address, it's ignored in the query. If `pk` is nil, it's ignored in the // query. Will return common.Idx(0) and error in case that Idx is not found in // the StateDB. -func (s *StateDB) GetIdxByEthAddrBJJ(addr ethCommon.Address, pk *babyjub.PublicKey) (common.Idx, error) { +func (s *StateDB) GetIdxByEthAddrBJJ(addr *ethCommon.Address, pk *babyjub.PublicKey) (common.Idx, error) { + if addr == nil { + return 0, errors.New("addr cannot be nil") + } if !bytes.Equal(addr.Bytes(), common.EmptyAddr.Bytes()) && pk == nil { // case ToEthAddr!=0 && ToBJJ=0 return s.GetIdxByEthAddr(addr) diff --git a/db/statedb/utils_test.go b/db/statedb/utils_test.go index 821cea3..2e0df30 100644 --- a/db/statedb/utils_test.go +++ b/db/statedb/utils_test.go @@ -32,47 +32,47 @@ func TestGetIdx(t *testing.T) { idx3 := common.Idx(1233) // store the keys for idx by Addr & BJJ - err = sdb.setIdxByEthAddrBJJ(idx, addr, pk) + err = sdb.setIdxByEthAddrBJJ(idx, &addr, pk) require.Nil(t, err) - idxR, err := sdb.GetIdxByEthAddrBJJ(addr, pk) + idxR, err := sdb.GetIdxByEthAddrBJJ(&addr, pk) assert.Nil(t, err) assert.Equal(t, idx, idxR) // expect error when getting only by EthAddr, as value does not exist // in the db for only EthAddr - _, err = sdb.GetIdxByEthAddr(addr) + _, err = sdb.GetIdxByEthAddr(&addr) assert.Nil(t, err) - _, err = sdb.GetIdxByEthAddr(addr2) + _, err = sdb.GetIdxByEthAddr(&addr2) assert.NotNil(t, err) // expect to fail - idxR, err = sdb.GetIdxByEthAddrBJJ(addr2, pk) + idxR, err = sdb.GetIdxByEthAddrBJJ(&addr2, pk) assert.NotNil(t, err) assert.Equal(t, common.Idx(0), idxR) - idxR, err = sdb.GetIdxByEthAddrBJJ(addr, pk2) + idxR, err = sdb.GetIdxByEthAddrBJJ(&addr, pk2) assert.NotNil(t, err) assert.Equal(t, common.Idx(0), idxR) // try to store bigger idx, will not affect as already exist a smaller // Idx for that Addr & BJJ - err = sdb.setIdxByEthAddrBJJ(idx2, addr, pk) + err = sdb.setIdxByEthAddrBJJ(idx2, &addr, pk) assert.Nil(t, err) // store smaller idx - err = sdb.setIdxByEthAddrBJJ(idx3, addr, pk) + err = sdb.setIdxByEthAddrBJJ(idx3, &addr, pk) assert.Nil(t, err) - idxR, err = sdb.GetIdxByEthAddrBJJ(addr, pk) + idxR, err = sdb.GetIdxByEthAddrBJJ(&addr, pk) assert.Nil(t, err) assert.Equal(t, idx3, idxR) // by EthAddr should work - idxR, err = sdb.GetIdxByEthAddr(addr) + idxR, err = sdb.GetIdxByEthAddr(&addr) assert.Nil(t, err) assert.Equal(t, idx3, idxR) // expect error when trying to get Idx by addr2 & pk2 - idxR, err = sdb.GetIdxByEthAddrBJJ(addr2, pk2) + idxR, err = sdb.GetIdxByEthAddrBJJ(&addr2, pk2) assert.NotNil(t, err) assert.Equal(t, ErrToIdxNotFound, err) assert.Equal(t, common.Idx(0), idxR) diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index 66216b4..0969ada 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -340,13 +340,15 @@ func (s *Synchronizer) rollupSync(blockNum int64) (*rollupData, error) { } // TODO: Replace GetLastL1TxsNum by GetNextL1TxsNum - nextForgeL1TxsNum := int64(0) + var nextForgeL1TxsNum int64 nextForgeL1TxsNumPtr, err := s.historyDB.GetLastL1TxsNum() if err != nil { return nil, err } if nextForgeL1TxsNumPtr != nil { nextForgeL1TxsNum = *nextForgeL1TxsNumPtr + 1 + } else { + nextForgeL1TxsNum = 0 } // Get newLastIdx that will be used to complete the accounts @@ -371,11 +373,9 @@ func (s *Synchronizer) rollupSync(blockNum int64) (*rollupData, error) { if err != nil { return nil, err } - forgeL1TxsNum := int64(0) + forgeL1TxsNum := nextForgeL1TxsNum // Check if this is a L1Batch to get L1 Tx from it if forgeBatchArgs.L1Batch { - forgeL1TxsNum = nextForgeL1TxsNum - // Get L1 User Txs from History DB // TODO: Get L1TX from HistoryDB filtered by toforgeL1txNum & fromidx = 0 and // update batch number and add accounts to createdAccounts updating idx @@ -395,7 +395,7 @@ func (s *Synchronizer) rollupSync(blockNum int64) (*rollupData, error) { // Get L1 Coordinator Txs for _, l1CoordinatorTx := range forgeBatchArgs.L1CoordinatorTxs { l1CoordinatorTx.Position = position - l1CoordinatorTx.ToForgeL1TxsNum = nextForgeL1TxsNum + l1CoordinatorTx.ToForgeL1TxsNum = &forgeL1TxsNum l1CoordinatorTx.UserOrigin = false l1CoordinatorTx.EthBlockNum = blockNum bn := new(common.BatchNum) @@ -440,7 +440,10 @@ func (s *Synchronizer) rollupSync(blockNum int64) (*rollupData, error) { return nil, err } - l2Txs := common.PoolL2TxsToL2Txs(poolL2Txs) // TODO: This is a big uggly, find a better way + l2Txs, err := common.PoolL2TxsToL2Txs(poolL2Txs) // TODO: This is a big uggly, find a better way + if err != nil { + return nil, err + } batchData.l2Txs = append(batchData.l2Txs, l2Txs...) batchData.exitTree = exitInfo @@ -454,7 +457,7 @@ func (s *Synchronizer) rollupSync(blockNum int64) (*rollupData, error) { StateRoot: common.Hash(forgeBatchArgs.NewStRoot.Bytes()), NumAccounts: numAccounts, ExitRoot: common.Hash(forgeBatchArgs.NewExitRoot.Bytes()), - ForgeL1TxsNum: forgeL1TxsNum, + ForgeL1TxsNum: &forgeL1TxsNum, // SlotNum: TODO: Calculate once ethClient provides the info // calculate from blockNum + ethClient Constants } batchData.batch = batch @@ -573,7 +576,9 @@ func getL1UserTx(l1UserTxEvents []eth.RollupEventL1UserTx, blockNum int64) ([]*c for _, eL1UserTx := range l1UserTxEvents { // Fill aditional Tx fields - eL1UserTx.L1Tx.ToForgeL1TxsNum = eL1UserTx.ToForgeL1TxsNum + toForge := new(int64) + *toForge = eL1UserTx.ToForgeL1TxsNum + eL1UserTx.L1Tx.ToForgeL1TxsNum = toForge eL1UserTx.L1Tx.Position = eL1UserTx.Position eL1UserTx.L1Tx.UserOrigin = true eL1UserTx.L1Tx.EthBlockNum = blockNum diff --git a/test/ethclient.go b/test/ethclient.go index 128fc19..cb209b8 100644 --- a/test/ethclient.go +++ b/test/ethclient.go @@ -566,7 +566,7 @@ func (c *Client) CtlAddL1TxUser(l1Tx *common.L1Tx) { r.State.MapL1TxQueue[r.State.LastToForgeL1TxsNum] = eth.NewQueueStruct() queue = r.State.MapL1TxQueue[r.State.LastToForgeL1TxsNum] } - if int64(l1Tx.FromIdx) > r.State.CurrentIdx { + if l1Tx.FromIdx != nil && int64(*l1Tx.FromIdx) > r.State.CurrentIdx { panic("l1Tx.FromIdx > r.State.CurrentIdx") } if int(l1Tx.TokenID)+1 > len(r.State.TokenList) { diff --git a/test/ethclient_test.go b/test/ethclient_test.go index 9e7ddb3..71d20c3 100644 --- a/test/ethclient_test.go +++ b/test/ethclient_test.go @@ -140,7 +140,7 @@ func TestClientRollup(t *testing.T) { for i := 0; i < N; i++ { keys[i] = genKeys(int64(i)) l1UserTx := common.L1Tx{ - FromIdx: common.Idx(0), + FromIdx: nil, FromEthAddr: keys[i].Addr, FromBJJ: keys[i].BJJPublicKey, TokenID: common.TokenID(0), diff --git a/test/historydb.go b/test/historydb.go index 167667f..cbfb01c 100644 --- a/test/historydb.go +++ b/test/historydb.go @@ -74,7 +74,9 @@ func GenBatches(nBatches int, blocks []common.Block) []common.Batch { SlotNum: common.SlotNum(i), } if i%2 == 0 { - batch.ForgeL1TxsNum = int64(i) + toForge := new(int64) + *toForge = int64(i) + batch.ForgeL1TxsNum = toForge } batches = append(batches, batch) } @@ -155,7 +157,7 @@ func GenL1Txs( panic(err) } tx = *nTx - if batches[i%len(batches)].ForgeL1TxsNum != 0 { + if batches[i%len(batches)].ForgeL1TxsNum != nil { // Add already forged txs tx.BatchNum = &batches[i%len(batches)].BatchNum setFromToAndAppend(fromIdx, tx, i, nUserTxs, userAddr, accounts, &userTxs, &othersTxs) @@ -170,13 +172,13 @@ func GenL1Txs( } // GetNextToForgeNumAndBatch returns the next BatchNum and ForgeL1TxsNum to be added -func GetNextToForgeNumAndBatch(batches []common.Batch) (common.BatchNum, int64) { +func GetNextToForgeNumAndBatch(batches []common.Batch) (common.BatchNum, *int64) { batchNum := batches[len(batches)-1].BatchNum + 1 - var toForgeL1TxsNum int64 + toForgeL1TxsNum := new(int64) found := false for i := len(batches) - 1; i >= 0; i-- { - if batches[i].ForgeL1TxsNum != 0 { - toForgeL1TxsNum = batches[i].ForgeL1TxsNum + 1 + if batches[i].ForgeL1TxsNum != nil { + *toForgeL1TxsNum = *batches[i].ForgeL1TxsNum + 1 found = true break } @@ -218,7 +220,9 @@ func setFromToAndAppend( panic(err) } } - tx.FromIdx = from.Idx + fromIdx := new(common.Idx) + *fromIdx = from.Idx + tx.FromIdx = fromIdx tx.FromEthAddr = from.EthAddr tx.FromBJJ = from.PublicKey tx.ToIdx = to.Idx @@ -232,7 +236,9 @@ func setFromToAndAppend( if err != nil { panic(err) } - tx.FromIdx = from.Idx + fromIdx := new(common.Idx) + *fromIdx = from.Idx + tx.FromIdx = fromIdx tx.FromEthAddr = from.EthAddr tx.FromBJJ = from.PublicKey tx.ToIdx = to.Idx @@ -435,9 +441,6 @@ func randomTxType(seed int) common.TxType { case 0: return common.TxTypeExit //nolint:gomnd - case 1: - return common.TxTypeWithdrawn - //nolint:gomnd case 2: return common.TxTypeTransfer //nolint:gomnd diff --git a/test/l2db.go b/test/l2db.go index 84e43fd..4d028ed 100644 --- a/test/l2db.go +++ b/test/l2db.go @@ -53,10 +53,14 @@ func GenPoolTxs(n int, tokens []common.Token) []*common.PoolL2Tx { *usd = *token.USD * amountF *absFee = fee.Percentage() * *usd } + toIdx := new(common.Idx) + *toIdx = common.Idx(i + 1) + toEthAddr := new(ethCommon.Address) + *toEthAddr = ethCommon.BigToAddress(big.NewInt(int64(i))) tx := &common.PoolL2Tx{ FromIdx: common.Idx(i), - ToIdx: common.Idx(i + 1), - ToEthAddr: ethCommon.BigToAddress(big.NewInt(int64(i))), + ToIdx: toIdx, + ToEthAddr: toEthAddr, ToBJJ: privK.Public(), TokenID: token.TokenID, Amount: big.NewInt(int64(i)), @@ -67,7 +71,6 @@ func GenPoolTxs(n int, tokens []common.Token) []*common.PoolL2Tx { State: state, Signature: privK.SignPoseidon(big.NewInt(int64(i))), Timestamp: time.Now().UTC(), - TokenSymbol: token.Symbol, AbsoluteFee: absFee, AbsoluteFeeUpdate: token.USDUpdate, } @@ -77,17 +80,31 @@ func GenPoolTxs(n int, tokens []common.Token) []*common.PoolL2Tx { panic(err) } if i%2 == 0 { // Optional parameters: rq - tx.RqFromIdx = common.Idx(i) - tx.RqToIdx = common.Idx(i + 1) - tx.RqToEthAddr = ethCommon.BigToAddress(big.NewInt(int64(i))) + rqFromIdx := new(common.Idx) + *rqFromIdx = common.Idx(i) + tx.RqFromIdx = rqFromIdx + rqToIdx := new(common.Idx) + *rqToIdx = common.Idx(i + 1) + tx.RqToIdx = rqToIdx + rqToEthAddr := new(ethCommon.Address) + *rqToEthAddr = ethCommon.BigToAddress(big.NewInt(int64(i))) + tx.RqToEthAddr = rqToEthAddr tx.RqToBJJ = privK.Public() - tx.RqTokenID = common.TokenID(i) + rqTokenID := new(common.TokenID) + *rqTokenID = common.TokenID(i) + tx.RqTokenID = rqTokenID tx.RqAmount = big.NewInt(int64(i)) - tx.RqFee = common.FeeSelector(i) - tx.RqNonce = uint64(i) + rqFee := new(common.FeeSelector) + *rqFee = common.FeeSelector(i) + tx.RqFee = rqFee + rqNonce := new(uint64) + *rqNonce = uint64(i) + tx.RqNonce = rqNonce } if i%3 == 0 { // Optional parameters: things that get updated "a posteriori" - tx.BatchNum = 489 + batchNum := new(common.BatchNum) + *batchNum = 489 + tx.BatchNum = batchNum } txs = append(txs, tx) } diff --git a/test/txs.go b/test/txs.go index 443ed83..6fcf75a 100644 --- a/test/txs.go +++ b/test/txs.go @@ -96,11 +96,16 @@ func GenerateTestTxs(t *testing.T, instructions Instructions) ([][]*common.L1Tx, batchCoordinatorL1Txs = append(batchCoordinatorL1Txs, &tx) idx++ } - + toIdx := new(common.Idx) + *toIdx = accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx + toEthAddr := new(ethCommon.Address) + *toEthAddr = accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr + rqToEthAddr := new(ethCommon.Address) + *rqToEthAddr = accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr tx := common.PoolL2Tx{ FromIdx: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx, - ToIdx: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx, - ToEthAddr: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr, + ToIdx: toIdx, + ToEthAddr: toEthAddr, ToBJJ: accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(), TokenID: inst.TokenID, Amount: big.NewInt(int64(inst.Amount)), @@ -108,8 +113,8 @@ func GenerateTestTxs(t *testing.T, instructions Instructions) ([][]*common.L1Tx, Nonce: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce, State: common.PoolL2TxStatePending, Timestamp: time.Now(), - BatchNum: common.BatchNum(0), - RqToEthAddr: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr, + BatchNum: nil, + RqToEthAddr: rqToEthAddr, RqToBJJ: accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(), Type: common.TxTypeTransfer, } @@ -130,8 +135,10 @@ func GenerateTestTxs(t *testing.T, instructions Instructions) ([][]*common.L1Tx, batchPoolL2Txs = append(batchPoolL2Txs, &tx) case common.TxTypeExit, common.TxTypeForceExit: + fromIdx := new(common.Idx) + *fromIdx = accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx tx := common.L1Tx{ - FromIdx: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx, + FromIdx: fromIdx, ToIdx: common.Idx(1), // as is an Exit TokenID: inst.TokenID, Amount: big.NewInt(int64(inst.Amount)), diff --git a/test/txs_test.go b/test/txs_test.go index 57573b9..b75a3c7 100644 --- a/test/txs_test.go +++ b/test/txs_test.go @@ -50,7 +50,7 @@ func TestGenerateTestL2Txs(t *testing.T) { // l2txs assert.Equal(t, common.TxTypeTransfer, l2txs[0][0].Type) assert.Equal(t, common.Idx(256), l2txs[0][0].FromIdx) - assert.Equal(t, common.Idx(258), l2txs[0][0].ToIdx) + assert.Equal(t, common.Idx(258), *l2txs[0][0].ToIdx) assert.Equal(t, accounts["B1"].BJJ.Public().String(), l2txs[0][0].ToBJJ.String()) assert.Equal(t, accounts["B1"].Addr.Hex(), l2txs[0][0].ToEthAddr.Hex()) assert.Equal(t, common.Nonce(0), l2txs[0][0].Nonce) diff --git a/txselector/txselector.go b/txselector/txselector.go index 3e17fba..545b984 100644 --- a/txselector/txselector.go +++ b/txselector/txselector.go @@ -78,7 +78,7 @@ func (txsel *TxSelector) GetL2TxSelection(batchNum common.BatchNum) ([]*common.P // discard the txs that don't have an Account in the AccountDB var validTxs txs for _, tx := range l2TxsRaw { - _, err = txsel.localAccountsDB.GetAccount(tx.FromIdx) + _, err = txsel.localAccountsDB.GetAccount(&tx.FromIdx) if err == nil { // if FromIdx has an account into the AccountsDB validTxs = append(validTxs, tx) @@ -120,16 +120,7 @@ func (txsel *TxSelector) GetL1L2TxSelection(batchNum common.BatchNum, l1Txs []*c // in AccountCreationAuthDB, if so, tx is used and L1CoordinatorTx of // CreateAccountAndDeposit is created. for i := 0; i < len(l2TxsRaw); i++ { - if l2TxsRaw[i].ToIdx >= common.IdxUserThreshold { - _, err = txsel.localAccountsDB.GetAccount(l2TxsRaw[i].ToIdx) - if err != nil { - // tx not valid - log.Debugw("invalid L2Tx: ToIdx not found in StateDB", "ToIdx", l2TxsRaw[i].ToIdx) - continue - } - // Account found in the DB, include the l2Tx in the selection - validTxs = append(validTxs, l2TxsRaw[i]) - } else if l2TxsRaw[i].ToIdx == common.Idx(0) { + if l2TxsRaw[i].ToIdx == nil { if checkAlreadyPendingToCreate(l1CoordinatorTxs, l2TxsRaw[i].ToEthAddr, l2TxsRaw[i].ToBJJ) { // if L2Tx needs a new L1CoordinatorTx of CreateAccount type, // and a previous L2Tx in the current process already created @@ -215,10 +206,14 @@ func (txsel *TxSelector) GetL1L2TxSelection(batchNum common.BatchNum, l1Txs []*c // coordinator can create a new account without // L1Authorization, as ToEthAddr==0xff // create L1CoordinatorTx for the accountCreation + if l2TxsRaw[i].ToEthAddr == nil { + log.Warn("l2TxsRaw[i].ToEthAddr should not be nil") + continue + } l1CoordinatorTx := &common.L1Tx{ Position: positionL1, UserOrigin: false, - FromEthAddr: l2TxsRaw[i].ToEthAddr, + FromEthAddr: *l2TxsRaw[i].ToEthAddr, FromBJJ: l2TxsRaw[i].ToBJJ, TokenID: l2TxsRaw[i].TokenID, LoadAmount: big.NewInt(0), @@ -227,7 +222,16 @@ func (txsel *TxSelector) GetL1L2TxSelection(batchNum common.BatchNum, l1Txs []*c positionL1++ l1CoordinatorTxs = append(l1CoordinatorTxs, l1CoordinatorTx) } - } else if l2TxsRaw[i].ToIdx == common.Idx(1) { + } else if *l2TxsRaw[i].ToIdx >= common.IdxUserThreshold { + _, err = txsel.localAccountsDB.GetAccount(l2TxsRaw[i].ToIdx) + if err != nil { + // tx not valid + log.Debugw("invalid L2Tx: ToIdx not found in StateDB", "ToIdx", l2TxsRaw[i].ToIdx) + continue + } + // Account found in the DB, include the l2Tx in the selection + validTxs = append(validTxs, l2TxsRaw[i]) + } else if *l2TxsRaw[i].ToIdx == common.Idx(1) { // nil already checked before // valid txs (of Exit type) validTxs = append(validTxs, l2TxsRaw[i]) } @@ -250,7 +254,11 @@ func (txsel *TxSelector) GetL1L2TxSelection(batchNum common.BatchNum, l1Txs []*c return l1Txs, l1CoordinatorTxs, l2Txs, nil } -func checkAlreadyPendingToCreate(l1CoordinatorTxs []*common.L1Tx, addr ethCommon.Address, bjj *babyjub.PublicKey) bool { +func checkAlreadyPendingToCreate(l1CoordinatorTxs []*common.L1Tx, addr *ethCommon.Address, bjj *babyjub.PublicKey) bool { + if addr == nil { + log.Warn("The provided addr is nil") + return false + } for i := 0; i < len(l1CoordinatorTxs); i++ { if bytes.Equal(l1CoordinatorTxs[i].FromEthAddr.Bytes(), addr.Bytes()) { if bjj == nil {