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.

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