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.

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