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.

977 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.
3 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update missing parts, improve til, and more - Node - Updated configuration to initialize the interface to all the smart contracts - Common - Moved BlockData and BatchData types to common so that they can be shared among: historydb, til and synchronizer - Remove hash.go (it was never used) - Remove slot.go (it was never used) - Remove smartcontractparams.go (it was never used, and appropriate structs are defined in `eth/`) - Comment state / status method until requirements of this method are properly defined, and move it to Synchronizer - Synchronizer - Simplify `Sync` routine to only sync one block per call, and return useful information. - Use BlockData and BatchData from common - Check that events belong to the expected block hash - In L1Batch, query L1UserTxs from HistoryDB - Fill ERC20 token information - Test AddTokens with test.Client - HistryDB - Use BlockData and BatchData from common - Add `GetAllTokens` method - Uncomment and update GetL1UserTxs (with corresponding tests) - Til - Rename all instances of RegisterToken to AddToken (to follow the smart contract implementation naming) - Use BlockData and BatchData from common - Move testL1CoordinatorTxs and testL2Txs to a separate struct from BatchData in Context - Start Context with BatchNum = 1 (which the protocol defines to be the first batchNum) - In every Batch, set StateRoot and ExitRoot to a non-nil big.Int (zero). - In all L1Txs, if LoadAmount is not used, set it to 0; if Amount is not used, set it to 0; so that no *big.Int is nil. - In L1UserTx, don't set BatchNum, because when L1UserTxs are created and obtained by the synchronizer, the BatchNum is not known yet (it's a synchronizer job to set it) - In L1UserTxs, set `UserOrigin` and set `ToForgeL1TxsNum`.
4 years ago
Update coordinator, call all api update functions - Common: - Rename Block.EthBlockNum to Block.Num to avoid unneeded repetition - API: - Add UpdateNetworkInfoBlock to update just block information, to be used when the node is not yet synchronized - Node: - Call API.UpdateMetrics and UpdateRecommendedFee in a loop, with configurable time intervals - Synchronizer: - When mapping events by TxHash, use an array to support the possibility of multiple calls of the same function happening in the same transaction (for example, a smart contract in a single transaction could call withdraw with delay twice, which would generate 2 withdraw events, and 2 deposit events). - In Stats, keep entire LastBlock instead of just the blockNum - In Stats, add lastL1BatchBlock - Test Stats and SCVars - Coordinator: - Enable writing the BatchInfo in every step of the pipeline to disk (with JSON text files) for debugging purposes. - Move the Pipeline functionality from the Coordinator to its own struct (Pipeline) - Implement shouldL1lL2Batch - In TxManager, implement logic to perform several attempts when doing ethereum node RPC calls before considering the error. (Both for calls to forgeBatch and transaction receipt) - In TxManager, reorganize the flow and note the specific points in which actions are made when err != nil - HistoryDB: - Implement GetLastL1BatchBlockNum: returns the blockNum of the latest forged l1Batch, to help the coordinator decide when to forge an L1Batch. - EthereumClient and test.Client: - Update EthBlockByNumber to return the last block when the passed number is -1.
3 years ago
  1. package til
  2. import (
  3. "crypto/ecdsa"
  4. "encoding/binary"
  5. "fmt"
  6. "math/big"
  7. "strings"
  8. "time"
  9. ethCommon "github.com/ethereum/go-ethereum/common"
  10. ethCrypto "github.com/ethereum/go-ethereum/crypto"
  11. "github.com/hermeznetwork/hermez-node/common"
  12. "github.com/hermeznetwork/hermez-node/log"
  13. "github.com/hermeznetwork/tracerr"
  14. "github.com/iden3/go-iden3-crypto/babyjub"
  15. )
  16. func newBatchData(batchNum int) common.BatchData {
  17. return common.BatchData{
  18. L1CoordinatorTxs: []common.L1Tx{},
  19. L2Txs: []common.L2Tx{},
  20. Batch: common.Batch{
  21. BatchNum: common.BatchNum(batchNum),
  22. StateRoot: big.NewInt(0), ExitRoot: big.NewInt(0),
  23. FeeIdxsCoordinator: make([]common.Idx, 0),
  24. CollectedFees: make(map[common.TokenID]*big.Int),
  25. },
  26. }
  27. }
  28. func newBlock(blockNum int64) common.BlockData {
  29. return common.BlockData{
  30. Block: common.Block{
  31. Num: blockNum,
  32. },
  33. Rollup: common.RollupData{
  34. L1UserTxs: []common.L1Tx{},
  35. },
  36. }
  37. }
  38. type contextExtra struct {
  39. openToForge int64
  40. toForgeL1TxsNum int64
  41. nonces map[common.Idx]common.Nonce
  42. idx int
  43. }
  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. var iBytes [8]byte
  650. binary.LittleEndian.PutUint64(iBytes[:], uint64(i))
  651. copy(sk[:], iBytes[:]) // only for testing
  652. // eth address
  653. var key ecdsa.PrivateKey
  654. key.D = big.NewInt(int64(i)) // only for testing
  655. key.PublicKey.X, key.PublicKey.Y = ethCrypto.S256().ScalarBaseMult(key.D.Bytes())
  656. key.Curve = ethCrypto.S256()
  657. addr := ethCrypto.PubkeyToAddress(key.PublicKey)
  658. u := User{
  659. Name: userNames[i-1],
  660. BJJ: &sk,
  661. EthSk: &key,
  662. Addr: addr,
  663. Accounts: make(map[common.TokenID]*Account),
  664. }
  665. tc.Users[userNames[i-1]] = &u
  666. }
  667. }
  668. // L1TxsToCommonL1Txs converts an array of []til.L1Tx to []common.L1Tx
  669. func L1TxsToCommonL1Txs(l1 []L1Tx) []common.L1Tx {
  670. var r []common.L1Tx
  671. for i := 0; i < len(l1); i++ {
  672. r = append(r, l1[i].L1Tx)
  673. }
  674. return r
  675. }
  676. // ConfigExtra is the configuration used in FillBlocksExtra to extend the
  677. // blocks returned by til.
  678. type ConfigExtra struct {
  679. // Address to set as forger for each batch
  680. BootCoordAddr ethCommon.Address
  681. // Coordinator user name used to select the corresponding accounts to
  682. // collect coordinator fees
  683. CoordUser string
  684. }
  685. // FillBlocksL1UserTxsBatchNum fills the BatchNum of forged L1UserTxs:
  686. // - blocks[].Rollup.L1UserTxs[].BatchNum
  687. func (tc *Context) FillBlocksL1UserTxsBatchNum(blocks []common.BlockData) {
  688. for i := range blocks {
  689. block := &blocks[i]
  690. for j := range block.Rollup.Batches {
  691. batch := &block.Rollup.Batches[j]
  692. if batch.L1Batch {
  693. // Set BatchNum for forged L1UserTxs to til blocks
  694. bn := batch.Batch.BatchNum
  695. for k := range blocks {
  696. block := &blocks[k]
  697. for l := range block.Rollup.L1UserTxs {
  698. tx := &block.Rollup.L1UserTxs[l]
  699. if *tx.ToForgeL1TxsNum == tc.extra.openToForge {
  700. tx.BatchNum = &bn
  701. }
  702. }
  703. }
  704. tc.extra.openToForge++
  705. }
  706. }
  707. }
  708. }
  709. // FillBlocksForgedL1UserTxs fills the L1UserTxs of a batch with the L1UserTxs
  710. // that are forged in that batch. It always sets `EffectiveAmount` = `Amount`
  711. // and `EffectiveDepositAmount` = `DepositAmount`.
  712. func (tc *Context) FillBlocksForgedL1UserTxs(blocks []common.BlockData) error {
  713. for i := range blocks {
  714. block := &blocks[i]
  715. for j := range block.Rollup.Batches {
  716. batch := &block.Rollup.Batches[j]
  717. if batch.L1Batch {
  718. batchNum := batch.Batch.BatchNum
  719. queue := tc.Queues[int(*batch.Batch.ForgeL1TxsNum)]
  720. batch.L1UserTxs = make([]common.L1Tx, len(queue))
  721. for k := range queue {
  722. tx := &batch.L1UserTxs[k]
  723. *tx = queue[k].L1Tx
  724. tx.EffectiveAmount = tx.Amount
  725. tx.EffectiveDepositAmount = tx.DepositAmount
  726. tx.BatchNum = &batchNum
  727. _tx, err := common.NewL1Tx(tx)
  728. if err != nil {
  729. return tracerr.Wrap(err)
  730. }
  731. *tx = *_tx
  732. }
  733. }
  734. }
  735. }
  736. return nil
  737. }
  738. // FillBlocksExtra fills extra fields not generated by til in each block, so
  739. // that the blockData is closer to what the HistoryDB stores. The filled
  740. // fields are:
  741. // - blocks[].Rollup.Batch.EthBlockNum
  742. // - blocks[].Rollup.Batch.ForgerAddr
  743. // - blocks[].Rollup.Batch.ForgeL1TxsNum
  744. // - blocks[].Rollup.Batch.L1CoordinatorTxs[].TxID
  745. // - blocks[].Rollup.Batch.L1CoordinatorTxs[].BatchNum
  746. // - blocks[].Rollup.Batch.L1CoordinatorTxs[].EthBlockNum
  747. // - blocks[].Rollup.Batch.L1CoordinatorTxs[].Position
  748. // - blocks[].Rollup.Batch.L1CoordinatorTxs[].EffectiveAmount
  749. // - blocks[].Rollup.Batch.L1CoordinatorTxs[].EffectiveDepositAmount
  750. // - blocks[].Rollup.Batch.L2Txs[].TxID
  751. // - blocks[].Rollup.Batch.L2Txs[].Position
  752. // - blocks[].Rollup.Batch.L2Txs[].Nonce
  753. // - blocks[].Rollup.Batch.ExitTree
  754. // - blocks[].Rollup.Batch.CreatedAccounts
  755. // - blocks[].Rollup.Batch.FeeIdxCoordinator
  756. // - blocks[].Rollup.Batch.CollectedFees
  757. func (tc *Context) FillBlocksExtra(blocks []common.BlockData, cfg *ConfigExtra) error {
  758. // Fill extra fields not generated by til in til block
  759. for i := range blocks {
  760. block := &blocks[i]
  761. for j := range block.Rollup.Batches {
  762. batch := &block.Rollup.Batches[j]
  763. batch.Batch.EthBlockNum = block.Block.Num
  764. // til doesn't fill the batch forger addr
  765. batch.Batch.ForgerAddr = cfg.BootCoordAddr
  766. if batch.L1Batch {
  767. toForgeL1TxsNumCpy := tc.extra.toForgeL1TxsNum
  768. // til doesn't fill the ForgeL1TxsNum
  769. batch.Batch.ForgeL1TxsNum = &toForgeL1TxsNumCpy
  770. tc.extra.toForgeL1TxsNum++
  771. }
  772. batchNum := batch.Batch.BatchNum
  773. for k := range batch.L1CoordinatorTxs {
  774. tx := &batch.L1CoordinatorTxs[k]
  775. tx.BatchNum = &batchNum
  776. tx.EthBlockNum = batch.Batch.EthBlockNum
  777. }
  778. }
  779. }
  780. // Fill CreatedAccounts
  781. for i := range blocks {
  782. block := &blocks[i]
  783. for j := range block.Rollup.Batches {
  784. batch := &block.Rollup.Batches[j]
  785. l1Txs := []common.L1Tx{}
  786. if batch.L1Batch {
  787. for _, tx := range tc.Queues[*batch.Batch.ForgeL1TxsNum] {
  788. l1Txs = append(l1Txs, tx.L1Tx)
  789. }
  790. }
  791. l1Txs = append(l1Txs, batch.L1CoordinatorTxs...)
  792. for k := range l1Txs {
  793. tx := &l1Txs[k]
  794. if tx.Type == common.TxTypeCreateAccountDeposit ||
  795. tx.Type == common.TxTypeCreateAccountDepositTransfer {
  796. user, ok := tc.UsersByIdx[tc.extra.idx]
  797. if !ok {
  798. return tracerr.Wrap(fmt.Errorf("Created account with idx: %v not found", tc.extra.idx))
  799. }
  800. batch.CreatedAccounts = append(batch.CreatedAccounts,
  801. common.Account{
  802. Idx: common.Idx(tc.extra.idx),
  803. TokenID: tx.TokenID,
  804. BatchNum: batch.Batch.BatchNum,
  805. BJJ: user.BJJ.Public().Compress(),
  806. EthAddr: user.Addr,
  807. Nonce: 0,
  808. Balance: big.NewInt(0),
  809. })
  810. tc.extra.idx++
  811. }
  812. }
  813. }
  814. }
  815. // Fill expected positions in L1CoordinatorTxs and L2Txs
  816. for i := range blocks {
  817. block := &blocks[i]
  818. for j := range block.Rollup.Batches {
  819. batch := &block.Rollup.Batches[j]
  820. position := 0
  821. if batch.L1Batch {
  822. position = len(tc.Queues[*batch.Batch.ForgeL1TxsNum])
  823. }
  824. for k := range batch.L1CoordinatorTxs {
  825. tx := &batch.L1CoordinatorTxs[k]
  826. tx.Position = position
  827. position++
  828. tx.EffectiveAmount = big.NewInt(0)
  829. tx.EffectiveDepositAmount = big.NewInt(0)
  830. nTx, err := common.NewL1Tx(tx)
  831. if err != nil {
  832. return tracerr.Wrap(err)
  833. }
  834. *tx = *nTx
  835. }
  836. for k := range batch.L2Txs {
  837. tx := &batch.L2Txs[k]
  838. tx.Position = position
  839. position++
  840. tx.Nonce = tc.extra.nonces[tx.FromIdx]
  841. tc.extra.nonces[tx.FromIdx]++
  842. if err := tx.SetID(); err != nil {
  843. return tracerr.Wrap(err)
  844. }
  845. nTx, err := common.NewL2Tx(tx)
  846. if err != nil {
  847. return tracerr.Wrap(err)
  848. }
  849. *tx = *nTx
  850. }
  851. }
  852. }
  853. // Fill ExitTree (only AccountIdx and Balance)
  854. for i := range blocks {
  855. block := &blocks[i]
  856. for j := range block.Rollup.Batches {
  857. batch := &block.Rollup.Batches[j]
  858. if batch.L1Batch {
  859. for _, _tx := range tc.Queues[*batch.Batch.ForgeL1TxsNum] {
  860. tx := _tx.L1Tx
  861. if tx.Type == common.TxTypeForceExit {
  862. batch.ExitTree =
  863. append(batch.ExitTree,
  864. common.ExitInfo{
  865. BatchNum: batch.Batch.BatchNum,
  866. AccountIdx: tx.FromIdx,
  867. Balance: tx.Amount,
  868. })
  869. }
  870. }
  871. }
  872. for k := range batch.L2Txs {
  873. tx := &batch.L2Txs[k]
  874. if tx.Type == common.TxTypeExit {
  875. batch.ExitTree = append(batch.ExitTree, common.ExitInfo{
  876. BatchNum: batch.Batch.BatchNum,
  877. AccountIdx: tx.FromIdx,
  878. Balance: tx.Amount,
  879. })
  880. }
  881. fee, err := common.CalcFeeAmount(tx.Amount, tx.Fee)
  882. if err != nil {
  883. return tracerr.Wrap(err)
  884. }
  885. // Find the TokenID of the tx
  886. fromAcc, ok := tc.accountsByIdx[int(tx.FromIdx)]
  887. if !ok {
  888. return tracerr.Wrap(fmt.Errorf("L2tx.FromIdx idx: %v not found", tx.FromIdx))
  889. }
  890. // Find the idx of the CoordUser for the
  891. // TokenID, and if it exists, add the fee to
  892. // the collectedFees. Only consider the
  893. // coordinator account to receive fee if it was
  894. // created in this or a previous batch
  895. if acc, ok := tc.l1CreatedAccounts[idxTokenIDToString(cfg.CoordUser, fromAcc.TokenID)]; ok &&
  896. common.BatchNum(acc.BatchNum) <= batch.Batch.BatchNum {
  897. found := false
  898. for _, idx := range batch.Batch.FeeIdxsCoordinator {
  899. if idx == common.Idx(acc.Idx) {
  900. found = true
  901. break
  902. }
  903. }
  904. if !found {
  905. batch.Batch.FeeIdxsCoordinator = append(batch.Batch.FeeIdxsCoordinator,
  906. common.Idx(acc.Idx))
  907. batch.Batch.CollectedFees[fromAcc.TokenID] = big.NewInt(0)
  908. }
  909. collected := batch.Batch.CollectedFees[fromAcc.TokenID]
  910. collected.Add(collected, fee)
  911. }
  912. }
  913. }
  914. }
  915. return nil
  916. }