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.

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