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.

996 lines
33 KiB

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