Browse Source

Feature/null refactor (#173)

* WIP: rebase

* Make nullable fields use pointers
feature/sql-semaphore1
a_bennassar 4 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") panic("Token not found")
} }
} else { } else {
token = test.GetToken(genericTx.FromIdx, accs, tokens)
token = test.GetToken(*genericTx.FromIdx, accs, tokens)
} }
var usd, loadUSD, feeUSD *float64 var usd, loadUSD, feeUSD *float64
if token.USD != nil { if token.USD != nil {
@ -244,7 +244,7 @@ func TestMain(m *testing.M) {
Type: genericTx.Type, Type: genericTx.Type,
Position: genericTx.Position, Position: genericTx.Position,
FromIdx: genericTx.FromIdx, FromIdx: genericTx.FromIdx,
ToIdx: genericTx.ToIdx,
ToIdx: *genericTx.ToIdx,
Amount: genericTx.Amount, Amount: genericTx.Amount,
AmountFloat: genericTx.AmountFloat, AmountFloat: genericTx.AmountFloat,
HistoricUSD: usd, HistoricUSD: usd,
@ -361,7 +361,7 @@ func TestGetHistoryTxs(t *testing.T) {
// idx // idx
fetchedTxs = historyTxAPIs{} fetchedTxs = historyTxAPIs{}
limit = 4 limit = 4
idx := tc.allTxs[0].FromIdx
idx := tc.allTxs[0].ToIdx
path = fmt.Sprintf( path = fmt.Sprintf(
"%s?accountIndex=%s&limit=%d&offset=", "%s?accountIndex=%s&limit=%d&offset=",
endpoint, idx, limit, endpoint, idx, limit,
@ -370,7 +370,8 @@ func TestGetHistoryTxs(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
idxTxs := historyTxAPIs{} idxTxs := historyTxAPIs{}
for i := 0; i < len(tc.allTxs); i++ { 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]) idxTxs = append(idxTxs, tc.allTxs[i])
} }
} }
@ -396,7 +397,6 @@ func TestGetHistoryTxs(t *testing.T) {
// type // type
txTypes := []common.TxType{ txTypes := []common.TxType{
common.TxTypeExit, common.TxTypeExit,
common.TxTypeWithdrawn,
common.TxTypeTransfer, common.TxTypeTransfer,
common.TxTypeDeposit, common.TxTypeDeposit,
common.TxTypeCreateAccountDeposit, 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) } func (htx *historyTxsAPI) Len() int { return len(htx.Txs) }
type l1Info struct { type l1Info struct {
ToForgeL1TxsNum int64 `json:"toForgeL1TransactionsNum"`
ToForgeL1TxsNum *int64 `json:"toForgeL1TransactionsNum"`
UserOrigin bool `json:"userOrigin"` UserOrigin bool `json:"userOrigin"`
FromEthAddr string `json:"fromHezEthereumAddress"` FromEthAddr string `json:"fromHezEthereumAddress"`
FromBJJ string `json:"fromBJJ"` FromBJJ string `json:"fromBJJ"`
@ -58,7 +58,7 @@ type historyTxAPI struct {
TxID string `json:"id"` TxID string `json:"id"`
Type common.TxType `json:"type"` Type common.TxType `json:"type"`
Position int `json:"position"` Position int `json:"position"`
FromIdx string `json:"fromAccountIndex"`
FromIdx *string `json:"fromAccountIndex"`
ToIdx string `json:"toAccountIndex"` ToIdx string `json:"toAccountIndex"`
Amount string `json:"amount"` Amount string `json:"amount"`
BatchNum *common.BatchNum `json:"batchNum"` BatchNum *common.BatchNum `json:"batchNum"`
@ -76,7 +76,6 @@ func historyTxsToAPI(dbTxs []*historydb.HistoryTx) []historyTxAPI {
TxID: dbTxs[i].TxID.String(), TxID: dbTxs[i].TxID.String(),
Type: dbTxs[i].Type, Type: dbTxs[i].Type,
Position: dbTxs[i].Position, Position: dbTxs[i].Position,
FromIdx: idxToHez(dbTxs[i].FromIdx, dbTxs[i].TokenSymbol),
ToIdx: idxToHez(dbTxs[i].ToIdx, dbTxs[i].TokenSymbol), ToIdx: idxToHez(dbTxs[i].ToIdx, dbTxs[i].TokenSymbol),
Amount: dbTxs[i].Amount.String(), Amount: dbTxs[i].Amount.String(),
HistoricUSD: dbTxs[i].HistoricUSD, HistoricUSD: dbTxs[i].HistoricUSD,
@ -95,12 +94,17 @@ func historyTxsToAPI(dbTxs []*historydb.HistoryTx) []historyTxAPI {
L1Info: nil, L1Info: nil,
L2Info: 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 { if dbTxs[i].IsL1 {
apiTx.IsL1 = "L1" apiTx.IsL1 = "L1"
apiTx.L1Info = &l1Info{ apiTx.L1Info = &l1Info{
ToForgeL1TxsNum: dbTxs[i].ToForgeL1TxsNum, 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), FromBJJ: bjjToString(dbTxs[i].FromBJJ),
LoadAmount: dbTxs[i].LoadAmount.String(), LoadAmount: dbTxs[i].LoadAmount.String(),
HistoricLoadAmountUSD: dbTxs[i].HistoricLoadAmountUSD, HistoricLoadAmountUSD: dbTxs[i].HistoricLoadAmountUSD,

+ 0
- 3
api/parsers.go

@ -149,9 +149,6 @@ func parseQueryTxType(c querier) (*common.TxType, error) {
case common.TxTypeExit: case common.TxTypeExit:
ret := common.TxTypeExit ret := common.TxTypeExit
return &ret, nil return &ret, nil
case common.TxTypeWithdrawn:
ret := common.TxTypeWithdrawn
return &ret, nil
case common.TxTypeTransfer: case common.TxTypeTransfer:
ret := common.TxTypeTransfer ret := common.TxTypeTransfer
return &ret, nil return &ret, nil

+ 0
- 4
api/parsers_test.go

@ -239,10 +239,6 @@ func TestParseQueryTxType(t *testing.T) {
res, err = parseQueryTxType(c) res, err = parseQueryTxType(c)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, common.TxTypeExit, *res) 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) c.m[name] = string(common.TxTypeTransfer)
res, err = parseQueryTxType(c) res, err = parseQueryTxType(c)
assert.NoError(t, err) assert.NoError(t, err)

+ 45
- 15
api/swagger.yml

@ -1240,13 +1240,24 @@ components:
fromAccountIndex: fromAccountIndex:
$ref: '#/components/schemas/AccountIndex' $ref: '#/components/schemas/AccountIndex'
toAccountIndex: 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: 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: 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: amount:
allOf: allOf:
- $ref: '#/components/schemas/BigInt' - $ref: '#/components/schemas/BigInt'
@ -1308,6 +1319,7 @@ components:
- type - type
- tokenId - tokenId
- fromAccountIndex - fromAccountIndex
- toAccountIndex
- toHezAccountIndex - toHezAccountIndex
- toHezEthereumAddress - toHezEthereumAddress
- toBjj - toBjj
@ -1333,13 +1345,24 @@ components:
fromAccountIndex: fromAccountIndex:
$ref: '#/components/schemas/AccountIndex' $ref: '#/components/schemas/AccountIndex'
toAccountIndex: 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: 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: 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: amount:
allOf: allOf:
- $ref: '#/components/schemas/BigInt' - $ref: '#/components/schemas/BigInt'
@ -1448,7 +1471,6 @@ components:
description: Type of transaction. description: Type of transaction.
enum: enum:
- Exit - Exit
- Withdrawn
- Transfer - Transfer
- Deposit - Deposit
- CreateAccountDeposit - CreateAccountDeposit
@ -1539,7 +1561,12 @@ components:
position: position:
$ref: '#/components/schemas/TransactionPosition' $ref: '#/components/schemas/TransactionPosition'
fromAccountIndex: 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: toAccountIndex:
allOf: allOf:
- $ref: '#/components/schemas/AccountIndex' - $ref: '#/components/schemas/AccountIndex'
@ -1666,6 +1693,7 @@ components:
minimum: 0 minimum: 0
maximum: 4294967295 maximum: 4294967295
example: 784 example: 784
nullable: true
TransactionPosition: TransactionPosition:
type: integer type: integer
description: Position that a transaction occupies in a batch. description: Position that a transaction occupies in a batch.
@ -1689,8 +1717,8 @@ components:
items: items:
type: object type: object
properties: properties:
token:
$ref: '#/components/schemas/Token'
tokenId:
$ref: '#/components/schemas/TokenId'
amount: amount:
allOf: allOf:
- $ref: '#/components/schemas/BigInt' - $ref: '#/components/schemas/BigInt'
@ -1716,6 +1744,9 @@ components:
$ref: '#/components/schemas/EthereumAddress' $ref: '#/components/schemas/EthereumAddress'
collectedFees: collectedFees:
$ref: '#/components/schemas/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: historicTotalCollectedFeesUSD:
type: number type: number
description: Sum of the all the fees collected, in USD, at the moment the batch was forged. description: Sum of the all the fees collected, in USD, at the moment the batch was forged.
@ -1737,7 +1768,6 @@ components:
allOf: allOf:
- $ref: '#/components/schemas/ToForgeL1TransactionsNum' - $ref: '#/components/schemas/ToForgeL1TransactionsNum'
- description: Identifier that corresponds to the group of L1 transactions forged in the current batch. - description: Identifier that corresponds to the group of L1 transactions forged in the current batch.
- nullable: true
- example: 5 - example: 5
slotNum: slotNum:
$ref: '#/components/schemas/SlotNum' $ref: '#/components/schemas/SlotNum'

+ 2
- 1
common/batch.go

@ -19,8 +19,9 @@ type Batch struct {
StateRoot Hash `meddler:"state_root"` StateRoot Hash `meddler:"state_root"`
NumAccounts int `meddler:"num_accounts"` NumAccounts int `meddler:"num_accounts"`
ExitRoot Hash `meddler:"exit_root"` 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 SlotNum SlotNum `meddler:"slot_num"` // Slot in which the batch is forged
TotalFeesUSD *float64 `meddler:"total_fees_usd"`
} }
// BatchNum identifies a batch // BatchNum identifies a batch

+ 23
- 9
common/l1tx.go

@ -25,10 +25,10 @@ type L1Tx struct {
// - L1UserTx: 0 // - L1UserTx: 0
// - L1CoordinatorTx: 1 // - L1CoordinatorTx: 1
TxID TxID 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 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 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 FromEthAddr ethCommon.Address
FromBJJ *babyjub.PublicKey FromBJJ *babyjub.PublicKey
ToIdx Idx // ToIdx is ignored in L1Tx/Deposit, but used in the L1Tx/DepositAndTransfer 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) { func NewL1Tx(l1Tx *L1Tx) (*L1Tx, error) {
// calculate TxType // calculate TxType
var txType TxType var txType TxType
if l1Tx.FromIdx == Idx(0) {
if l1Tx.FromIdx == nil {
if l1Tx.ToIdx == Idx(0) { if l1Tx.ToIdx == Idx(0) {
txType = TxTypeCreateAccountDeposit txType = TxTypeCreateAccountDeposit
} else if l1Tx.ToIdx >= IdxUserThreshold { } else if l1Tx.ToIdx >= IdxUserThreshold {
@ -55,7 +55,7 @@ func NewL1Tx(l1Tx *L1Tx) (*L1Tx, error) {
} else { } else {
return l1Tx, fmt.Errorf("Can not determine type of L1Tx, invalid ToIdx value: %d", l1Tx.ToIdx) 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) { if l1Tx.ToIdx == Idx(0) {
txType = TxTypeDeposit txType = TxTypeDeposit
} else if l1Tx.ToIdx == Idx(1) { } else if l1Tx.ToIdx == Idx(1) {
@ -83,7 +83,11 @@ func NewL1Tx(l1Tx *L1Tx) (*L1Tx, error) {
txid[0] = TxIDPrefixL1CoordTx txid[0] = TxIDPrefixL1CoordTx
} }
var toForgeL1TxsNumBytes [8]byte 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[:]) copy(txid[1:9], toForgeL1TxsNumBytes[:])
var positionBytes [2]byte var positionBytes [2]byte
@ -98,19 +102,25 @@ func NewL1Tx(l1Tx *L1Tx) (*L1Tx, error) {
func (tx *L1Tx) Tx() *Tx { func (tx *L1Tx) Tx() *Tx {
f := new(big.Float).SetInt(tx.Amount) f := new(big.Float).SetInt(tx.Amount)
amountFloat, _ := f.Float64() amountFloat, _ := f.Float64()
userOrigin := new(bool)
*userOrigin = tx.UserOrigin
fromEthAddr := new(ethCommon.Address)
*fromEthAddr = tx.FromEthAddr
toIdx := new(Idx)
*toIdx = tx.ToIdx
genericTx := &Tx{ genericTx := &Tx{
IsL1: true, IsL1: true,
TxID: tx.TxID, TxID: tx.TxID,
Type: tx.Type, Type: tx.Type,
Position: tx.Position, Position: tx.Position,
FromIdx: tx.FromIdx, FromIdx: tx.FromIdx,
ToIdx: tx.ToIdx,
ToIdx: toIdx,
Amount: tx.Amount, Amount: tx.Amount,
AmountFloat: amountFloat, AmountFloat: amountFloat,
TokenID: tx.TokenID, TokenID: tx.TokenID,
ToForgeL1TxsNum: tx.ToForgeL1TxsNum, ToForgeL1TxsNum: tx.ToForgeL1TxsNum,
UserOrigin: tx.UserOrigin,
FromEthAddr: tx.FromEthAddr,
UserOrigin: userOrigin,
FromEthAddr: fromEthAddr,
FromBJJ: tx.FromBJJ, FromBJJ: tx.FromBJJ,
LoadAmount: tx.LoadAmount, LoadAmount: tx.LoadAmount,
EthBlockNum: tx.EthBlockNum, EthBlockNum: tx.EthBlockNum,
@ -171,10 +181,14 @@ func L1TxFromBytes(b []byte) (*L1Tx, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
tx.FromIdx, err = IdxFromBytes(b[52:58])
fromIdx, err := IdxFromBytes(b[52:58])
if err != nil { if err != nil {
return nil, err return nil, err
} }
if fromIdx != 0 {
tx.FromIdx = new(Idx)
*tx.FromIdx = fromIdx
}
tx.LoadAmount = Float16FromBytes(b[58:60]).BigInt() tx.LoadAmount = Float16FromBytes(b[58:60]).BigInt()
tx.Amount = Float16FromBytes(b[60:62]).BigInt() tx.Amount = Float16FromBytes(b[60:62]).BigInt()
tx.TokenID, err = TokenIDFromBytes(b[62:66]) tx.TokenID, err = TokenIDFromBytes(b[62:66])

+ 9
- 3
common/l1tx_test.go

@ -12,14 +12,18 @@ import (
) )
func TestNewL1Tx(t *testing.T) { func TestNewL1Tx(t *testing.T) {
toForge := new(int64)
*toForge = 123456
fromIdx := new(Idx)
*fromIdx = 300
l1Tx := &L1Tx{ l1Tx := &L1Tx{
ToForgeL1TxsNum: int64(123456),
ToForgeL1TxsNum: toForge,
Position: 71, Position: 71,
ToIdx: 301, ToIdx: 301,
TokenID: 5, TokenID: 5,
Amount: big.NewInt(1), Amount: big.NewInt(1),
LoadAmount: big.NewInt(2), LoadAmount: big.NewInt(2),
FromIdx: 300,
FromIdx: fromIdx,
} }
l1Tx, err := NewL1Tx(l1Tx) l1Tx, err := NewL1Tx(l1Tx)
assert.Nil(t, err) assert.Nil(t, err)
@ -34,12 +38,14 @@ func TestL1TxByteParsers(t *testing.T) {
pk, err := pkComp.Decompress() pk, err := pkComp.Decompress()
require.Nil(t, err) require.Nil(t, err)
fromIdx := new(Idx)
*fromIdx = 2
l1Tx := &L1Tx{ l1Tx := &L1Tx{
ToIdx: 3, ToIdx: 3,
TokenID: 5, TokenID: 5,
Amount: big.NewInt(1), Amount: big.NewInt(1),
LoadAmount: big.NewInt(2), LoadAmount: big.NewInt(2),
FromIdx: 2,
FromIdx: fromIdx,
FromBJJ: pk, FromBJJ: pk,
FromEthAddr: ethCommon.HexToAddress("0xc58d29fA6e86E4FAe04DDcEd660d45BCf3Cb2370"), FromEthAddr: ethCommon.HexToAddress("0xc58d29fA6e86E4FAe04DDcEd660d45BCf3Cb2370"),
} }

+ 12
- 4
common/l2tx.go

@ -68,13 +68,17 @@ func (tx *L2Tx) Tx() *Tx {
*fee = tx.Fee *fee = tx.Fee
nonce := new(Nonce) nonce := new(Nonce)
*nonce = tx.Nonce *nonce = tx.Nonce
fromIdx := new(Idx)
*fromIdx = tx.FromIdx
toIdx := new(Idx)
*toIdx = tx.ToIdx
return &Tx{ return &Tx{
IsL1: false, IsL1: false,
TxID: tx.TxID, TxID: tx.TxID,
Type: tx.Type, Type: tx.Type,
Position: tx.Position, Position: tx.Position,
FromIdx: tx.FromIdx,
ToIdx: tx.ToIdx,
FromIdx: fromIdx,
ToIdx: toIdx,
Amount: tx.Amount, Amount: tx.Amount,
USD: tx.USD, USD: tx.USD,
AmountFloat: amountFloat, AmountFloat: amountFloat,
@ -89,11 +93,15 @@ func (tx *L2Tx) Tx() *Tx {
// PoolL2Tx returns the data structure of PoolL2Tx with the parameters of a // PoolL2Tx returns the data structure of PoolL2Tx with the parameters of a
// L2Tx filled // L2Tx filled
func (tx *L2Tx) PoolL2Tx() *PoolL2Tx { func (tx *L2Tx) PoolL2Tx() *PoolL2Tx {
batchNum := new(BatchNum)
*batchNum = tx.BatchNum
toIdx := new(Idx)
*toIdx = tx.ToIdx
return &PoolL2Tx{ return &PoolL2Tx{
TxID: tx.TxID, TxID: tx.TxID,
BatchNum: tx.BatchNum,
BatchNum: batchNum,
FromIdx: tx.FromIdx, FromIdx: tx.FromIdx,
ToIdx: tx.ToIdx,
ToIdx: toIdx,
Amount: tx.Amount, Amount: tx.Amount,
Fee: tx.Fee, Fee: tx.Fee,
Nonce: tx.Nonce, Nonce: tx.Nonce,

+ 45
- 26
common/pooll2tx.go

@ -1,6 +1,7 @@
package common package common
import ( import (
"errors"
"fmt" "fmt"
"math/big" "math/big"
"time" "time"
@ -19,8 +20,8 @@ type PoolL2Tx struct {
// values: | type | FromIdx | Nonce | // values: | type | FromIdx | Nonce |
TxID TxID `meddler:"tx_id"` 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) 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 ToBJJ *babyjub.PublicKey `meddler:"to_bjj"` // TODO: stop using json, use scanner/valuer
TokenID TokenID `meddler:"token_id"` TokenID TokenID `meddler:"token_id"`
Amount *big.Int `meddler:"amount,bigint"` // TODO: change to float16 Amount *big.Int `meddler:"amount,bigint"` // TODO: change to float16
@ -32,21 +33,20 @@ type PoolL2Tx struct {
Signature *babyjub.Signature `meddler:"signature"` // tx signature Signature *babyjub.Signature `meddler:"signature"` // tx signature
Timestamp time.Time `meddler:"timestamp,utctime"` // time when added to the tx pool Timestamp time.Time `meddler:"timestamp,utctime"` // time when added to the tx pool
// Stored in DB: optional fileds, may be uninitialized // 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 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 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"` AbsoluteFee *float64 `meddler:"fee_usd"`
AbsoluteFeeUpdate *time.Time `meddler:"usd_update,utctime"` AbsoluteFeeUpdate *time.Time `meddler:"usd_update,utctime"`
Type TxType `meddler:"tx_type"` Type TxType `meddler:"tx_type"`
// Extra metadata, may be uninitialized // Extra metadata, may be uninitialized
RqTxCompressedData []byte `meddler:"-"` // 253 bits, optional for atomic txs 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 // NewPoolL2Tx returns the given L2Tx with the TxId & Type parameters calculated
@ -54,11 +54,11 @@ type PoolL2Tx struct {
func NewPoolL2Tx(poolL2Tx *PoolL2Tx) (*PoolL2Tx, error) { func NewPoolL2Tx(poolL2Tx *PoolL2Tx) (*PoolL2Tx, error) {
// calculate TxType // calculate TxType
var txType TxType var txType TxType
if poolL2Tx.ToIdx == Idx(0) {
if poolL2Tx.ToIdx == nil || *poolL2Tx.ToIdx == Idx(0) {
txType = TxTypeTransfer txType = TxTypeTransfer
} else if poolL2Tx.ToIdx == Idx(1) {
} else if *poolL2Tx.ToIdx == Idx(1) {
txType = TxTypeExit txType = TxTypeExit
} else if poolL2Tx.ToIdx >= IdxUserThreshold {
} else if *poolL2Tx.ToIdx >= IdxUserThreshold {
txType = TxTypeTransfer txType = TxTypeTransfer
} else { } else {
return poolL2Tx, fmt.Errorf("Can not determine type of PoolL2Tx, invalid ToIdx value: %d", poolL2Tx.ToIdx) 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 { if err != nil {
return nil, err 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 toBJJAy := tx.ToBJJ.Y
rqTxCompressedDataV2, err := tx.TxCompressedDataV2() rqTxCompressedDataV2, err := tx.TxCompressedDataV2()
if err != nil { if err != nil {
return nil, err 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 // 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 // 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{ return &L2Tx{
TxID: tx.TxID, TxID: tx.TxID,
BatchNum: tx.BatchNum,
BatchNum: *tx.BatchNum,
FromIdx: tx.FromIdx, FromIdx: tx.FromIdx,
ToIdx: tx.ToIdx,
ToIdx: *tx.ToIdx,
Amount: tx.Amount, Amount: tx.Amount,
Fee: tx.Fee, Fee: tx.Fee,
Nonce: tx.Nonce, Nonce: tx.Nonce,
Type: tx.Type, Type: tx.Type,
}
}, nil
} }
// Tx returns a *Tx from the PoolL2Tx // 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{ return &Tx{
TxID: tx.TxID, TxID: tx.TxID,
FromIdx: tx.FromIdx,
FromIdx: fromIdx,
ToIdx: tx.ToIdx, ToIdx: tx.ToIdx,
Amount: tx.Amount, Amount: tx.Amount,
Nonce: &tx.Nonce, Nonce: &tx.Nonce,
Fee: &tx.Fee, Fee: &tx.Fee,
Type: tx.Type, Type: tx.Type,
}
}, nil
} }
// PoolL2TxsToL2Txs returns an array of []*L2Tx from an array of []*PoolL2Tx // 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 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 // 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) { func TestNewPoolL2Tx(t *testing.T) {
toIdx := new(Idx)
*toIdx = 300
poolL2Tx := &PoolL2Tx{ poolL2Tx := &PoolL2Tx{
FromIdx: 87654, FromIdx: 87654,
ToIdx: 300,
ToIdx: toIdx,
Amount: big.NewInt(4), Amount: big.NewInt(4),
TokenID: 5, TokenID: 5,
Nonce: 144, Nonce: 144,
@ -27,10 +29,11 @@ func TestTxCompressedData(t *testing.T) {
var sk babyjub.PrivateKey var sk babyjub.PrivateKey
_, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001")) _, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001"))
assert.Nil(t, err) assert.Nil(t, err)
toIdx := new(Idx)
*toIdx = 3
tx := PoolL2Tx{ tx := PoolL2Tx{
FromIdx: 2, FromIdx: 2,
ToIdx: 3,
ToIdx: toIdx,
Amount: big.NewInt(4), Amount: big.NewInt(4),
TokenID: 5, TokenID: 5,
Nonce: 6, Nonce: 6,
@ -45,10 +48,10 @@ func TestTxCompressedData(t *testing.T) {
assert.True(t, ok) assert.True(t, ok)
assert.Equal(t, expected.Bytes(), txCompressedData.Bytes()) assert.Equal(t, expected.Bytes(), txCompressedData.Bytes())
assert.Equal(t, "10000000000060000000500040000000000030000000000020001c60be60f", hex.EncodeToString(txCompressedData.Bytes())[1:]) assert.Equal(t, "10000000000060000000500040000000000030000000000020001c60be60f", hex.EncodeToString(txCompressedData.Bytes())[1:])
*toIdx = 8
tx = PoolL2Tx{ tx = PoolL2Tx{
FromIdx: 7, FromIdx: 7,
ToIdx: 8,
ToIdx: toIdx,
Amount: big.NewInt(9), Amount: big.NewInt(9),
TokenID: 10, TokenID: 10,
Nonce: 11, Nonce: 11,
@ -71,15 +74,16 @@ func TestHashToSign(t *testing.T) {
_, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001")) _, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001"))
assert.Nil(t, err) assert.Nil(t, err)
ethAddr := ethCommon.HexToAddress("0xc58d29fA6e86E4FAe04DDcEd660d45BCf3Cb2370") ethAddr := ethCommon.HexToAddress("0xc58d29fA6e86E4FAe04DDcEd660d45BCf3Cb2370")
toIdx := new(Idx)
*toIdx = 3
tx := PoolL2Tx{ tx := PoolL2Tx{
FromIdx: 2, FromIdx: 2,
ToIdx: 3,
ToIdx: toIdx,
Amount: big.NewInt(4), Amount: big.NewInt(4),
TokenID: 5, TokenID: 5,
Nonce: 6, Nonce: 6,
ToBJJ: sk.Public(), ToBJJ: sk.Public(),
RqToEthAddr: ethAddr,
RqToEthAddr: &ethAddr,
RqToBJJ: sk.Public(), RqToBJJ: sk.Public(),
} }
toSign, err := tx.HashToSign() toSign, err := tx.HashToSign()
@ -92,15 +96,16 @@ func TestVerifyTxSignature(t *testing.T) {
_, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001")) _, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001"))
assert.Nil(t, err) assert.Nil(t, err)
ethAddr := ethCommon.HexToAddress("0xc58d29fA6e86E4FAe04DDcEd660d45BCf3Cb2370") ethAddr := ethCommon.HexToAddress("0xc58d29fA6e86E4FAe04DDcEd660d45BCf3Cb2370")
toIdx := new(Idx)
*toIdx = 3
tx := PoolL2Tx{ tx := PoolL2Tx{
FromIdx: 2, FromIdx: 2,
ToIdx: 3,
ToIdx: toIdx,
Amount: big.NewInt(4), Amount: big.NewInt(4),
TokenID: 5, TokenID: 5,
Nonce: 6, Nonce: 6,
ToBJJ: sk.Public(), ToBJJ: sk.Public(),
RqToEthAddr: ethAddr,
RqToEthAddr: &ethAddr,
RqToBJJ: sk.Public(), RqToBJJ: sk.Public(),
} }
toSign, err := tx.HashToSign() toSign, err := tx.HashToSign()

+ 15
- 14
common/tx.go

@ -3,6 +3,7 @@ package common
import ( import (
"database/sql/driver" "database/sql/driver"
"encoding/hex" "encoding/hex"
"errors"
"fmt" "fmt"
"math/big" "math/big"
@ -57,8 +58,6 @@ type TxType string
const ( const (
// TxTypeExit represents L2->L1 token transfer. A leaf for this account appears in the exit tree of the block // TxTypeExit represents L2->L1 token transfer. A leaf for this account appears in the exit tree of the block
TxTypeExit TxType = "Exit" 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 represents L2->L2 token transfer
TxTypeTransfer TxType = "Transfer" TxTypeTransfer TxType = "Transfer"
// TxTypeDeposit represents L1->L2 transfer // TxTypeDeposit represents L1->L2 transfer
@ -86,8 +85,8 @@ type Tx struct {
TxID TxID `meddler:"id"` TxID TxID `meddler:"id"`
Type TxType `meddler:"type"` Type TxType `meddler:"type"`
Position int `meddler:"position"` 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"` Amount *big.Int `meddler:"amount,bigint"`
AmountFloat float64 `meddler:"amount_f"` AmountFloat float64 `meddler:"amount_f"`
TokenID TokenID `meddler:"token_id"` 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 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 EthBlockNum int64 `meddler:"eth_block_num"` // Ethereum Block Number in which this L1Tx was added to the queue
// L1 // 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"` FromBJJ *babyjub.PublicKey `meddler:"from_bjj"`
LoadAmount *big.Int `meddler:"load_amount,bigintnull"` LoadAmount *big.Int `meddler:"load_amount,bigintnull"`
LoadAmountFloat *float64 `meddler:"load_amount_f"` LoadAmountFloat *float64 `meddler:"load_amount_f"`
@ -109,22 +108,24 @@ type Tx struct {
} }
// L1Tx returns a *L1Tx from the Tx // 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, TxID: tx.TxID,
ToForgeL1TxsNum: tx.ToForgeL1TxsNum, ToForgeL1TxsNum: tx.ToForgeL1TxsNum,
Position: tx.Position, Position: tx.Position,
UserOrigin: tx.UserOrigin,
UserOrigin: *tx.UserOrigin,
FromIdx: tx.FromIdx, FromIdx: tx.FromIdx,
FromEthAddr: tx.FromEthAddr,
FromEthAddr: *tx.FromEthAddr,
FromBJJ: tx.FromBJJ, FromBJJ: tx.FromBJJ,
ToIdx: tx.ToIdx,
ToIdx: *tx.ToIdx,
TokenID: tx.TokenID, TokenID: tx.TokenID,
Amount: tx.Amount, Amount: tx.Amount,
LoadAmount: tx.LoadAmount, LoadAmount: tx.LoadAmount,
EthBlockNum: tx.EthBlockNum, EthBlockNum: tx.EthBlockNum,
Type: tx.Type, Type: tx.Type,
BatchNum: tx.BatchNum, 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, num_accounts,
exit_root, exit_root,
forge_l1_txs_num, forge_l1_txs_num,
slot_num
slot_num,
total_fees_usd
) VALUES %s;`, ) VALUES %s;`,
batches[:], batches[:],
) )

+ 1
- 1
db/historydb/historydb_test.go

@ -109,7 +109,7 @@ func TestBatches(t *testing.T) {
// Test GetLastL1TxsNum // Test GetLastL1TxsNum
fetchedLastL1TxsNum, err = historyDB.GetLastL1TxsNum() fetchedLastL1TxsNum, err = historyDB.GetLastL1TxsNum()
assert.NoError(t, err) 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) { func TestBids(t *testing.T) {

+ 4
- 4
db/historydb/views.go

@ -17,7 +17,7 @@ type HistoryTx struct {
TxID common.TxID `meddler:"id"` TxID common.TxID `meddler:"id"`
Type common.TxType `meddler:"type"` Type common.TxType `meddler:"type"`
Position int `meddler:"position"` Position int `meddler:"position"`
FromIdx common.Idx `meddler:"from_idx"`
FromIdx *common.Idx `meddler:"from_idx"`
ToIdx common.Idx `meddler:"to_idx"` ToIdx common.Idx `meddler:"to_idx"`
Amount *big.Int `meddler:"amount,bigint"` Amount *big.Int `meddler:"amount,bigint"`
AmountFloat float64 `meddler:"amount_f"` 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 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 EthBlockNum int64 `meddler:"eth_block_num"` // Ethereum Block Number in which this L1Tx was added to the queue
// L1 // 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"` FromBJJ *babyjub.PublicKey `meddler:"from_bjj"`
LoadAmount *big.Int `meddler:"load_amount,bigintnull"` LoadAmount *big.Int `meddler:"load_amount,bigintnull"`
LoadAmountFloat *float64 `meddler:"load_amount_f"` LoadAmountFloat *float64 `meddler:"load_amount_f"`

+ 16
- 12
db/l2db/l2db.go

@ -1,6 +1,7 @@
package l2db package l2db
import ( import (
"errors"
"math/big" "math/big"
"time" "time"
@ -49,7 +50,10 @@ func (l2db *L2DB) AddAccountCreationAuth(auth *common.AccountCreationAuth) error
} }
// GetAccountCreationAuth returns an account creation authorization into the DB // 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) auth := new(common.AccountCreationAuth)
return auth, meddler.QueryRow( return auth, meddler.QueryRow(
l2db.db, auth, l2db.db, auth,
@ -64,8 +68,8 @@ func (l2db *L2DB) AddTxTest(tx *common.PoolL2Tx) error {
type withouUSD struct { type withouUSD struct {
TxID common.TxID `meddler:"tx_id"` TxID common.TxID `meddler:"tx_id"`
FromIdx common.Idx `meddler:"from_idx"` 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"` ToBJJ *babyjub.PublicKey `meddler:"to_bjj"`
TokenID common.TokenID `meddler:"token_id"` TokenID common.TokenID `meddler:"token_id"`
Amount *big.Int `meddler:"amount,bigint"` Amount *big.Int `meddler:"amount,bigint"`
@ -75,15 +79,15 @@ func (l2db *L2DB) AddTxTest(tx *common.PoolL2Tx) error {
State common.PoolL2TxState `meddler:"state"` State common.PoolL2TxState `meddler:"state"`
Signature *babyjub.Signature `meddler:"signature"` Signature *babyjub.Signature `meddler:"signature"`
Timestamp time.Time `meddler:"timestamp,utctime"` 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"` 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"` 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"` Type common.TxType `meddler:"tx_type"`
} }
return meddler.Insert(l2db.db, "tx_pool", &withouUSD{ 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 // selectPoolTx select part of queries to get common.PoolL2Tx
const selectPoolTx = `SELECT tx_pool.*, token.usd * tx_pool.amount_f AS value_usd, 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 // GetTx return the specified Tx
func (l2db *L2DB) GetTx(txID common.TxID) (*common.PoolL2Tx, error) { 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) fetchedTx, err := l2DB.GetTx(id)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, common.PoolL2TxStateForging, fetchedTx.State) 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) fetchedTx, err := l2DB.GetTx(id)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, common.PoolL2TxStateForged, fetchedTx.State) 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) fetchedTx, err := l2DB.GetTx(id)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, common.PoolL2TxStateInvalid, fetchedTx.State) 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) fetchedTx, err := l2DB.GetTx(id)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, common.PoolL2TxStateInvalid, fetchedTx.State) 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{} reorgedTxIDs := []common.TxID{}
nonReorgedTxIDs := []common.TxID{} nonReorgedTxIDs := []common.TxID{}
for i := 0; i < len(txs); i++ { for i := 0; i < len(txs); i++ {
txs[i].BatchNum = new(common.BatchNum)
if txs[i].State == common.PoolL2TxStateForged || txs[i].State == common.PoolL2TxStateInvalid { 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) reorgedTxIDs = append(reorgedTxIDs, txs[i].TxID)
} else { } else {
txs[i].BatchNum = lastValidBatch
*txs[i].BatchNum = lastValidBatch
nonReorgedTxIDs = append(nonReorgedTxIDs, txs[i].TxID) nonReorgedTxIDs = append(nonReorgedTxIDs, txs[i].TxID)
} }
err := l2DB.AddTxTest(txs[i]) err := l2DB.AddTxTest(txs[i])
@ -269,17 +270,16 @@ func TestReorg(t *testing.T) {
} }
err := l2DB.Reorg(lastValidBatch) err := l2DB.Reorg(lastValidBatch)
assert.NoError(t, err) assert.NoError(t, err)
var nullBatchNum common.BatchNum
for _, id := range reorgedTxIDs { for _, id := range reorgedTxIDs {
tx, err := l2DB.GetTx(id) tx, err := l2DB.GetTx(id)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, nullBatchNum, tx.BatchNum)
assert.Nil(t, tx.BatchNum)
assert.Equal(t, common.PoolL2TxStatePending, tx.State) assert.Equal(t, common.PoolL2TxStatePending, tx.State)
} }
for _, id := range nonReorgedTxIDs { for _, id := range nonReorgedTxIDs {
tx, err := l2DB.GetTx(id) tx, err := l2DB.GetTx(id)
assert.NoError(t, err) 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 safeBatchNum := toDeleteBatchNum + l2DB.safetyPeriod + 1
// Add txs to the DB // Add txs to the DB
for i := 0; i < int(l2DB.maxTxs); i++ { for i := 0; i < int(l2DB.maxTxs); i++ {
txs[i].BatchNum = new(common.BatchNum)
if i%1 == 0 { // keep tx if i%1 == 0 { // keep tx
txs[i].BatchNum = safeBatchNum
*txs[i].BatchNum = safeBatchNum
keepedIDs = append(keepedIDs, txs[i].TxID) keepedIDs = append(keepedIDs, txs[i].TxID)
} else if i%2 == 0 { // delete after safety period } else if i%2 == 0 { // delete after safety period
txs[i].BatchNum = toDeleteBatchNum
*txs[i].BatchNum = toDeleteBatchNum
if i%3 == 0 { if i%3 == 0 {
txs[i].State = common.PoolL2TxStateForged txs[i].State = common.PoolL2TxStateForged
} else { } else {
@ -343,7 +344,7 @@ func TestAuth(t *testing.T) {
err := l2DB.AddAccountCreationAuth(auths[i]) err := l2DB.AddAccountCreationAuth(auths[i])
assert.NoError(t, err) assert.NoError(t, err)
// Fetch from DB // Fetch from DB
auth, err := l2DB.GetAccountCreationAuth(auths[i].EthAddr)
auth, err := l2DB.GetAccountCreationAuth(&auths[i].EthAddr)
assert.NoError(t, err) assert.NoError(t, err)
// Check fetched vs generated // Check fetched vs generated
assert.Equal(t, auths[i].EthAddr, auth.EthAddr) assert.Equal(t, auths[i].EthAddr, auth.EthAddr)

+ 7
- 6
db/migrations/0001.sql

@ -17,14 +17,15 @@ CREATE TABLE coordinator (
CREATE TABLE batch ( CREATE TABLE batch (
batch_num BIGINT PRIMARY KEY, 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 forger_addr BYTEA NOT NULL, -- fake foreign key for coordinator
fees_collected BYTEA NOT NULL, fees_collected BYTEA NOT NULL,
state_root BYTEA NOT NULL, state_root BYTEA NOT NULL,
num_accounts BIGINT NOT NULL, num_accounts BIGINT NOT NULL,
exit_root BYTEA NOT NULL, exit_root BYTEA NOT NULL,
forge_l1_txs_num BIGINT, forge_l1_txs_num BIGINT,
slot_num BIGINT NOT NULL
slot_num BIGINT NOT NULL,
total_fees_usd NUMERIC
); );
CREATE TABLE exit_tree ( CREATE TABLE exit_tree (
@ -80,7 +81,7 @@ CREATE TABLE tx (
id BYTEA PRIMARY KEY, id BYTEA PRIMARY KEY,
type VARCHAR(40) NOT NULL, type VARCHAR(40) NOT NULL,
position INT NOT NULL, position INT NOT NULL,
from_idx BIGINT NOT NULL,
from_idx BIGINT,
to_idx BIGINT NOT NULL, to_idx BIGINT NOT NULL,
amount BYTEA NOT NULL, amount BYTEA NOT NULL,
amount_f NUMERIC NOT NULL, amount_f NUMERIC NOT NULL,
@ -470,9 +471,9 @@ CREATE TABLE consensus_vars (
CREATE TABLE tx_pool ( CREATE TABLE tx_pool (
tx_id BYTEA PRIMARY KEY, tx_id BYTEA PRIMARY KEY,
from_idx BIGINT NOT NULL, 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, token_id INT NOT NULL REFERENCES token (token_id) ON DELETE CASCADE,
amount BYTEA NOT NULL, amount BYTEA NOT NULL,
amount_f NUMERIC 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 // 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) return getAccountInTreeDB(s.db, idx)
} }
// getAccountInTreeDB is abstracted from StateDB to be used from StateDB and // getAccountInTreeDB is abstracted from StateDB to be used from StateDB and
// from ExitTree. GetAccount returns the account for the given Idx // 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() idxBytes, err := idx.Bytes()
if err != nil { if err != nil {
return nil, err 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 // StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
// MerkleTree, returning a CircomProcessorProof. // MerkleTree, returning a CircomProcessorProof.
func (s *StateDB) CreateAccount(idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) { 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 { if err != nil {
return cpp, err return cpp, err
} }
// store idx by EthAddr & BJJ // store idx by EthAddr & BJJ
err = s.setIdxByEthAddrBJJ(idx, account.EthAddr, account.PublicKey)
err = s.setIdxByEthAddrBJJ(idx, &account.EthAddr, account.PublicKey)
return cpp, err 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 // from ExitTree. Creates a new Account in the StateDB for the given Idx. If
// StateDB.mt==nil, MerkleTree is not affected, otherwise updates the // StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
// MerkleTree, returning a CircomProcessorProof. // 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() // store at the DB the key: v, and value: leaf.Bytes()
v, err := account.HashValue() v, err := account.HashValue()
if err != nil { 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 // UpdateAccount updates the Account in the StateDB for the given Idx. If
// StateDB.mt==nil, MerkleTree is not affected, otherwise updates the // StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
// MerkleTree, returning a CircomProcessorProof. // 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) 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 // from ExitTree. Updates the Account in the StateDB for the given Idx. If
// StateDB.mt==nil, MerkleTree is not affected, otherwise updates the // StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
// MerkleTree, returning a CircomProcessorProof. // 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() // store at the DB the key: v, and value: account.Bytes()
v, err := account.HashValue() v, err := account.HashValue()
if err != nil { 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 // 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.NotNil(t, err)
assert.Equal(t, db.ErrNotFound, err) assert.Equal(t, db.ErrNotFound, err)
@ -140,13 +141,15 @@ func TestStateDBWithoutMT(t *testing.T) {
} }
for i := 0; i < len(accounts); i++ { 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.Nil(t, err)
assert.Equal(t, accounts[i], accGetted) assert.Equal(t, accounts[i], accGetted)
} }
// try already existing idx and get error // 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) assert.Nil(t, err)
_, err = sdb.CreateAccount(common.Idx(1), accounts[1]) // check that can not be created twice _, err = sdb.CreateAccount(common.Idx(1), accounts[1]) // check that can not be created twice
assert.NotNil(t, err) assert.NotNil(t, err)
@ -155,7 +158,8 @@ func TestStateDBWithoutMT(t *testing.T) {
// update accounts // update accounts
for i := 0; i < len(accounts); i++ { for i := 0; i < len(accounts); i++ {
accounts[i].Nonce = accounts[i].Nonce + 1 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) assert.Nil(t, err)
} }
@ -178,7 +182,8 @@ func TestStateDBWithMT(t *testing.T) {
} }
// get non-existing account, expecting an error // 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.NotNil(t, err)
assert.Equal(t, db.ErrNotFound, err) assert.Equal(t, db.ErrNotFound, err)
@ -189,13 +194,15 @@ func TestStateDBWithMT(t *testing.T) {
} }
for i := 0; i < len(accounts); i++ { 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.Nil(t, err)
assert.Equal(t, accounts[i], accGetted) assert.Equal(t, accounts[i], accGetted)
} }
// try already existing idx and get error // 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) assert.Nil(t, err)
_, err = sdb.CreateAccount(common.Idx(1), accounts[1]) // check that can not be created twice _, err = sdb.CreateAccount(common.Idx(1), accounts[1]) // check that can not be created twice
assert.NotNil(t, err) assert.NotNil(t, err)
@ -207,10 +214,12 @@ func TestStateDBWithMT(t *testing.T) {
// update accounts // update accounts
for i := 0; i < len(accounts); i++ { for i := 0; i < len(accounts); i++ {
accounts[i].Nonce = accounts[i].Nonce + 1 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) 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.Nil(t, err)
assert.Equal(t, accounts[1].Nonce, a.Nonce) 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 { if s.zki != nil {
// Txs // Txs
// s.zki.TxCompressedData[s.i] = tx.TxCompressedData() // uncomment once L1Tx.TxCompressedData is ready // 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.ToIdx[s.i] = tx.ToIdx.BigInt()
s.zki.OnChain[s.i] = big.NewInt(1) 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 { if err != nil {
return nil, nil, false, err return nil, nil, false, err
} }
return &tx.FromIdx, exitAccount, newExit, nil
return tx.FromIdx, exitAccount, newExit, nil
default: default:
} }
@ -307,7 +311,7 @@ func (s *StateDB) processL2Tx(exitTree *merkletree.MerkleTree, tx *common.PoolL2
var err error var err error
var auxToIdx common.Idx var auxToIdx common.Idx
// if tx.ToIdx==0, get toIdx by ToEthAddr or ToBJJ // 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) auxToIdx, err = s.GetIdxByEthAddrBJJ(tx.ToEthAddr, tx.ToBJJ)
if err != nil { if err != nil {
log.Error(err) 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() s.zki.ToIdx[s.i] = tx.ToIdx.BigInt()
// fill AuxToIdx if needed // 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 // use toIdx that can have been filled by tx.ToIdx or
// if tx.Idx==0 (this case), toIdx is filled by the Idx // if tx.Idx==0 (this case), toIdx is filled by the Idx
// from db by ToEthAddr&ToBJJ // 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.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.OnChain[s.i] = big.NewInt(0)
s.zki.NewAccount[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: case common.TxTypeTransfer:
// go to the MT account of sender and receiver, and update // go to the MT account of sender and receiver, and update
// balance & nonce // 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 { if err != nil {
return nil, nil, false, err return nil, nil, false, err
} }
case common.TxTypeExit: case common.TxTypeExit:
// execute exit flow // 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 { if err != nil {
return nil, nil, false, err 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 // in case that the tx is a L1Tx>DepositTransfer
var accReceiver *common.Account var accReceiver *common.Account
if transfer { if transfer {
accReceiver, err = s.GetAccount(tx.ToIdx)
accReceiver, err = s.GetAccount(&tx.ToIdx)
if err != nil { if err != nil {
return err return err
} }
@ -458,7 +475,7 @@ func (s *StateDB) applyDeposit(tx *common.L1Tx, transfer bool) error {
// this is done after updating Sender Account (depositer) // this is done after updating Sender Account (depositer)
if transfer { if transfer {
// update receiver account in localStateDB // update receiver account in localStateDB
p, err := s.UpdateAccount(tx.ToIdx, accReceiver)
p, err := s.UpdateAccount(&tx.ToIdx, accReceiver)
if err != nil { if err != nil {
return err 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. // the real ToIdx is found trhrough the ToEthAddr or ToBJJ.
func (s *StateDB) applyTransfer(tx *common.Tx, auxToIdx common.Idx) error { func (s *StateDB) applyTransfer(tx *common.Tx, auxToIdx common.Idx) error {
if auxToIdx == 0 { 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 // get sender and receiver accounts from localStateDB
accSender, err := s.GetAccount(tx.FromIdx) accSender, err := s.GetAccount(tx.FromIdx)
if err != nil { if err != nil {
return err return err
} }
accReceiver, err := s.GetAccount(auxToIdx)
accReceiver, err := s.GetAccount(&auxToIdx)
if err != nil { if err != nil {
return err return err
} }
@ -525,7 +545,7 @@ func (s *StateDB) applyTransfer(tx *common.Tx, auxToIdx common.Idx) error {
} }
// update receiver account in localStateDB // update receiver account in localStateDB
pReceiver, err := s.UpdateAccount(auxToIdx, accReceiver)
pReceiver, err := s.UpdateAccount(&auxToIdx, accReceiver)
if err != nil { if err != nil {
return err return err
} }
@ -555,7 +575,7 @@ func (s *StateDB) applyCreateAccountDepositTransfer(tx *common.L1Tx) error {
EthAddr: tx.FromEthAddr, EthAddr: tx.FromEthAddr,
} }
accSender.Balance = new(big.Int).Add(accSender.Balance, tx.LoadAmount) 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 { if err != nil {
return err return err
} }
@ -587,7 +607,7 @@ func (s *StateDB) applyCreateAccountDepositTransfer(tx *common.L1Tx) error {
} }
// update receiver account in localStateDB // update receiver account in localStateDB
p, err = s.UpdateAccount(tx.ToIdx, accReceiver)
p, err = s.UpdateAccount(&tx.ToIdx, accReceiver)
if err != nil { if err != nil {
return err return err
} }

+ 10
- 7
db/statedb/txprocessors_test.go

@ -40,7 +40,8 @@ func TestProcessTxs(t *testing.T) {
require.Nil(t, err) 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.Nil(t, err)
assert.Equal(t, "23", acc.Balance.String()) 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 // Nonce & TokenID =0, after ProcessTxs call has the expected value
assert.Equal(t, 0, len(exitInfos)) 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.Nil(t, err)
assert.Equal(t, "28", acc.Balance.String()) 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]) _, exitInfos, err = sdb.ProcessTxs(l1Txs[1], coordinatorL1Txs[1], poolL2Txs[1])
require.Nil(t, err) require.Nil(t, err)
assert.Equal(t, 5, len(exitInfos)) assert.Equal(t, 5, len(exitInfos))
acc, err = sdb.GetAccount(common.Idx(256))
acc, err = sdb.GetAccount(&accountIdx)
require.Nil(t, err) require.Nil(t, err)
assert.Equal(t, "48", acc.Balance.String()) 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]) _, exitInfos, err = sdb.ProcessTxs(l1Txs[2], coordinatorL1Txs[2], poolL2Txs[2])
require.Nil(t, err) require.Nil(t, err)
assert.Equal(t, 1, len(exitInfos)) assert.Equal(t, 1, len(exitInfos))
acc, err = sdb.GetAccount(common.Idx(256))
acc, err = sdb.GetAccount(&accountIdx)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "23", acc.Balance.String()) 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]) _, exitInfos, err := sdb.ProcessTxs(l1Txs[0], coordinatorL1Txs[0], poolL2Txs[0])
require.Nil(t, err) require.Nil(t, err)
assert.Equal(t, 0, len(exitInfos)) 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.Nil(t, err)
assert.Equal(t, "28", acc.Balance.String()) 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]) _, exitInfos, err = sdb.ProcessTxs(l1Txs[1], coordinatorL1Txs[1], poolL2Txs[1])
require.Nil(t, err) require.Nil(t, err)
assert.Equal(t, 5, len(exitInfos)) assert.Equal(t, 5, len(exitInfos))
acc, err = sdb.GetAccount(common.Idx(256))
acc, err = sdb.GetAccount(&accountIdx)
require.Nil(t, err) require.Nil(t, err)
assert.Equal(t, "48", acc.Balance.String()) 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]) _, exitInfos, err = sdb.ProcessTxs(l1Txs[2], coordinatorL1Txs[2], poolL2Txs[2])
require.Nil(t, err) require.Nil(t, err)
assert.Equal(t, 1, len(exitInfos)) assert.Equal(t, 1, len(exitInfos))
acc, err = sdb.GetAccount(common.Idx(256))
acc, err = sdb.GetAccount(&accountIdx)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "23", acc.Balance.String()) assert.Equal(t, "23", acc.Balance.String())
} }

+ 11
- 4
db/statedb/utils.go

@ -2,6 +2,7 @@ package statedb
import ( import (
"bytes" "bytes"
"errors"
"math/big" "math/big"
ethCommon "github.com/ethereum/go-ethereum/common" ethCommon "github.com/ethereum/go-ethereum/common"
@ -11,7 +12,7 @@ import (
"github.com/iden3/go-merkletree" "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() pkComp := pk.Compress()
var b []byte var b []byte
b = append(b, addr.Bytes()...) 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 // - key: EthAddr & BabyJubJub PublicKey Compressed, value: idx
// If Idx already exist for the given EthAddr & BJJ, the remaining Idx will be // If Idx already exist for the given EthAddr & BJJ, the remaining Idx will be
// always the smallest one. // 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) oldIdx, err := s.GetIdxByEthAddrBJJ(addr, pk)
if err == nil { if err == nil {
// EthAddr & BJJ already have an Idx // 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 // 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 // Ethereum Address. Will return common.Idx(0) and error in case that Idx is
// not found in the StateDB. // 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()) b, err := s.db.Get(addr.Bytes())
if err != nil { if err != nil {
return common.Idx(0), ErrToIdxNotFound 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 // 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 // query. Will return common.Idx(0) and error in case that Idx is not found in
// the StateDB. // 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 { if !bytes.Equal(addr.Bytes(), common.EmptyAddr.Bytes()) && pk == nil {
// case ToEthAddr!=0 && ToBJJ=0 // case ToEthAddr!=0 && ToBJJ=0
return s.GetIdxByEthAddr(addr) return s.GetIdxByEthAddr(addr)

+ 11
- 11
db/statedb/utils_test.go

@ -32,47 +32,47 @@ func TestGetIdx(t *testing.T) {
idx3 := common.Idx(1233) idx3 := common.Idx(1233)
// store the keys for idx by Addr & BJJ // store the keys for idx by Addr & BJJ
err = sdb.setIdxByEthAddrBJJ(idx, addr, pk)
err = sdb.setIdxByEthAddrBJJ(idx, &addr, pk)
require.Nil(t, err) require.Nil(t, err)
idxR, err := sdb.GetIdxByEthAddrBJJ(addr, pk)
idxR, err := sdb.GetIdxByEthAddrBJJ(&addr, pk)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, idx, idxR) assert.Equal(t, idx, idxR)
// expect error when getting only by EthAddr, as value does not exist // expect error when getting only by EthAddr, as value does not exist
// in the db for only EthAddr // in the db for only EthAddr
_, err = sdb.GetIdxByEthAddr(addr)
_, err = sdb.GetIdxByEthAddr(&addr)
assert.Nil(t, err) assert.Nil(t, err)
_, err = sdb.GetIdxByEthAddr(addr2)
_, err = sdb.GetIdxByEthAddr(&addr2)
assert.NotNil(t, err) assert.NotNil(t, err)
// expect to fail // expect to fail
idxR, err = sdb.GetIdxByEthAddrBJJ(addr2, pk)
idxR, err = sdb.GetIdxByEthAddrBJJ(&addr2, pk)
assert.NotNil(t, err) assert.NotNil(t, err)
assert.Equal(t, common.Idx(0), idxR) assert.Equal(t, common.Idx(0), idxR)
idxR, err = sdb.GetIdxByEthAddrBJJ(addr, pk2)
idxR, err = sdb.GetIdxByEthAddrBJJ(&addr, pk2)
assert.NotNil(t, err) assert.NotNil(t, err)
assert.Equal(t, common.Idx(0), idxR) assert.Equal(t, common.Idx(0), idxR)
// try to store bigger idx, will not affect as already exist a smaller // try to store bigger idx, will not affect as already exist a smaller
// Idx for that Addr & BJJ // Idx for that Addr & BJJ
err = sdb.setIdxByEthAddrBJJ(idx2, addr, pk)
err = sdb.setIdxByEthAddrBJJ(idx2, &addr, pk)
assert.Nil(t, err) assert.Nil(t, err)
// store smaller idx // store smaller idx
err = sdb.setIdxByEthAddrBJJ(idx3, addr, pk)
err = sdb.setIdxByEthAddrBJJ(idx3, &addr, pk)
assert.Nil(t, err) assert.Nil(t, err)
idxR, err = sdb.GetIdxByEthAddrBJJ(addr, pk)
idxR, err = sdb.GetIdxByEthAddrBJJ(&addr, pk)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, idx3, idxR) assert.Equal(t, idx3, idxR)
// by EthAddr should work // by EthAddr should work
idxR, err = sdb.GetIdxByEthAddr(addr)
idxR, err = sdb.GetIdxByEthAddr(&addr)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, idx3, idxR) assert.Equal(t, idx3, idxR)
// expect error when trying to get Idx by addr2 & pk2 // 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.NotNil(t, err)
assert.Equal(t, ErrToIdxNotFound, err) assert.Equal(t, ErrToIdxNotFound, err)
assert.Equal(t, common.Idx(0), idxR) 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 // TODO: Replace GetLastL1TxsNum by GetNextL1TxsNum
nextForgeL1TxsNum := int64(0)
var nextForgeL1TxsNum int64
nextForgeL1TxsNumPtr, err := s.historyDB.GetLastL1TxsNum() nextForgeL1TxsNumPtr, err := s.historyDB.GetLastL1TxsNum()
if err != nil { if err != nil {
return nil, err return nil, err
} }
if nextForgeL1TxsNumPtr != nil { if nextForgeL1TxsNumPtr != nil {
nextForgeL1TxsNum = *nextForgeL1TxsNumPtr + 1 nextForgeL1TxsNum = *nextForgeL1TxsNumPtr + 1
} else {
nextForgeL1TxsNum = 0
} }
// Get newLastIdx that will be used to complete the accounts // 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 { if err != nil {
return nil, err return nil, err
} }
forgeL1TxsNum := int64(0)
forgeL1TxsNum := nextForgeL1TxsNum
// Check if this is a L1Batch to get L1 Tx from it // Check if this is a L1Batch to get L1 Tx from it
if forgeBatchArgs.L1Batch { if forgeBatchArgs.L1Batch {
forgeL1TxsNum = nextForgeL1TxsNum
// Get L1 User Txs from History DB // Get L1 User Txs from History DB
// TODO: Get L1TX from HistoryDB filtered by toforgeL1txNum & fromidx = 0 and // TODO: Get L1TX from HistoryDB filtered by toforgeL1txNum & fromidx = 0 and
// update batch number and add accounts to createdAccounts updating idx // 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 // Get L1 Coordinator Txs
for _, l1CoordinatorTx := range forgeBatchArgs.L1CoordinatorTxs { for _, l1CoordinatorTx := range forgeBatchArgs.L1CoordinatorTxs {
l1CoordinatorTx.Position = position l1CoordinatorTx.Position = position
l1CoordinatorTx.ToForgeL1TxsNum = nextForgeL1TxsNum
l1CoordinatorTx.ToForgeL1TxsNum = &forgeL1TxsNum
l1CoordinatorTx.UserOrigin = false l1CoordinatorTx.UserOrigin = false
l1CoordinatorTx.EthBlockNum = blockNum l1CoordinatorTx.EthBlockNum = blockNum
bn := new(common.BatchNum) bn := new(common.BatchNum)
@ -440,7 +440,10 @@ func (s *Synchronizer) rollupSync(blockNum int64) (*rollupData, error) {
return nil, err 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.l2Txs = append(batchData.l2Txs, l2Txs...)
batchData.exitTree = exitInfo batchData.exitTree = exitInfo
@ -454,7 +457,7 @@ func (s *Synchronizer) rollupSync(blockNum int64) (*rollupData, error) {
StateRoot: common.Hash(forgeBatchArgs.NewStRoot.Bytes()), StateRoot: common.Hash(forgeBatchArgs.NewStRoot.Bytes()),
NumAccounts: numAccounts, NumAccounts: numAccounts,
ExitRoot: common.Hash(forgeBatchArgs.NewExitRoot.Bytes()), ExitRoot: common.Hash(forgeBatchArgs.NewExitRoot.Bytes()),
ForgeL1TxsNum: forgeL1TxsNum,
ForgeL1TxsNum: &forgeL1TxsNum,
// SlotNum: TODO: Calculate once ethClient provides the info // calculate from blockNum + ethClient Constants // SlotNum: TODO: Calculate once ethClient provides the info // calculate from blockNum + ethClient Constants
} }
batchData.batch = batch batchData.batch = batch
@ -573,7 +576,9 @@ func getL1UserTx(l1UserTxEvents []eth.RollupEventL1UserTx, blockNum int64) ([]*c
for _, eL1UserTx := range l1UserTxEvents { for _, eL1UserTx := range l1UserTxEvents {
// Fill aditional Tx fields // 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.Position = eL1UserTx.Position
eL1UserTx.L1Tx.UserOrigin = true eL1UserTx.L1Tx.UserOrigin = true
eL1UserTx.L1Tx.EthBlockNum = blockNum 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() r.State.MapL1TxQueue[r.State.LastToForgeL1TxsNum] = eth.NewQueueStruct()
queue = r.State.MapL1TxQueue[r.State.LastToForgeL1TxsNum] 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") panic("l1Tx.FromIdx > r.State.CurrentIdx")
} }
if int(l1Tx.TokenID)+1 > len(r.State.TokenList) { 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++ { for i := 0; i < N; i++ {
keys[i] = genKeys(int64(i)) keys[i] = genKeys(int64(i))
l1UserTx := common.L1Tx{ l1UserTx := common.L1Tx{
FromIdx: common.Idx(0),
FromIdx: nil,
FromEthAddr: keys[i].Addr, FromEthAddr: keys[i].Addr,
FromBJJ: keys[i].BJJPublicKey, FromBJJ: keys[i].BJJPublicKey,
TokenID: common.TokenID(0), 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), SlotNum: common.SlotNum(i),
} }
if i%2 == 0 { if i%2 == 0 {
batch.ForgeL1TxsNum = int64(i)
toForge := new(int64)
*toForge = int64(i)
batch.ForgeL1TxsNum = toForge
} }
batches = append(batches, batch) batches = append(batches, batch)
} }
@ -155,7 +157,7 @@ func GenL1Txs(
panic(err) panic(err)
} }
tx = *nTx tx = *nTx
if batches[i%len(batches)].ForgeL1TxsNum != 0 {
if batches[i%len(batches)].ForgeL1TxsNum != nil {
// Add already forged txs // Add already forged txs
tx.BatchNum = &batches[i%len(batches)].BatchNum tx.BatchNum = &batches[i%len(batches)].BatchNum
setFromToAndAppend(fromIdx, tx, i, nUserTxs, userAddr, accounts, &userTxs, &othersTxs) 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 // 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 batchNum := batches[len(batches)-1].BatchNum + 1
var toForgeL1TxsNum int64
toForgeL1TxsNum := new(int64)
found := false found := false
for i := len(batches) - 1; i >= 0; i-- { 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 found = true
break break
} }
@ -218,7 +220,9 @@ func setFromToAndAppend(
panic(err) panic(err)
} }
} }
tx.FromIdx = from.Idx
fromIdx := new(common.Idx)
*fromIdx = from.Idx
tx.FromIdx = fromIdx
tx.FromEthAddr = from.EthAddr tx.FromEthAddr = from.EthAddr
tx.FromBJJ = from.PublicKey tx.FromBJJ = from.PublicKey
tx.ToIdx = to.Idx tx.ToIdx = to.Idx
@ -232,7 +236,9 @@ func setFromToAndAppend(
if err != nil { if err != nil {
panic(err) panic(err)
} }
tx.FromIdx = from.Idx
fromIdx := new(common.Idx)
*fromIdx = from.Idx
tx.FromIdx = fromIdx
tx.FromEthAddr = from.EthAddr tx.FromEthAddr = from.EthAddr
tx.FromBJJ = from.PublicKey tx.FromBJJ = from.PublicKey
tx.ToIdx = to.Idx tx.ToIdx = to.Idx
@ -435,9 +441,6 @@ func randomTxType(seed int) common.TxType {
case 0: case 0:
return common.TxTypeExit return common.TxTypeExit
//nolint:gomnd //nolint:gomnd
case 1:
return common.TxTypeWithdrawn
//nolint:gomnd
case 2: case 2:
return common.TxTypeTransfer return common.TxTypeTransfer
//nolint:gomnd //nolint:gomnd

+ 27
- 10
test/l2db.go

@ -53,10 +53,14 @@ func GenPoolTxs(n int, tokens []common.Token) []*common.PoolL2Tx {
*usd = *token.USD * amountF *usd = *token.USD * amountF
*absFee = fee.Percentage() * *usd *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{ tx := &common.PoolL2Tx{
FromIdx: common.Idx(i), FromIdx: common.Idx(i),
ToIdx: common.Idx(i + 1),
ToEthAddr: ethCommon.BigToAddress(big.NewInt(int64(i))),
ToIdx: toIdx,
ToEthAddr: toEthAddr,
ToBJJ: privK.Public(), ToBJJ: privK.Public(),
TokenID: token.TokenID, TokenID: token.TokenID,
Amount: big.NewInt(int64(i)), Amount: big.NewInt(int64(i)),
@ -67,7 +71,6 @@ func GenPoolTxs(n int, tokens []common.Token) []*common.PoolL2Tx {
State: state, State: state,
Signature: privK.SignPoseidon(big.NewInt(int64(i))), Signature: privK.SignPoseidon(big.NewInt(int64(i))),
Timestamp: time.Now().UTC(), Timestamp: time.Now().UTC(),
TokenSymbol: token.Symbol,
AbsoluteFee: absFee, AbsoluteFee: absFee,
AbsoluteFeeUpdate: token.USDUpdate, AbsoluteFeeUpdate: token.USDUpdate,
} }
@ -77,17 +80,31 @@ func GenPoolTxs(n int, tokens []common.Token) []*common.PoolL2Tx {
panic(err) panic(err)
} }
if i%2 == 0 { // Optional parameters: rq 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.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.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" 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) 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) batchCoordinatorL1Txs = append(batchCoordinatorL1Txs, &tx)
idx++ 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{ tx := common.PoolL2Tx{
FromIdx: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx, 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(), ToBJJ: accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(),
TokenID: inst.TokenID, TokenID: inst.TokenID,
Amount: big.NewInt(int64(inst.Amount)), 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, Nonce: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce,
State: common.PoolL2TxStatePending, State: common.PoolL2TxStatePending,
Timestamp: time.Now(), 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(), RqToBJJ: accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(),
Type: common.TxTypeTransfer, Type: common.TxTypeTransfer,
} }
@ -130,8 +135,10 @@ func GenerateTestTxs(t *testing.T, instructions Instructions) ([][]*common.L1Tx,
batchPoolL2Txs = append(batchPoolL2Txs, &tx) batchPoolL2Txs = append(batchPoolL2Txs, &tx)
case common.TxTypeExit, common.TxTypeForceExit: case common.TxTypeExit, common.TxTypeForceExit:
fromIdx := new(common.Idx)
*fromIdx = accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx
tx := common.L1Tx{ tx := common.L1Tx{
FromIdx: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx,
FromIdx: fromIdx,
ToIdx: common.Idx(1), // as is an Exit ToIdx: common.Idx(1), // as is an Exit
TokenID: inst.TokenID, TokenID: inst.TokenID,
Amount: big.NewInt(int64(inst.Amount)), Amount: big.NewInt(int64(inst.Amount)),

+ 1
- 1
test/txs_test.go

@ -50,7 +50,7 @@ func TestGenerateTestL2Txs(t *testing.T) {
// l2txs // l2txs
assert.Equal(t, common.TxTypeTransfer, l2txs[0][0].Type) assert.Equal(t, common.TxTypeTransfer, l2txs[0][0].Type)
assert.Equal(t, common.Idx(256), l2txs[0][0].FromIdx) 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"].BJJ.Public().String(), l2txs[0][0].ToBJJ.String())
assert.Equal(t, accounts["B1"].Addr.Hex(), l2txs[0][0].ToEthAddr.Hex()) assert.Equal(t, accounts["B1"].Addr.Hex(), l2txs[0][0].ToEthAddr.Hex())
assert.Equal(t, common.Nonce(0), l2txs[0][0].Nonce) 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 // discard the txs that don't have an Account in the AccountDB
var validTxs txs var validTxs txs
for _, tx := range l2TxsRaw { for _, tx := range l2TxsRaw {
_, err = txsel.localAccountsDB.GetAccount(tx.FromIdx)
_, err = txsel.localAccountsDB.GetAccount(&tx.FromIdx)
if err == nil { if err == nil {
// if FromIdx has an account into the AccountsDB // if FromIdx has an account into the AccountsDB
validTxs = append(validTxs, tx) 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 // in AccountCreationAuthDB, if so, tx is used and L1CoordinatorTx of
// CreateAccountAndDeposit is created. // CreateAccountAndDeposit is created.
for i := 0; i < len(l2TxsRaw); i++ { 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 checkAlreadyPendingToCreate(l1CoordinatorTxs, l2TxsRaw[i].ToEthAddr, l2TxsRaw[i].ToBJJ) {
// if L2Tx needs a new L1CoordinatorTx of CreateAccount type, // if L2Tx needs a new L1CoordinatorTx of CreateAccount type,
// and a previous L2Tx in the current process already created // 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 // coordinator can create a new account without
// L1Authorization, as ToEthAddr==0xff // L1Authorization, as ToEthAddr==0xff
// create L1CoordinatorTx for the accountCreation // create L1CoordinatorTx for the accountCreation
if l2TxsRaw[i].ToEthAddr == nil {
log.Warn("l2TxsRaw[i].ToEthAddr should not be nil")
continue
}
l1CoordinatorTx := &common.L1Tx{ l1CoordinatorTx := &common.L1Tx{
Position: positionL1, Position: positionL1,
UserOrigin: false, UserOrigin: false,
FromEthAddr: l2TxsRaw[i].ToEthAddr,
FromEthAddr: *l2TxsRaw[i].ToEthAddr,
FromBJJ: l2TxsRaw[i].ToBJJ, FromBJJ: l2TxsRaw[i].ToBJJ,
TokenID: l2TxsRaw[i].TokenID, TokenID: l2TxsRaw[i].TokenID,
LoadAmount: big.NewInt(0), LoadAmount: big.NewInt(0),
@ -227,7 +222,16 @@ func (txsel *TxSelector) GetL1L2TxSelection(batchNum common.BatchNum, l1Txs []*c
positionL1++ positionL1++
l1CoordinatorTxs = append(l1CoordinatorTxs, l1CoordinatorTx) 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) // valid txs (of Exit type)
validTxs = append(validTxs, l2TxsRaw[i]) validTxs = append(validTxs, l2TxsRaw[i])
} }
@ -250,7 +254,11 @@ func (txsel *TxSelector) GetL1L2TxSelection(batchNum common.BatchNum, l1Txs []*c
return l1Txs, l1CoordinatorTxs, l2Txs, nil 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++ { for i := 0; i < len(l1CoordinatorTxs); i++ {
if bytes.Equal(l1CoordinatorTxs[i].FromEthAddr.Bytes(), addr.Bytes()) { if bytes.Equal(l1CoordinatorTxs[i].FromEthAddr.Bytes(), addr.Bytes()) {
if bjj == nil { if bjj == nil {

Loading…
Cancel
Save