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.

560 lines
14 KiB

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
  1. package til
  2. import (
  3. "bufio"
  4. "bytes"
  5. "fmt"
  6. "io"
  7. "math/big"
  8. "sort"
  9. "strconv"
  10. "github.com/hermeznetwork/hermez-node/common"
  11. "github.com/hermeznetwork/hermez-node/log"
  12. "github.com/hermeznetwork/tracerr"
  13. )
  14. var eof = rune(0)
  15. var errof = fmt.Errorf("eof in parseline")
  16. var commentLine = fmt.Errorf("comment in parseline") //nolint:golint
  17. var newEventLine = fmt.Errorf("newEventLine") //nolint:golint
  18. var setTypeLine = fmt.Errorf("setTypeLine") //nolint:golint
  19. // setType defines the type of the set
  20. type setType string
  21. // setTypeBlockchain defines the type 'Blockchain' of the set
  22. var setTypeBlockchain = setType("Blockchain")
  23. // setTypePoolL2 defines the type 'PoolL2' of the set
  24. var setTypePoolL2 = setType("PoolL2")
  25. // typeNewBatch is used for testing purposes only, and represents the
  26. // common.TxType of a new batch
  27. var typeNewBatch common.TxType = "InstrTypeNewBatch"
  28. // typeNewBatchL1 is used for testing purposes only, and represents the
  29. // common.TxType of a new batch
  30. var typeNewBatchL1 common.TxType = "InstrTypeNewBatchL1"
  31. // typeNewBlock is used for testing purposes only, and represents the
  32. // common.TxType of a new ethereum block
  33. var typeNewBlock common.TxType = "InstrTypeNewBlock"
  34. // typeAddToken is used for testing purposes only, and represents the
  35. // common.TxType of a new Token regsitration
  36. // It has 'nolint:gosec' as the string 'Token' triggers gosec as a potential leaked Token (which is not the case)
  37. var typeAddToken common.TxType = "InstrTypeAddToken" //nolint:gosec
  38. var txTypeCreateAccountDepositCoordinator common.TxType = "TypeCreateAccountDepositCoordinator"
  39. //nolint
  40. const (
  41. ILLEGAL token = iota
  42. WS
  43. EOF
  44. IDENT // val
  45. )
  46. // instruction is the data structure that represents one line of code
  47. type instruction struct {
  48. lineNum int
  49. literal string
  50. from string
  51. to string
  52. amount *big.Int
  53. loadAmount *big.Int
  54. fee uint8
  55. tokenID common.TokenID
  56. typ common.TxType // D: Deposit, T: Transfer, E: ForceExit
  57. }
  58. // parsedSet contains the full Set of Instructions representing a full code
  59. type parsedSet struct {
  60. typ setType
  61. instructions []instruction
  62. users []string
  63. }
  64. func (i instruction) String() string {
  65. buf := bytes.NewBufferString("")
  66. fmt.Fprintf(buf, "Type: %s, ", i.typ)
  67. fmt.Fprintf(buf, "From: %s, ", i.from)
  68. if i.typ == common.TxTypeTransfer ||
  69. i.typ == common.TxTypeDepositTransfer ||
  70. i.typ == common.TxTypeCreateAccountDepositTransfer {
  71. fmt.Fprintf(buf, "To: %s, ", i.to)
  72. }
  73. if i.typ == common.TxTypeDeposit ||
  74. i.typ == common.TxTypeDepositTransfer ||
  75. i.typ == common.TxTypeCreateAccountDepositTransfer {
  76. fmt.Fprintf(buf, "LoadAmount: %d, ", i.loadAmount)
  77. }
  78. if i.typ != common.TxTypeDeposit {
  79. fmt.Fprintf(buf, "Amount: %d, ", i.amount)
  80. }
  81. if i.typ == common.TxTypeTransfer ||
  82. i.typ == common.TxTypeDepositTransfer ||
  83. i.typ == common.TxTypeCreateAccountDepositTransfer {
  84. fmt.Fprintf(buf, "Fee: %d, ", i.fee)
  85. }
  86. fmt.Fprintf(buf, "TokenID: %d\n", i.tokenID)
  87. return buf.String()
  88. }
  89. // Raw returns a string with the raw representation of the Instruction
  90. func (i instruction) raw() string {
  91. buf := bytes.NewBufferString("")
  92. fmt.Fprintf(buf, "%s", i.typ)
  93. fmt.Fprintf(buf, "(%d)", i.tokenID)
  94. fmt.Fprintf(buf, "%s", i.from)
  95. if i.typ == common.TxTypeTransfer ||
  96. i.typ == common.TxTypeDepositTransfer ||
  97. i.typ == common.TxTypeCreateAccountDepositTransfer {
  98. fmt.Fprintf(buf, "-%s", i.to)
  99. }
  100. fmt.Fprintf(buf, ":")
  101. if i.typ == common.TxTypeDeposit ||
  102. i.typ == common.TxTypeDepositTransfer ||
  103. i.typ == common.TxTypeCreateAccountDepositTransfer {
  104. fmt.Fprintf(buf, "%d", i.loadAmount)
  105. }
  106. if i.typ != common.TxTypeDeposit {
  107. fmt.Fprintf(buf, "%d", i.amount)
  108. }
  109. if i.typ == common.TxTypeTransfer {
  110. fmt.Fprintf(buf, "(%d)", i.fee)
  111. }
  112. return buf.String()
  113. }
  114. type token int
  115. type scanner struct {
  116. r *bufio.Reader
  117. }
  118. func isWhitespace(ch rune) bool {
  119. return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\v' || ch == '\f'
  120. }
  121. func isLetter(ch rune) bool {
  122. return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
  123. }
  124. func isComment(ch rune) bool {
  125. return ch == '/'
  126. }
  127. func isDigit(ch rune) bool {
  128. return (ch >= '0' && ch <= '9')
  129. }
  130. // newScanner creates a new scanner with the given io.Reader
  131. func newScanner(r io.Reader) *scanner {
  132. return &scanner{r: bufio.NewReader(r)}
  133. }
  134. func (s *scanner) read() rune {
  135. ch, _, err := s.r.ReadRune()
  136. if err != nil {
  137. return eof
  138. }
  139. return ch
  140. }
  141. func (s *scanner) unread() {
  142. _ = s.r.UnreadRune()
  143. }
  144. // scan returns the token and literal string of the current value
  145. func (s *scanner) scan() (tok token, lit string) {
  146. ch := s.read()
  147. if isWhitespace(ch) {
  148. // space
  149. s.unread()
  150. return s.scanWhitespace()
  151. } else if isLetter(ch) || isDigit(ch) {
  152. // letter/digit
  153. s.unread()
  154. return s.scanIndent()
  155. } else if isComment(ch) {
  156. // comment
  157. s.unread()
  158. return s.scanIndent()
  159. }
  160. if ch == eof {
  161. return EOF, ""
  162. }
  163. return ILLEGAL, string(ch)
  164. }
  165. func (s *scanner) scanWhitespace() (token token, lit string) {
  166. var buf bytes.Buffer
  167. buf.WriteRune(s.read())
  168. for {
  169. if ch := s.read(); ch == eof {
  170. break
  171. } else if !isWhitespace(ch) {
  172. s.unread()
  173. break
  174. } else {
  175. _, _ = buf.WriteRune(ch)
  176. }
  177. }
  178. return WS, buf.String()
  179. }
  180. func (s *scanner) scanIndent() (tok token, lit string) {
  181. var buf bytes.Buffer
  182. buf.WriteRune(s.read())
  183. for {
  184. if ch := s.read(); ch == eof {
  185. break
  186. } else if !isLetter(ch) && !isDigit(ch) {
  187. s.unread()
  188. break
  189. } else {
  190. _, _ = buf.WriteRune(ch)
  191. }
  192. }
  193. if len(buf.String()) == 1 {
  194. return token(rune(buf.String()[0])), buf.String()
  195. }
  196. return IDENT, buf.String()
  197. }
  198. // parser defines the parser
  199. type parser struct {
  200. s *scanner
  201. buf struct {
  202. tok token
  203. lit string
  204. n int
  205. }
  206. }
  207. // newParser creates a new parser from a io.Reader
  208. func newParser(r io.Reader) *parser {
  209. return &parser{s: newScanner(r)}
  210. }
  211. func (p *parser) scan() (tok token, lit string) {
  212. // if there is a token in the buffer return it
  213. if p.buf.n != 0 {
  214. p.buf.n = 0
  215. return p.buf.tok, p.buf.lit
  216. }
  217. tok, lit = p.s.scan()
  218. p.buf.tok, p.buf.lit = tok, lit
  219. return
  220. }
  221. func (p *parser) scanIgnoreWhitespace() (tok token, lit string) {
  222. tok, lit = p.scan()
  223. if tok == WS {
  224. tok, lit = p.scan()
  225. }
  226. return
  227. }
  228. // parseLine parses the current line
  229. func (p *parser) parseLine(setType setType) (*instruction, error) {
  230. c := &instruction{}
  231. tok, lit := p.scanIgnoreWhitespace()
  232. if tok == EOF {
  233. return nil, tracerr.Wrap(errof)
  234. }
  235. c.literal += lit
  236. if lit == "/" {
  237. _, _ = p.s.r.ReadString('\n')
  238. return nil, commentLine
  239. } else if lit == ">" {
  240. if setType == setTypePoolL2 {
  241. return c, tracerr.Wrap(fmt.Errorf("Unexpected '>' at PoolL2Txs set"))
  242. }
  243. _, lit = p.scanIgnoreWhitespace()
  244. if lit == "batch" {
  245. _, _ = p.s.r.ReadString('\n')
  246. return &instruction{typ: typeNewBatch}, newEventLine
  247. } else if lit == "batchL1" {
  248. _, _ = p.s.r.ReadString('\n')
  249. return &instruction{typ: typeNewBatchL1}, newEventLine
  250. } else if lit == "block" {
  251. _, _ = p.s.r.ReadString('\n')
  252. return &instruction{typ: typeNewBlock}, newEventLine
  253. } else {
  254. return c, tracerr.Wrap(fmt.Errorf("Unexpected '> %s', expected '> batch' or '> block'", lit))
  255. }
  256. } else if lit == "Type" {
  257. if err := p.expectChar(c, ":"); err != nil {
  258. return c, tracerr.Wrap(err)
  259. }
  260. _, lit = p.scanIgnoreWhitespace()
  261. if lit == "Blockchain" {
  262. return &instruction{typ: "Blockchain"}, setTypeLine
  263. } else if lit == "PoolL2" {
  264. return &instruction{typ: "PoolL2"}, setTypeLine
  265. } else {
  266. return c, tracerr.Wrap(fmt.Errorf("Invalid set type: '%s'. Valid set types: 'Blockchain', 'PoolL2'", lit))
  267. }
  268. } else if lit == "AddToken" {
  269. if err := p.expectChar(c, "("); err != nil {
  270. return c, tracerr.Wrap(err)
  271. }
  272. _, lit = p.scanIgnoreWhitespace()
  273. c.literal += lit
  274. tidI, err := strconv.Atoi(lit)
  275. if err != nil {
  276. line, _ := p.s.r.ReadString('\n')
  277. c.literal += line
  278. return c, tracerr.Wrap(err)
  279. }
  280. c.tokenID = common.TokenID(tidI)
  281. if err := p.expectChar(c, ")"); err != nil {
  282. return c, tracerr.Wrap(err)
  283. }
  284. c.typ = typeAddToken
  285. line, _ := p.s.r.ReadString('\n')
  286. c.literal += line
  287. return c, newEventLine
  288. }
  289. if setType == "" {
  290. return c, tracerr.Wrap(fmt.Errorf("Set type not defined"))
  291. }
  292. transferring := false
  293. fee := false
  294. if setType == setTypeBlockchain {
  295. switch lit {
  296. case "Deposit":
  297. c.typ = common.TxTypeDeposit
  298. case "Exit":
  299. c.typ = common.TxTypeExit
  300. fee = true
  301. case "Transfer":
  302. c.typ = common.TxTypeTransfer
  303. transferring = true
  304. fee = true
  305. case "CreateAccountDeposit":
  306. c.typ = common.TxTypeCreateAccountDeposit
  307. case "CreateAccountDepositTransfer":
  308. c.typ = common.TxTypeCreateAccountDepositTransfer
  309. transferring = true
  310. case "CreateAccountCoordinator":
  311. c.typ = txTypeCreateAccountDepositCoordinator
  312. // transferring is false, as the Coordinator tx transfer will be 0
  313. case "DepositTransfer":
  314. c.typ = common.TxTypeDepositTransfer
  315. transferring = true
  316. case "ForceTransfer":
  317. c.typ = common.TxTypeForceTransfer
  318. transferring = true
  319. case "ForceExit":
  320. c.typ = common.TxTypeForceExit
  321. default:
  322. return c, tracerr.Wrap(fmt.Errorf("Unexpected Blockchain tx type: %s", lit))
  323. }
  324. } else if setType == setTypePoolL2 {
  325. switch lit {
  326. case "PoolTransfer":
  327. c.typ = common.TxTypeTransfer
  328. transferring = true
  329. fee = true
  330. case "PoolTransferToEthAddr":
  331. c.typ = common.TxTypeTransferToEthAddr
  332. transferring = true
  333. fee = true
  334. case "PoolTransferToBJJ":
  335. c.typ = common.TxTypeTransferToBJJ
  336. transferring = true
  337. fee = true
  338. case "PoolExit":
  339. c.typ = common.TxTypeExit
  340. fee = true
  341. default:
  342. return c, tracerr.Wrap(fmt.Errorf("Unexpected PoolL2 tx type: %s", lit))
  343. }
  344. } else {
  345. return c, tracerr.Wrap(fmt.Errorf("Invalid set type: '%s'. Valid set types: 'Blockchain', 'PoolL2'", setType))
  346. }
  347. if err := p.expectChar(c, "("); err != nil {
  348. return c, tracerr.Wrap(err)
  349. }
  350. _, lit = p.scanIgnoreWhitespace()
  351. c.literal += lit
  352. tidI, err := strconv.Atoi(lit)
  353. if err != nil {
  354. line, _ := p.s.r.ReadString('\n')
  355. c.literal += line
  356. return c, tracerr.Wrap(err)
  357. }
  358. c.tokenID = common.TokenID(tidI)
  359. if err := p.expectChar(c, ")"); err != nil {
  360. return c, tracerr.Wrap(err)
  361. }
  362. _, lit = p.scanIgnoreWhitespace()
  363. c.literal += lit
  364. c.from = lit
  365. if c.typ == txTypeCreateAccountDepositCoordinator {
  366. line, _ := p.s.r.ReadString('\n')
  367. c.literal += line
  368. return c, nil
  369. }
  370. _, lit = p.scanIgnoreWhitespace()
  371. c.literal += lit
  372. if transferring {
  373. if lit != "-" {
  374. return c, tracerr.Wrap(fmt.Errorf("Expected '-', found '%s'", lit))
  375. }
  376. _, lit = p.scanIgnoreWhitespace()
  377. c.literal += lit
  378. c.to = lit
  379. _, lit = p.scanIgnoreWhitespace()
  380. c.literal += lit
  381. }
  382. if lit != ":" {
  383. line, _ := p.s.r.ReadString('\n')
  384. c.literal += line
  385. return c, tracerr.Wrap(fmt.Errorf("Expected ':', found '%s'", lit))
  386. }
  387. if c.typ == common.TxTypeDepositTransfer ||
  388. c.typ == common.TxTypeCreateAccountDepositTransfer {
  389. // deposit case
  390. _, lit = p.scanIgnoreWhitespace()
  391. c.literal += lit
  392. loadAmount, ok := new(big.Int).SetString(lit, 10)
  393. if !ok {
  394. line, _ := p.s.r.ReadString('\n')
  395. c.literal += line
  396. return c, tracerr.Wrap(fmt.Errorf("Can not parse number for LoadAmount"))
  397. }
  398. c.loadAmount = loadAmount
  399. if err := p.expectChar(c, ","); err != nil {
  400. return c, tracerr.Wrap(err)
  401. }
  402. }
  403. _, lit = p.scanIgnoreWhitespace()
  404. c.literal += lit
  405. amount, ok := new(big.Int).SetString(lit, 10)
  406. if !ok {
  407. line, _ := p.s.r.ReadString('\n')
  408. c.literal += line
  409. return c, tracerr.Wrap(fmt.Errorf("Can not parse number for Amount: %s", lit))
  410. }
  411. if c.typ == common.TxTypeDeposit ||
  412. c.typ == common.TxTypeCreateAccountDeposit {
  413. c.loadAmount = amount
  414. } else {
  415. c.amount = amount
  416. }
  417. if fee {
  418. if err := p.expectChar(c, "("); err != nil {
  419. return c, tracerr.Wrap(err)
  420. }
  421. _, lit = p.scanIgnoreWhitespace()
  422. c.literal += lit
  423. fee, err := strconv.Atoi(lit)
  424. if err != nil {
  425. line, _ := p.s.r.ReadString('\n')
  426. c.literal += line
  427. return c, tracerr.Wrap(err)
  428. }
  429. if fee > common.MaxFeePlan-1 {
  430. line, _ := p.s.r.ReadString('\n')
  431. c.literal += line
  432. return c, tracerr.Wrap(fmt.Errorf("Fee %d can not be bigger than 255", fee))
  433. }
  434. c.fee = uint8(fee)
  435. if err := p.expectChar(c, ")"); err != nil {
  436. return c, tracerr.Wrap(err)
  437. }
  438. }
  439. if tok == EOF {
  440. return nil, tracerr.Wrap(errof)
  441. }
  442. return c, nil
  443. }
  444. func (p *parser) expectChar(c *instruction, ch string) error {
  445. _, lit := p.scanIgnoreWhitespace()
  446. c.literal += lit
  447. if lit != ch {
  448. line, _ := p.s.r.ReadString('\n')
  449. c.literal += line
  450. return tracerr.Wrap(fmt.Errorf("Expected '%s', found '%s'", ch, lit))
  451. }
  452. return nil
  453. }
  454. func idxTokenIDToString(idx string, tid common.TokenID) string {
  455. return idx + strconv.Itoa(int(tid))
  456. }
  457. // parse parses through reader
  458. func (p *parser) parse() (*parsedSet, error) {
  459. ps := &parsedSet{}
  460. i := 0 // lines will start counting at line 1
  461. users := make(map[string]bool)
  462. for {
  463. i++
  464. instruction, err := p.parseLine(ps.typ)
  465. if tracerr.Unwrap(err) == errof {
  466. break
  467. }
  468. if tracerr.Unwrap(err) == setTypeLine {
  469. if ps.typ != "" {
  470. return ps, tracerr.Wrap(fmt.Errorf("Line %d: Instruction of 'Type: %s' when there is already a previous instruction 'Type: %s' defined", i, instruction.typ, ps.typ))
  471. }
  472. if instruction.typ == "PoolL2" {
  473. ps.typ = setTypePoolL2
  474. } else if instruction.typ == "Blockchain" {
  475. ps.typ = setTypeBlockchain
  476. } else {
  477. log.Fatalf("Line %d: Invalid set type: '%s'. Valid set types: 'Blockchain', 'PoolL2'", i, instruction.typ)
  478. }
  479. continue
  480. }
  481. if tracerr.Unwrap(err) == commentLine {
  482. continue
  483. }
  484. instruction.lineNum = i
  485. if tracerr.Unwrap(err) == newEventLine {
  486. if instruction.typ == typeAddToken && instruction.tokenID == common.TokenID(0) {
  487. return ps, tracerr.Wrap(fmt.Errorf("Line %d: AddToken can not register TokenID 0", i))
  488. }
  489. ps.instructions = append(ps.instructions, *instruction)
  490. continue
  491. }
  492. if err != nil {
  493. return ps, tracerr.Wrap(fmt.Errorf("Line %d: %s, err: %s", i, instruction.literal, err.Error()))
  494. }
  495. if ps.typ == "" {
  496. return ps, tracerr.Wrap(fmt.Errorf("Line %d: Set type not defined", i))
  497. }
  498. ps.instructions = append(ps.instructions, *instruction)
  499. users[instruction.from] = true
  500. if instruction.typ == common.TxTypeTransfer || instruction.typ == common.TxTypeTransferToEthAddr || instruction.typ == common.TxTypeTransferToBJJ { // type: Transfer
  501. users[instruction.to] = true
  502. }
  503. }
  504. for u := range users {
  505. ps.users = append(ps.users, u)
  506. }
  507. sort.Strings(ps.users)
  508. return ps, nil
  509. }