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.

959 lines
31 KiB

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