You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

997 lines
33 KiB

Update coordinator, call all api update functions - Common: - Rename Block.EthBlockNum to Block.Num to avoid unneeded repetition - API: - Add UpdateNetworkInfoBlock to update just block information, to be used when the node is not yet synchronized - Node: - Call API.UpdateMetrics and UpdateRecommendedFee in a loop, with configurable time intervals - Synchronizer: - When mapping events by TxHash, use an array to support the possibility of multiple calls of the same function happening in the same transaction (for example, a smart contract in a single transaction could call withdraw with delay twice, which would generate 2 withdraw events, and 2 deposit events). - In Stats, keep entire LastBlock instead of just the blockNum - In Stats, add lastL1BatchBlock - Test Stats and SCVars - Coordinator: - Enable writing the BatchInfo in every step of the pipeline to disk (with JSON text files) for debugging purposes. - Move the Pipeline functionality from the Coordinator to its own struct (Pipeline) - Implement shouldL1lL2Batch - In TxManager, implement logic to perform several attempts when doing ethereum node RPC calls before considering the error. (Both for calls to forgeBatch and transaction receipt) - In TxManager, reorganize the flow and note the specific points in which actions are made when err != nil - HistoryDB: - Implement GetLastL1BatchBlockNum: returns the blockNum of the latest forged l1Batch, to help the coordinator decide when to forge an L1Batch. - EthereumClient and test.Client: - Update EthBlockByNumber to return the last block when the passed number is -1.
3 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update coordinator, call all api update functions - Common: - Rename Block.EthBlockNum to Block.Num to avoid unneeded repetition - API: - Add UpdateNetworkInfoBlock to update just block information, to be used when the node is not yet synchronized - Node: - Call API.UpdateMetrics and UpdateRecommendedFee in a loop, with configurable time intervals - Synchronizer: - When mapping events by TxHash, use an array to support the possibility of multiple calls of the same function happening in the same transaction (for example, a smart contract in a single transaction could call withdraw with delay twice, which would generate 2 withdraw events, and 2 deposit events). - In Stats, keep entire LastBlock instead of just the blockNum - In Stats, add lastL1BatchBlock - Test Stats and SCVars - Coordinator: - Enable writing the BatchInfo in every step of the pipeline to disk (with JSON text files) for debugging purposes. - Move the Pipeline functionality from the Coordinator to its own struct (Pipeline) - Implement shouldL1lL2Batch - In TxManager, implement logic to perform several attempts when doing ethereum node RPC calls before considering the error. (Both for calls to forgeBatch and transaction receipt) - In TxManager, reorganize the flow and note the specific points in which actions are made when err != nil - HistoryDB: - Implement GetLastL1BatchBlockNum: returns the blockNum of the latest forged l1Batch, to help the coordinator decide when to forge an L1Batch. - EthereumClient and test.Client: - Update EthBlockByNumber to return the last block when the passed number is -1.
3 years ago
  1. package til
  2. import (
  3. "crypto/ecdsa"
  4. "encoding/binary"
  5. "fmt"
  6. "math/big"
  7. "strings"
  8. "time"
  9. ethCommon "github.com/ethereum/go-ethereum/common"
  10. ethCrypto "github.com/ethereum/go-ethereum/crypto"
  11. "github.com/hermeznetwork/hermez-node/common"
  12. "github.com/hermeznetwork/hermez-node/log"
  13. "github.com/hermeznetwork/tracerr"
  14. "github.com/iden3/go-iden3-crypto/babyjub"
  15. )
  16. func newBatchData(batchNum int) common.BatchData {
  17. return common.BatchData{
  18. L1CoordinatorTxs: []common.L1Tx{},
  19. L2Txs: []common.L2Tx{},
  20. Batch: common.Batch{
  21. BatchNum: common.BatchNum(batchNum),
  22. StateRoot: big.NewInt(0), ExitRoot: big.NewInt(0),
  23. FeeIdxsCoordinator: make([]common.Idx, 0),
  24. CollectedFees: make(map[common.TokenID]*big.Int),
  25. },
  26. }
  27. }
  28. func newBlock(blockNum int64) common.BlockData {
  29. return common.BlockData{
  30. Block: common.Block{
  31. Num: blockNum,
  32. },
  33. Rollup: common.RollupData{
  34. L1UserTxs: []common.L1Tx{},
  35. },
  36. }
  37. }
  38. type contextExtra struct {
  39. openToForge int64
  40. toForgeL1TxsNum int64
  41. nonces map[common.Idx]common.Nonce
  42. idx int
  43. idxByTxID map[common.TxID]common.Idx
  44. }
  45. // Context contains the data of the test
  46. type Context struct {
  47. instructions []Instruction
  48. userNames []string
  49. Users map[string]*User // Name -> *User
  50. UsersByIdx map[int]*User
  51. accountsByIdx map[int]*Account
  52. LastRegisteredTokenID common.TokenID
  53. l1CreatedAccounts map[string]*Account // (Name, TokenID) -> *Account
  54. // rollupConstMaxL1UserTx Maximum L1-user transactions allowed to be
  55. // queued in a batch
  56. rollupConstMaxL1UserTx int
  57. chainID uint16
  58. idx int
  59. currBlock common.BlockData
  60. currBatch common.BatchData
  61. currBatchNum int
  62. Queues [][]L1Tx
  63. ToForgeNum int
  64. openToForge int
  65. currBatchTest struct {
  66. l1CoordinatorTxs []L1Tx
  67. l2Txs []L2Tx
  68. }
  69. blockNum int64
  70. extra contextExtra
  71. }
  72. // NewContext returns a new Context
  73. func NewContext(chainID uint16, rollupConstMaxL1UserTx int) *Context {
  74. currBatchNum := 1 // The protocol defines the first batchNum to be 1
  75. return &Context{
  76. Users: make(map[string]*User),
  77. l1CreatedAccounts: make(map[string]*Account),
  78. UsersByIdx: make(map[int]*User),
  79. accountsByIdx: make(map[int]*Account),
  80. LastRegisteredTokenID: 0,
  81. rollupConstMaxL1UserTx: rollupConstMaxL1UserTx,
  82. chainID: chainID,
  83. idx: common.UserThreshold,
  84. // We use some placeholder values for StateRoot and ExitTree
  85. // because these values will never be nil
  86. currBlock: newBlock(2), //nolint:gomnd
  87. currBatch: newBatchData(currBatchNum),
  88. currBatchNum: currBatchNum,
  89. // start with 2 queues, one for toForge, and the other for openToForge
  90. Queues: make([][]L1Tx, 2),
  91. ToForgeNum: 0,
  92. openToForge: 1,
  93. //nolint:gomnd
  94. blockNum: 2, // rollup genesis blockNum
  95. extra: contextExtra{
  96. openToForge: 0,
  97. toForgeL1TxsNum: 0,
  98. nonces: make(map[common.Idx]common.Nonce),
  99. idx: common.UserThreshold,
  100. idxByTxID: make(map[common.TxID]common.Idx),
  101. },
  102. }
  103. }
  104. // Account contains the data related to the account for a specific TokenID of a User
  105. type Account struct {
  106. Idx common.Idx
  107. TokenID common.TokenID
  108. Nonce common.Nonce
  109. BatchNum int
  110. }
  111. // User contains the data related to a testing user
  112. type User struct {
  113. Name string
  114. BJJ *babyjub.PrivateKey
  115. EthSk *ecdsa.PrivateKey
  116. Addr ethCommon.Address
  117. Accounts map[common.TokenID]*Account
  118. }
  119. // L1Tx is the data structure used internally for transaction test generation,
  120. // which contains a common.L1Tx data plus some intermediate data for the
  121. // transaction generation.
  122. type L1Tx struct {
  123. lineNum int
  124. fromIdxName string
  125. toIdxName string
  126. L1Tx common.L1Tx
  127. }
  128. // L2Tx is the data structure used internally for transaction test generation,
  129. // which contains a common.L2Tx data plus some intermediate data for the
  130. // transaction generation.
  131. type L2Tx struct {
  132. lineNum int
  133. fromIdxName string
  134. toIdxName string
  135. tokenID common.TokenID
  136. L2Tx common.L2Tx
  137. }
  138. // GenerateBlocks returns an array of BlockData for a given set made of a
  139. // string. It uses the users (keys & nonces) of the Context.
  140. func (tc *Context) GenerateBlocks(set string) ([]common.BlockData, error) {
  141. parser := newParser(strings.NewReader(set))
  142. parsedSet, err := parser.parse()
  143. if err != nil {
  144. return nil, tracerr.Wrap(err)
  145. }
  146. if parsedSet.typ != SetTypeBlockchain {
  147. return nil, tracerr.Wrap(fmt.Errorf("Expected set type: %s, found: %s", SetTypeBlockchain, parsedSet.typ))
  148. }
  149. tc.instructions = parsedSet.instructions
  150. tc.userNames = parsedSet.users
  151. return tc.generateBlocks()
  152. }
  153. // GenerateBlocksFromInstructions returns an array of BlockData for a given set
  154. // made of instructions. It uses the users (keys & nonces) of the Context.
  155. func (tc *Context) GenerateBlocksFromInstructions(set []Instruction) ([]common.BlockData, error) {
  156. userNames := []string{}
  157. addedNames := make(map[string]bool)
  158. for _, inst := range set {
  159. if _, ok := addedNames[inst.From]; !ok {
  160. // If the name wasn't already added
  161. userNames = append(userNames, inst.From)
  162. addedNames[inst.From] = true
  163. }
  164. if _, ok := addedNames[inst.To]; !ok {
  165. // If the name wasn't already added
  166. userNames = append(userNames, inst.To)
  167. addedNames[inst.To] = true
  168. }
  169. }
  170. tc.userNames = userNames
  171. tc.instructions = set
  172. return tc.generateBlocks()
  173. }
  174. func (tc *Context) generateBlocks() ([]common.BlockData, error) {
  175. tc.generateKeys(tc.userNames)
  176. var blocks []common.BlockData
  177. for _, inst := range tc.instructions {
  178. switch inst.Typ {
  179. case TxTypeCreateAccountDepositCoordinator: // tx source: L1CoordinatorTx
  180. if err := tc.checkIfTokenIsRegistered(inst); err != nil {
  181. log.Error(err)
  182. return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error()))
  183. }
  184. tx := common.L1Tx{
  185. FromEthAddr: tc.Users[inst.From].Addr,
  186. FromBJJ: tc.Users[inst.From].BJJ.Public().Compress(),
  187. TokenID: inst.TokenID,
  188. Amount: big.NewInt(0),
  189. DepositAmount: big.NewInt(0),
  190. Type: common.TxTypeCreateAccountDeposit, // as TxTypeCreateAccountDepositCoordinator is not valid oustide Til package
  191. }
  192. testTx := L1Tx{
  193. lineNum: inst.LineNum,
  194. fromIdxName: inst.From,
  195. L1Tx: tx,
  196. }
  197. tc.currBatchTest.l1CoordinatorTxs = append(tc.currBatchTest.l1CoordinatorTxs, testTx)
  198. case common.TxTypeCreateAccountDeposit, common.TxTypeCreateAccountDepositTransfer: // tx source: L1UserTx
  199. if err := tc.checkIfTokenIsRegistered(inst); err != nil {
  200. log.Error(err)
  201. return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error()))
  202. }
  203. tx := common.L1Tx{
  204. FromEthAddr: tc.Users[inst.From].Addr,
  205. FromBJJ: tc.Users[inst.From].BJJ.Public().Compress(),
  206. TokenID: inst.TokenID,
  207. Amount: big.NewInt(0),
  208. DepositAmount: inst.DepositAmount,
  209. Type: inst.Typ,
  210. }
  211. if inst.Typ == common.TxTypeCreateAccountDepositTransfer {
  212. tx.Amount = inst.Amount
  213. }
  214. testTx := L1Tx{
  215. lineNum: inst.LineNum,
  216. fromIdxName: inst.From,
  217. toIdxName: inst.To,
  218. L1Tx: tx,
  219. }
  220. if err := tc.addToL1UserQueue(testTx); err != nil {
  221. return nil, tracerr.Wrap(err)
  222. }
  223. case common.TxTypeDeposit, common.TxTypeDepositTransfer: // tx source: L1UserTx
  224. if err := tc.checkIfTokenIsRegistered(inst); err != nil {
  225. log.Error(err)
  226. return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error()))
  227. }
  228. if err := tc.checkIfAccountExists(inst.From, inst); err != nil {
  229. log.Error(err)
  230. return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error()))
  231. }
  232. tx := common.L1Tx{
  233. TokenID: inst.TokenID,
  234. Amount: big.NewInt(0),
  235. DepositAmount: inst.DepositAmount,
  236. Type: inst.Typ,
  237. }
  238. if inst.Typ == common.TxTypeDepositTransfer {
  239. tx.Amount = inst.Amount
  240. }
  241. testTx := L1Tx{
  242. lineNum: inst.LineNum,
  243. fromIdxName: inst.From,
  244. toIdxName: inst.To,
  245. L1Tx: tx,
  246. }
  247. if err := tc.addToL1UserQueue(testTx); err != nil {
  248. return nil, tracerr.Wrap(err)
  249. }
  250. case common.TxTypeTransfer: // L2Tx
  251. if err := tc.checkIfTokenIsRegistered(inst); err != nil {
  252. log.Error(err)
  253. return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error()))
  254. }
  255. tx := common.L2Tx{
  256. Amount: inst.Amount,
  257. Fee: common.FeeSelector(inst.Fee),
  258. Type: common.TxTypeTransfer,
  259. EthBlockNum: tc.blockNum,
  260. }
  261. tx.BatchNum = common.BatchNum(tc.currBatchNum) // when converted to PoolL2Tx BatchNum parameter is lost
  262. testTx := L2Tx{
  263. lineNum: inst.LineNum,
  264. fromIdxName: inst.From,
  265. toIdxName: inst.To,
  266. tokenID: inst.TokenID,
  267. L2Tx: tx,
  268. }
  269. tc.currBatchTest.l2Txs = append(tc.currBatchTest.l2Txs, testTx)
  270. case common.TxTypeForceTransfer: // tx source: L1UserTx
  271. if err := tc.checkIfTokenIsRegistered(inst); err != nil {
  272. log.Error(err)
  273. return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error()))
  274. }
  275. tx := common.L1Tx{
  276. TokenID: inst.TokenID,
  277. Amount: inst.Amount,
  278. DepositAmount: big.NewInt(0),
  279. Type: common.TxTypeForceTransfer,
  280. }
  281. testTx := L1Tx{
  282. lineNum: inst.LineNum,
  283. fromIdxName: inst.From,
  284. toIdxName: inst.To,
  285. L1Tx: tx,
  286. }
  287. if err := tc.addToL1UserQueue(testTx); err != nil {
  288. return nil, tracerr.Wrap(err)
  289. }
  290. case common.TxTypeExit: // tx source: L2Tx
  291. if err := tc.checkIfTokenIsRegistered(inst); err != nil {
  292. log.Error(err)
  293. return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error()))
  294. }
  295. tx := common.L2Tx{
  296. ToIdx: common.Idx(1), // as is an Exit
  297. Fee: common.FeeSelector(inst.Fee),
  298. Amount: inst.Amount,
  299. Type: common.TxTypeExit,
  300. EthBlockNum: tc.blockNum,
  301. }
  302. tx.BatchNum = common.BatchNum(tc.currBatchNum) // when converted to PoolL2Tx BatchNum parameter is lost
  303. testTx := L2Tx{
  304. lineNum: inst.LineNum,
  305. fromIdxName: inst.From,
  306. toIdxName: inst.To,
  307. tokenID: inst.TokenID,
  308. L2Tx: tx,
  309. }
  310. tc.currBatchTest.l2Txs = append(tc.currBatchTest.l2Txs, testTx)
  311. case common.TxTypeForceExit: // tx source: L1UserTx
  312. if err := tc.checkIfTokenIsRegistered(inst); err != nil {
  313. log.Error(err)
  314. return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error()))
  315. }
  316. tx := common.L1Tx{
  317. ToIdx: common.Idx(1), // as is an Exit
  318. TokenID: inst.TokenID,
  319. Amount: inst.Amount,
  320. DepositAmount: big.NewInt(0),
  321. Type: common.TxTypeForceExit,
  322. }
  323. testTx := L1Tx{
  324. lineNum: inst.LineNum,
  325. fromIdxName: inst.From,
  326. toIdxName: inst.To,
  327. L1Tx: tx,
  328. }
  329. if err := tc.addToL1UserQueue(testTx); err != nil {
  330. return nil, tracerr.Wrap(err)
  331. }
  332. case TypeNewBatch:
  333. if err := tc.calculateIdxForL1Txs(true, tc.currBatchTest.l1CoordinatorTxs); err != nil {
  334. return nil, tracerr.Wrap(err)
  335. }
  336. if err := tc.setIdxs(); err != nil {
  337. log.Error(err)
  338. return nil, tracerr.Wrap(err)
  339. }
  340. case TypeNewBatchL1:
  341. // for each L1UserTx of the Queues[ToForgeNum], calculate the Idx
  342. if err := tc.calculateIdxForL1Txs(false, tc.Queues[tc.ToForgeNum]); err != nil {
  343. return nil, tracerr.Wrap(err)
  344. }
  345. if err := tc.calculateIdxForL1Txs(true, tc.currBatchTest.l1CoordinatorTxs); err != nil {
  346. return nil, tracerr.Wrap(err)
  347. }
  348. tc.currBatch.L1Batch = true
  349. if err := tc.setIdxs(); err != nil {
  350. return nil, tracerr.Wrap(err)
  351. }
  352. toForgeL1TxsNum := int64(tc.openToForge)
  353. tc.currBatch.Batch.ForgeL1TxsNum = &toForgeL1TxsNum
  354. // advance batch
  355. tc.ToForgeNum++
  356. if tc.ToForgeNum == tc.openToForge {
  357. tc.openToForge++
  358. newQueue := []L1Tx{}
  359. tc.Queues = append(tc.Queues, newQueue)
  360. }
  361. case TypeNewBlock:
  362. blocks = append(blocks, tc.currBlock)
  363. tc.blockNum++
  364. tc.currBlock = newBlock(tc.blockNum)
  365. case TypeAddToken:
  366. newToken := common.Token{
  367. EthAddr: ethCommon.BigToAddress(big.NewInt(int64(inst.TokenID * 100))), //nolint:gomnd
  368. // Name: fmt.Sprintf("Token %d", inst.TokenID),
  369. // Symbol: fmt.Sprintf("TK%d", inst.TokenID),
  370. // Decimals: 18,
  371. TokenID: inst.TokenID,
  372. EthBlockNum: tc.blockNum,
  373. }
  374. if inst.TokenID != tc.LastRegisteredTokenID+1 {
  375. return nil, tracerr.Wrap(fmt.Errorf("Line %d: AddToken TokenID should be sequential, expected TokenID: %d, defined TokenID: %d", inst.LineNum, tc.LastRegisteredTokenID+1, inst.TokenID))
  376. }
  377. tc.LastRegisteredTokenID++
  378. tc.currBlock.Rollup.AddedTokens = append(tc.currBlock.Rollup.AddedTokens, newToken)
  379. default:
  380. return nil, tracerr.Wrap(fmt.Errorf("Line %d: Unexpected type: %s", inst.LineNum, inst.Typ))
  381. }
  382. }
  383. return blocks, nil
  384. }
  385. // calculateIdxsForL1Txs calculates new Idx for new created accounts. If
  386. // 'isCoordinatorTxs==true', adds the tx to tc.currBatch.L1CoordinatorTxs.
  387. func (tc *Context) calculateIdxForL1Txs(isCoordinatorTxs bool, txs []L1Tx) error {
  388. // for each batch.L1CoordinatorTxs of the Queues[ToForgeNum], calculate the Idx
  389. for i := 0; i < len(txs); i++ {
  390. tx := txs[i]
  391. if tx.L1Tx.Type == common.TxTypeCreateAccountDeposit || tx.L1Tx.Type == common.TxTypeCreateAccountDepositTransfer {
  392. if tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID] != nil { // if account already exists, return error
  393. return tracerr.Wrap(fmt.Errorf("Can not create same account twice (same User (%s) & same TokenID (%d)) (this is a design property of Til)", tx.fromIdxName, tx.L1Tx.TokenID))
  394. }
  395. tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID] = &Account{
  396. Idx: common.Idx(tc.idx),
  397. TokenID: tx.L1Tx.TokenID,
  398. Nonce: common.Nonce(0),
  399. BatchNum: tc.currBatchNum,
  400. }
  401. tc.l1CreatedAccounts[idxTokenIDToString(tx.fromIdxName, tx.L1Tx.TokenID)] = tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID]
  402. tc.accountsByIdx[tc.idx] = tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID]
  403. tc.UsersByIdx[tc.idx] = tc.Users[tx.fromIdxName]
  404. tc.idx++
  405. }
  406. if isCoordinatorTxs {
  407. tc.currBatch.L1CoordinatorTxs = append(tc.currBatch.L1CoordinatorTxs, tx.L1Tx)
  408. }
  409. }
  410. return nil
  411. }
  412. // setIdxs sets the Idxs to the transactions of the tc.currBatch
  413. func (tc *Context) setIdxs() error {
  414. // once Idxs are calculated, update transactions to use the new Idxs
  415. for i := 0; i < len(tc.currBatchTest.l2Txs); i++ {
  416. testTx := &tc.currBatchTest.l2Txs[i]
  417. if tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID] == nil {
  418. return tracerr.Wrap(fmt.Errorf("Line %d: %s from User %s for TokenID %d while account not created yet", testTx.lineNum, testTx.L2Tx.Type, testTx.fromIdxName, testTx.tokenID))
  419. }
  420. if testTx.L2Tx.Type == common.TxTypeTransfer {
  421. if _, ok := tc.l1CreatedAccounts[idxTokenIDToString(testTx.toIdxName, testTx.tokenID)]; !ok {
  422. return tracerr.Wrap(fmt.Errorf("Line %d: Can not create Transfer for a non existing account. Batch %d, ToIdx name: %s, TokenID: %d", testTx.lineNum, tc.currBatchNum, testTx.toIdxName, testTx.tokenID))
  423. }
  424. }
  425. tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Nonce++
  426. // next line is commented to avoid Blockchain L2Txs to have
  427. // Nonce different from 0, as from Blockchain those
  428. // transactions will come without Nonce
  429. // testTx.L2Tx.Nonce = tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Nonce
  430. // set real Idx
  431. testTx.L2Tx.FromIdx = tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Idx
  432. if testTx.L2Tx.Type == common.TxTypeTransfer {
  433. testTx.L2Tx.ToIdx = tc.Users[testTx.toIdxName].Accounts[testTx.tokenID].Idx
  434. }
  435. // in case Type==Exit, ToIdx=1, already set at the
  436. // GenerateBlocks main switch inside TxTypeExit case
  437. nTx, err := common.NewL2Tx(&testTx.L2Tx)
  438. if err != nil {
  439. return tracerr.Wrap(fmt.Errorf("Line %d: %s", testTx.lineNum, err.Error()))
  440. }
  441. testTx.L2Tx = *nTx
  442. tc.currBatch.L2Txs = append(tc.currBatch.L2Txs, testTx.L2Tx)
  443. }
  444. tc.currBatch.Batch.LastIdx = int64(tc.idx - 1) // `-1` because tc.idx is the next available idx
  445. tc.currBlock.Rollup.Batches = append(tc.currBlock.Rollup.Batches, tc.currBatch)
  446. tc.currBatchNum++
  447. tc.currBatch = newBatchData(tc.currBatchNum)
  448. tc.currBatchTest.l1CoordinatorTxs = nil
  449. tc.currBatchTest.l2Txs = nil
  450. return nil
  451. }
  452. // addToL1UserQueue adds the L1UserTx into the queue that is open and has space
  453. func (tc *Context) addToL1UserQueue(tx L1Tx) error {
  454. if len(tc.Queues[tc.openToForge]) >= tc.rollupConstMaxL1UserTx {
  455. // if current OpenToForge queue reached its Max, move into a
  456. // new queue
  457. tc.openToForge++
  458. newQueue := []L1Tx{}
  459. tc.Queues = append(tc.Queues, newQueue)
  460. }
  461. // Fill L1UserTx specific parameters
  462. tx.L1Tx.UserOrigin = true
  463. toForgeL1TxsNum := int64(tc.openToForge)
  464. tx.L1Tx.ToForgeL1TxsNum = &toForgeL1TxsNum
  465. tx.L1Tx.EthBlockNum = tc.blockNum
  466. tx.L1Tx.Position = len(tc.Queues[tc.openToForge])
  467. // When an L1UserTx is generated, all idxs must be available (except when idx == 0 or idx == 1)
  468. if tx.L1Tx.Type != common.TxTypeCreateAccountDeposit && tx.L1Tx.Type != common.TxTypeCreateAccountDepositTransfer {
  469. tx.L1Tx.FromIdx = tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID].Idx
  470. }
  471. tx.L1Tx.FromEthAddr = tc.Users[tx.fromIdxName].Addr
  472. tx.L1Tx.FromBJJ = tc.Users[tx.fromIdxName].BJJ.Public().Compress()
  473. if tx.toIdxName == "" {
  474. tx.L1Tx.ToIdx = common.Idx(0)
  475. } else {
  476. account, ok := tc.Users[tx.toIdxName].Accounts[tx.L1Tx.TokenID]
  477. if !ok {
  478. return tracerr.Wrap(fmt.Errorf("Line %d: Transfer to User: %s, for TokenID: %d, "+
  479. "while account not created yet", tx.lineNum, tx.toIdxName, tx.L1Tx.TokenID))
  480. }
  481. tx.L1Tx.ToIdx = account.Idx
  482. }
  483. if tx.L1Tx.Type == common.TxTypeForceExit {
  484. tx.L1Tx.ToIdx = common.Idx(1)
  485. }
  486. nTx, err := common.NewL1Tx(&tx.L1Tx)
  487. if err != nil {
  488. return tracerr.Wrap(fmt.Errorf("Line %d: %s", tx.lineNum, err.Error()))
  489. }
  490. tx.L1Tx = *nTx
  491. tc.Queues[tc.openToForge] = append(tc.Queues[tc.openToForge], tx)
  492. tc.currBlock.Rollup.L1UserTxs = append(tc.currBlock.Rollup.L1UserTxs, tx.L1Tx)
  493. return nil
  494. }
  495. func (tc *Context) checkIfAccountExists(tf string, inst Instruction) error {
  496. if tc.Users[tf].Accounts[inst.TokenID] == nil {
  497. return tracerr.Wrap(fmt.Errorf("%s at User: %s, for TokenID: %d, while account not created yet", inst.Typ, tf, inst.TokenID))
  498. }
  499. return nil
  500. }
  501. func (tc *Context) checkIfTokenIsRegistered(inst Instruction) error {
  502. if inst.TokenID > tc.LastRegisteredTokenID {
  503. return tracerr.Wrap(fmt.Errorf("Can not process %s: TokenID %d not registered, last registered TokenID: %d", inst.Typ, inst.TokenID, tc.LastRegisteredTokenID))
  504. }
  505. return nil
  506. }
  507. // GeneratePoolL2Txs returns an array of common.PoolL2Tx from a given set made
  508. // of a string. It uses the users (keys) of the Context.
  509. func (tc *Context) GeneratePoolL2Txs(set string) ([]common.PoolL2Tx, error) {
  510. parser := newParser(strings.NewReader(set))
  511. parsedSet, err := parser.parse()
  512. if err != nil {
  513. return nil, tracerr.Wrap(err)
  514. }
  515. if parsedSet.typ != SetTypePoolL2 {
  516. return nil, tracerr.Wrap(fmt.Errorf("Expected set type: %s, found: %s", SetTypePoolL2, parsedSet.typ))
  517. }
  518. tc.instructions = parsedSet.instructions
  519. tc.userNames = parsedSet.users
  520. return tc.generatePoolL2Txs()
  521. }
  522. // GeneratePoolL2TxsFromInstructions returns an array of common.PoolL2Tx from a
  523. // given set made of instructions. It uses the users (keys) of the Context.
  524. func (tc *Context) GeneratePoolL2TxsFromInstructions(set []Instruction) ([]common.PoolL2Tx, error) {
  525. userNames := []string{}
  526. addedNames := make(map[string]bool)
  527. for _, inst := range set {
  528. if _, ok := addedNames[inst.From]; !ok {
  529. // If the name wasn't already added
  530. userNames = append(userNames, inst.From)
  531. addedNames[inst.From] = true
  532. }
  533. if _, ok := addedNames[inst.To]; !ok {
  534. // If the name wasn't already added
  535. userNames = append(userNames, inst.To)
  536. addedNames[inst.To] = true
  537. }
  538. }
  539. tc.userNames = userNames
  540. tc.instructions = set
  541. return tc.generatePoolL2Txs()
  542. }
  543. func (tc *Context) generatePoolL2Txs() ([]common.PoolL2Tx, error) {
  544. tc.generateKeys(tc.userNames)
  545. txs := []common.PoolL2Tx{}
  546. for _, inst := range tc.instructions {
  547. switch inst.Typ {
  548. case common.TxTypeTransfer, common.TxTypeTransferToEthAddr, common.TxTypeTransferToBJJ:
  549. if err := tc.checkIfAccountExists(inst.From, inst); err != nil {
  550. log.Error(err)
  551. return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error()))
  552. }
  553. if inst.Typ == common.TxTypeTransfer {
  554. // if TxTypeTransfer, need to exist the ToIdx account
  555. if err := tc.checkIfAccountExists(inst.To, inst); err != nil {
  556. log.Error(err)
  557. return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error()))
  558. }
  559. }
  560. // if account of receiver does not exist, don't use
  561. // ToIdx, and use only ToEthAddr & ToBJJ
  562. tx := common.PoolL2Tx{
  563. FromIdx: tc.Users[inst.From].Accounts[inst.TokenID].Idx,
  564. TokenID: inst.TokenID,
  565. Amount: inst.Amount,
  566. Fee: common.FeeSelector(inst.Fee),
  567. Nonce: tc.Users[inst.From].Accounts[inst.TokenID].Nonce,
  568. State: common.PoolL2TxStatePending,
  569. Timestamp: time.Now(),
  570. RqToEthAddr: common.EmptyAddr,
  571. RqToBJJ: common.EmptyBJJComp,
  572. Type: inst.Typ,
  573. }
  574. tc.Users[inst.From].Accounts[inst.TokenID].Nonce++
  575. if tx.Type == common.TxTypeTransfer {
  576. tx.ToIdx = tc.Users[inst.To].Accounts[inst.TokenID].Idx
  577. tx.ToEthAddr = tc.Users[inst.To].Addr
  578. tx.ToBJJ = tc.Users[inst.To].BJJ.Public().Compress()
  579. } else if tx.Type == common.TxTypeTransferToEthAddr {
  580. tx.ToIdx = common.Idx(0)
  581. tx.ToEthAddr = tc.Users[inst.To].Addr
  582. } else if tx.Type == common.TxTypeTransferToBJJ {
  583. tx.ToIdx = common.Idx(0)
  584. tx.ToEthAddr = common.FFAddr
  585. tx.ToBJJ = tc.Users[inst.To].BJJ.Public().Compress()
  586. }
  587. nTx, err := common.NewPoolL2Tx(&tx)
  588. if err != nil {
  589. return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error()))
  590. }
  591. tx = *nTx
  592. // perform signature and set it to tx.Signature
  593. toSign, err := tx.HashToSign(tc.chainID)
  594. if err != nil {
  595. return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error()))
  596. }
  597. sig := tc.Users[inst.From].BJJ.SignPoseidon(toSign)
  598. tx.Signature = sig.Compress()
  599. txs = append(txs, tx)
  600. case common.TxTypeExit:
  601. tx := common.PoolL2Tx{
  602. FromIdx: tc.Users[inst.From].Accounts[inst.TokenID].Idx,
  603. ToIdx: common.Idx(1), // as is an Exit
  604. Fee: common.FeeSelector(inst.Fee),
  605. TokenID: inst.TokenID,
  606. Amount: inst.Amount,
  607. Nonce: tc.Users[inst.From].Accounts[inst.TokenID].Nonce,
  608. State: common.PoolL2TxStatePending,
  609. Type: common.TxTypeExit,
  610. }
  611. tc.Users[inst.From].Accounts[inst.TokenID].Nonce++
  612. nTx, err := common.NewPoolL2Tx(&tx)
  613. if err != nil {
  614. return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error()))
  615. }
  616. tx = *nTx
  617. // perform signature and set it to tx.Signature
  618. toSign, err := tx.HashToSign(tc.chainID)
  619. if err != nil {
  620. return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error()))
  621. }
  622. sig := tc.Users[inst.From].BJJ.SignPoseidon(toSign)
  623. tx.Signature = sig.Compress()
  624. txs = append(txs, tx)
  625. default:
  626. return nil, tracerr.Wrap(fmt.Errorf("Line %d: instruction type unrecognized: %s", inst.LineNum, inst.Typ))
  627. }
  628. }
  629. return txs, nil
  630. }
  631. // RestartNonces sets all the Users.Accounts.Nonces to 0
  632. func (tc *Context) RestartNonces() {
  633. for name, user := range tc.Users {
  634. for tokenID := range user.Accounts {
  635. tc.Users[name].Accounts[tokenID].Nonce = common.Nonce(0)
  636. }
  637. }
  638. }
  639. // generateKeys generates BabyJubJub & Address keys for the given list of user
  640. // names in a deterministic way. This means, that for the same given
  641. // 'userNames' in a certain order, the keys will be always the same.
  642. func (tc *Context) generateKeys(userNames []string) {
  643. for i := 1; i < len(userNames)+1; i++ {
  644. if _, ok := tc.Users[userNames[i-1]]; ok {
  645. // account already created
  646. continue
  647. }
  648. // babyjubjub key
  649. var sk babyjub.PrivateKey
  650. var iBytes [8]byte
  651. binary.LittleEndian.PutUint64(iBytes[:], uint64(i))
  652. copy(sk[:], iBytes[:]) // only for testing
  653. // eth address
  654. var key ecdsa.PrivateKey
  655. key.D = big.NewInt(int64(i)) // only for testing
  656. key.PublicKey.X, key.PublicKey.Y = ethCrypto.S256().ScalarBaseMult(key.D.Bytes())
  657. key.Curve = ethCrypto.S256()
  658. addr := ethCrypto.PubkeyToAddress(key.PublicKey)
  659. u := User{
  660. Name: userNames[i-1],
  661. BJJ: &sk,
  662. EthSk: &key,
  663. Addr: addr,
  664. Accounts: make(map[common.TokenID]*Account),
  665. }
  666. tc.Users[userNames[i-1]] = &u
  667. }
  668. }
  669. // L1TxsToCommonL1Txs converts an array of []til.L1Tx to []common.L1Tx
  670. func L1TxsToCommonL1Txs(l1 []L1Tx) []common.L1Tx {
  671. var r []common.L1Tx
  672. for i := 0; i < len(l1); i++ {
  673. r = append(r, l1[i].L1Tx)
  674. }
  675. return r
  676. }
  677. // ConfigExtra is the configuration used in FillBlocksExtra to extend the
  678. // blocks returned by til.
  679. type ConfigExtra struct {
  680. // Address to set as forger for each batch
  681. BootCoordAddr ethCommon.Address
  682. // Coordinator user name used to select the corresponding accounts to
  683. // collect coordinator fees
  684. CoordUser string
  685. }
  686. // FillBlocksL1UserTxsBatchNum fills the BatchNum of forged L1UserTxs:
  687. // - blocks[].Rollup.L1UserTxs[].BatchNum
  688. func (tc *Context) FillBlocksL1UserTxsBatchNum(blocks []common.BlockData) {
  689. for i := range blocks {
  690. block := &blocks[i]
  691. for j := range block.Rollup.Batches {
  692. batch := &block.Rollup.Batches[j]
  693. if batch.L1Batch {
  694. // Set BatchNum for forged L1UserTxs to til blocks
  695. bn := batch.Batch.BatchNum
  696. for k := range blocks {
  697. block := &blocks[k]
  698. for l := range block.Rollup.L1UserTxs {
  699. tx := &block.Rollup.L1UserTxs[l]
  700. if *tx.ToForgeL1TxsNum == tc.extra.openToForge {
  701. tx.BatchNum = &bn
  702. }
  703. }
  704. }
  705. tc.extra.openToForge++
  706. }
  707. }
  708. }
  709. }
  710. // FillBlocksForgedL1UserTxs fills the L1UserTxs of a batch with the L1UserTxs
  711. // that are forged in that batch. It always sets `EffectiveAmount` = `Amount`
  712. // and `EffectiveDepositAmount` = `DepositAmount`. This function requires a
  713. // previous call to `FillBlocksExtra`.
  714. // - blocks[].Rollup.L1UserTxs[].BatchNum
  715. // - blocks[].Rollup.L1UserTxs[].EffectiveAmount
  716. // - blocks[].Rollup.L1UserTxs[].EffectiveDepositAmount
  717. // - blocks[].Rollup.L1UserTxs[].EffectiveFromIdx
  718. func (tc *Context) FillBlocksForgedL1UserTxs(blocks []common.BlockData) error {
  719. for i := range blocks {
  720. block := &blocks[i]
  721. for j := range block.Rollup.Batches {
  722. batch := &block.Rollup.Batches[j]
  723. if batch.L1Batch {
  724. batchNum := batch.Batch.BatchNum
  725. queue := tc.Queues[int(*batch.Batch.ForgeL1TxsNum)]
  726. batch.L1UserTxs = make([]common.L1Tx, len(queue))
  727. for k := range queue {
  728. tx := &batch.L1UserTxs[k]
  729. *tx = queue[k].L1Tx
  730. tx.EffectiveAmount = tx.Amount
  731. tx.EffectiveDepositAmount = tx.DepositAmount
  732. tx.BatchNum = &batchNum
  733. _tx, err := common.NewL1Tx(tx)
  734. if err != nil {
  735. return tracerr.Wrap(err)
  736. }
  737. *tx = *_tx
  738. if tx.FromIdx == 0 {
  739. tx.EffectiveFromIdx = tc.extra.idxByTxID[tx.TxID]
  740. } else {
  741. tx.EffectiveFromIdx = tx.FromIdx
  742. }
  743. }
  744. }
  745. }
  746. }
  747. return nil
  748. }
  749. // FillBlocksExtra fills extra fields not generated by til in each block, so
  750. // that the blockData is closer to what the HistoryDB stores. The filled
  751. // fields are:
  752. // - blocks[].Rollup.Batch.EthBlockNum
  753. // - blocks[].Rollup.Batch.ForgerAddr
  754. // - blocks[].Rollup.Batch.ForgeL1TxsNum
  755. // - blocks[].Rollup.Batch.L1CoordinatorTxs[].TxID
  756. // - blocks[].Rollup.Batch.L1CoordinatorTxs[].BatchNum
  757. // - blocks[].Rollup.Batch.L1CoordinatorTxs[].EthBlockNum
  758. // - blocks[].Rollup.Batch.L1CoordinatorTxs[].Position
  759. // - blocks[].Rollup.Batch.L1CoordinatorTxs[].EffectiveAmount
  760. // - blocks[].Rollup.Batch.L1CoordinatorTxs[].EffectiveDepositAmount
  761. // - blocks[].Rollup.Batch.L1CoordinatorTxs[].EffectiveFromIdx
  762. // - blocks[].Rollup.Batch.L2Txs[].TxID
  763. // - blocks[].Rollup.Batch.L2Txs[].Position
  764. // - blocks[].Rollup.Batch.L2Txs[].Nonce
  765. // - blocks[].Rollup.Batch.L2Txs[].TokenID
  766. // - blocks[].Rollup.Batch.ExitTree
  767. // - blocks[].Rollup.Batch.CreatedAccounts
  768. // - blocks[].Rollup.Batch.FeeIdxCoordinator
  769. // - blocks[].Rollup.Batch.CollectedFees
  770. func (tc *Context) FillBlocksExtra(blocks []common.BlockData, cfg *ConfigExtra) error {
  771. // Fill extra fields not generated by til in til block
  772. for i := range blocks {
  773. block := &blocks[i]
  774. for j := range block.Rollup.Batches {
  775. batch := &block.Rollup.Batches[j]
  776. batch.Batch.EthBlockNum = block.Block.Num
  777. // til doesn't fill the batch forger addr
  778. batch.Batch.ForgerAddr = cfg.BootCoordAddr
  779. if batch.L1Batch {
  780. toForgeL1TxsNumCpy := tc.extra.toForgeL1TxsNum
  781. // til doesn't fill the ForgeL1TxsNum
  782. batch.Batch.ForgeL1TxsNum = &toForgeL1TxsNumCpy
  783. tc.extra.toForgeL1TxsNum++
  784. }
  785. batchNum := batch.Batch.BatchNum
  786. for k := range batch.L1CoordinatorTxs {
  787. tx := &batch.L1CoordinatorTxs[k]
  788. tx.BatchNum = &batchNum
  789. tx.EthBlockNum = batch.Batch.EthBlockNum
  790. }
  791. }
  792. }
  793. // Fill CreatedAccounts
  794. for i := range blocks {
  795. block := &blocks[i]
  796. for j := range block.Rollup.Batches {
  797. batch := &block.Rollup.Batches[j]
  798. l1Txs := []*common.L1Tx{}
  799. if batch.L1Batch {
  800. for k := range tc.Queues[*batch.Batch.ForgeL1TxsNum] {
  801. l1Txs = append(l1Txs, &tc.Queues[*batch.Batch.ForgeL1TxsNum][k].L1Tx)
  802. }
  803. }
  804. for k := range batch.L1CoordinatorTxs {
  805. l1Txs = append(l1Txs, &batch.L1CoordinatorTxs[k])
  806. }
  807. for k := range l1Txs {
  808. tx := l1Txs[k]
  809. if tx.Type == common.TxTypeCreateAccountDeposit ||
  810. tx.Type == common.TxTypeCreateAccountDepositTransfer {
  811. user, ok := tc.UsersByIdx[tc.extra.idx]
  812. if !ok {
  813. return tracerr.Wrap(fmt.Errorf("Created account with idx: %v not found", tc.extra.idx))
  814. }
  815. batch.CreatedAccounts = append(batch.CreatedAccounts,
  816. common.Account{
  817. Idx: common.Idx(tc.extra.idx),
  818. TokenID: tx.TokenID,
  819. BatchNum: batch.Batch.BatchNum,
  820. BJJ: user.BJJ.Public().Compress(),
  821. EthAddr: user.Addr,
  822. Nonce: 0,
  823. Balance: big.NewInt(0),
  824. })
  825. if !tx.UserOrigin {
  826. tx.EffectiveFromIdx = common.Idx(tc.extra.idx)
  827. }
  828. tc.extra.idxByTxID[tx.TxID] = common.Idx(tc.extra.idx)
  829. tc.extra.idx++
  830. }
  831. }
  832. }
  833. }
  834. // Fill expected positions in L1CoordinatorTxs and L2Txs
  835. for i := range blocks {
  836. block := &blocks[i]
  837. for j := range block.Rollup.Batches {
  838. batch := &block.Rollup.Batches[j]
  839. position := 0
  840. if batch.L1Batch {
  841. position = len(tc.Queues[*batch.Batch.ForgeL1TxsNum])
  842. }
  843. for k := range batch.L1CoordinatorTxs {
  844. tx := &batch.L1CoordinatorTxs[k]
  845. tx.Position = position
  846. position++
  847. tx.EffectiveAmount = big.NewInt(0)
  848. tx.EffectiveDepositAmount = big.NewInt(0)
  849. nTx, err := common.NewL1Tx(tx)
  850. if err != nil {
  851. return tracerr.Wrap(err)
  852. }
  853. *tx = *nTx
  854. }
  855. for k := range batch.L2Txs {
  856. tx := &batch.L2Txs[k]
  857. tx.Position = position
  858. position++
  859. tx.Nonce = tc.extra.nonces[tx.FromIdx]
  860. tx.TokenID = tc.accountsByIdx[int(tx.FromIdx)].TokenID
  861. tc.extra.nonces[tx.FromIdx]++
  862. if err := tx.SetID(); err != nil {
  863. return tracerr.Wrap(err)
  864. }
  865. nTx, err := common.NewL2Tx(tx)
  866. if err != nil {
  867. return tracerr.Wrap(err)
  868. }
  869. *tx = *nTx
  870. }
  871. }
  872. }
  873. // Fill ExitTree (only AccountIdx and Balance)
  874. for i := range blocks {
  875. block := &blocks[i]
  876. for j := range block.Rollup.Batches {
  877. batch := &block.Rollup.Batches[j]
  878. if batch.L1Batch {
  879. for _, _tx := range tc.Queues[*batch.Batch.ForgeL1TxsNum] {
  880. tx := _tx.L1Tx
  881. if tx.Type == common.TxTypeForceExit {
  882. batch.ExitTree =
  883. append(batch.ExitTree,
  884. common.ExitInfo{
  885. BatchNum: batch.Batch.BatchNum,
  886. AccountIdx: tx.FromIdx,
  887. Balance: tx.Amount,
  888. })
  889. }
  890. }
  891. }
  892. for k := range batch.L2Txs {
  893. tx := &batch.L2Txs[k]
  894. if tx.Type == common.TxTypeExit {
  895. batch.ExitTree = append(batch.ExitTree, common.ExitInfo{
  896. BatchNum: batch.Batch.BatchNum,
  897. AccountIdx: tx.FromIdx,
  898. Balance: tx.Amount,
  899. })
  900. }
  901. fee, err := common.CalcFeeAmount(tx.Amount, tx.Fee)
  902. if err != nil {
  903. return tracerr.Wrap(err)
  904. }
  905. // Find the TokenID of the tx
  906. fromAcc, ok := tc.accountsByIdx[int(tx.FromIdx)]
  907. if !ok {
  908. return tracerr.Wrap(fmt.Errorf("L2tx.FromIdx idx: %v not found", tx.FromIdx))
  909. }
  910. // Find the idx of the CoordUser for the
  911. // TokenID, and if it exists, add the fee to
  912. // the collectedFees. Only consider the
  913. // coordinator account to receive fee if it was
  914. // created in this or a previous batch
  915. if acc, ok := tc.l1CreatedAccounts[idxTokenIDToString(cfg.CoordUser, fromAcc.TokenID)]; ok &&
  916. common.BatchNum(acc.BatchNum) <= batch.Batch.BatchNum {
  917. found := false
  918. for _, idx := range batch.Batch.FeeIdxsCoordinator {
  919. if idx == common.Idx(acc.Idx) {
  920. found = true
  921. break
  922. }
  923. }
  924. if !found {
  925. batch.Batch.FeeIdxsCoordinator = append(batch.Batch.FeeIdxsCoordinator,
  926. common.Idx(acc.Idx))
  927. batch.Batch.CollectedFees[fromAcc.TokenID] = big.NewInt(0)
  928. }
  929. collected := batch.Batch.CollectedFees[fromAcc.TokenID]
  930. collected.Add(collected, fee)
  931. }
  932. }
  933. }
  934. }
  935. return nil
  936. }