Browse Source

Feature/null refactor (#173)

* WIP: rebase

* Make nullable fields use pointers
feature/sql-semaphore1
a_bennassar 3 years ago
committed by GitHub
parent
commit
49366e3fa4
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 422 additions and 240 deletions
  1. +5
    -5
      api/api_test.go
  2. +9
    -5
      api/dbtoapistructs.go
  3. +0
    -3
      api/parsers.go
  4. +0
    -4
      api/parsers_test.go
  5. +45
    -15
      api/swagger.yml
  6. +2
    -1
      common/batch.go
  7. +23
    -9
      common/l1tx.go
  8. +9
    -3
      common/l1tx_test.go
  9. +12
    -4
      common/l2tx.go
  10. +45
    -26
      common/pooll2tx.go
  11. +16
    -11
      common/pooll2tx_test.go
  12. +15
    -14
      common/tx.go
  13. +2
    -1
      db/historydb/historydb.go
  14. +1
    -1
      db/historydb/historydb_test.go
  15. +4
    -4
      db/historydb/views.go
  16. +16
    -12
      db/l2db/l2db.go
  17. +13
    -12
      db/l2db/l2db_test.go
  18. +7
    -6
      db/migrations/0001.sql
  19. +22
    -7
      db/statedb/statedb.go
  20. +18
    -9
      db/statedb/statedb_test.go
  21. +34
    -14
      db/statedb/txprocessors.go
  22. +10
    -7
      db/statedb/txprocessors_test.go
  23. +11
    -4
      db/statedb/utils.go
  24. +11
    -11
      db/statedb/utils_test.go
  25. +13
    -8
      synchronizer/synchronizer.go
  26. +1
    -1
      test/ethclient.go
  27. +1
    -1
      test/ethclient_test.go
  28. +14
    -11
      test/historydb.go
  29. +27
    -10
      test/l2db.go
  30. +13
    -6
      test/txs.go
  31. +1
    -1
      test/txs_test.go
  32. +22
    -14
      txselector/txselector.go

+ 5
- 5
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,

+ 9
- 5
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,

+ 0
- 3
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

+ 0
- 4
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)

+ 45
- 15
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'

+ 2
- 1
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

+ 23
- 9
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])

+ 9
- 3
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"),
}

+ 12
- 4
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,

+ 45
- 26
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

+ 16
- 11
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: &ethAddr,
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: &ethAddr,
RqToBJJ: sk.Public(),
}
toSign, err := tx.HashToSign()

+ 15
- 14
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
}

+ 2
- 1
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[:],
)

+ 1
- 1
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) {

+ 4
- 4
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"`

+ 16
- 12
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) {

+ 13
- 12
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)

+ 7
- 6
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,

+ 22
- 7
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 {

+ 18
- 9
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)
}

+ 34
- 14
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
}

+ 10
- 7
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())
}

+ 11
- 4
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)

+ 11
- 11
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)

+ 13
- 8
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

+ 1
- 1
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) {

+ 1
- 1
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),

+ 14
- 11
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

+ 27
- 10
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)
}

+ 13
- 6
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)),

+ 1
- 1
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)

+ 22
- 14
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 {

Loading…
Cancel
Save