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.

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