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.

558 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. fee := false
  292. if setType == setTypeBlockchain {
  293. switch lit {
  294. case "Deposit":
  295. c.typ = common.TxTypeDeposit
  296. case "Exit":
  297. c.typ = common.TxTypeExit
  298. fee = true
  299. case "Transfer":
  300. c.typ = common.TxTypeTransfer
  301. transferring = true
  302. fee = true
  303. case "CreateAccountDeposit":
  304. c.typ = common.TxTypeCreateAccountDeposit
  305. case "CreateAccountDepositTransfer":
  306. c.typ = common.TxTypeCreateAccountDepositTransfer
  307. transferring = true
  308. case "CreateAccountCoordinator":
  309. c.typ = txTypeCreateAccountDepositCoordinator
  310. // transferring is false, as the Coordinator tx transfer will be 0
  311. case "DepositTransfer":
  312. c.typ = common.TxTypeDepositTransfer
  313. transferring = true
  314. case "ForceTransfer":
  315. c.typ = common.TxTypeForceTransfer
  316. transferring = true
  317. case "ForceExit":
  318. c.typ = common.TxTypeForceExit
  319. default:
  320. return c, fmt.Errorf("Unexpected Blockchain tx type: %s", lit)
  321. }
  322. } else if setType == setTypePoolL2 {
  323. switch lit {
  324. case "PoolTransfer":
  325. c.typ = common.TxTypeTransfer
  326. transferring = true
  327. fee = true
  328. case "PoolTransferToEthAddr":
  329. c.typ = common.TxTypeTransferToEthAddr
  330. transferring = true
  331. fee = true
  332. case "PoolTransferToBJJ":
  333. c.typ = common.TxTypeTransferToBJJ
  334. transferring = true
  335. fee = true
  336. case "PoolExit":
  337. c.typ = common.TxTypeExit
  338. fee = true
  339. default:
  340. return c, fmt.Errorf("Unexpected PoolL2 tx type: %s", lit)
  341. }
  342. } else {
  343. return c, fmt.Errorf("Invalid set type: '%s'. Valid set types: 'Blockchain', 'PoolL2'", setType)
  344. }
  345. if err := p.expectChar(c, "("); err != nil {
  346. return c, err
  347. }
  348. _, lit = p.scanIgnoreWhitespace()
  349. c.literal += lit
  350. tidI, err := strconv.Atoi(lit)
  351. if err != nil {
  352. line, _ := p.s.r.ReadString('\n')
  353. c.literal += line
  354. return c, err
  355. }
  356. c.tokenID = common.TokenID(tidI)
  357. if err := p.expectChar(c, ")"); err != nil {
  358. return c, err
  359. }
  360. _, lit = p.scanIgnoreWhitespace()
  361. c.literal += lit
  362. c.from = lit
  363. if c.typ == txTypeCreateAccountDepositCoordinator {
  364. line, _ := p.s.r.ReadString('\n')
  365. c.literal += line
  366. return c, nil
  367. }
  368. _, lit = p.scanIgnoreWhitespace()
  369. c.literal += lit
  370. if transferring {
  371. if lit != "-" {
  372. return c, fmt.Errorf("Expected '-', found '%s'", lit)
  373. }
  374. _, lit = p.scanIgnoreWhitespace()
  375. c.literal += lit
  376. c.to = lit
  377. _, lit = p.scanIgnoreWhitespace()
  378. c.literal += lit
  379. }
  380. if lit != ":" {
  381. line, _ := p.s.r.ReadString('\n')
  382. c.literal += line
  383. return c, fmt.Errorf("Expected ':', found '%s'", lit)
  384. }
  385. if c.typ == common.TxTypeDepositTransfer ||
  386. c.typ == common.TxTypeCreateAccountDepositTransfer {
  387. // deposit case
  388. _, lit = p.scanIgnoreWhitespace()
  389. c.literal += lit
  390. loadAmount, err := strconv.Atoi(lit)
  391. if err != nil {
  392. line, _ := p.s.r.ReadString('\n')
  393. c.literal += line
  394. return c, err
  395. }
  396. c.loadAmount = uint64(loadAmount)
  397. if err := p.expectChar(c, ","); err != nil {
  398. return c, err
  399. }
  400. }
  401. _, lit = p.scanIgnoreWhitespace()
  402. c.literal += lit
  403. amount, err := strconv.Atoi(lit)
  404. if err != nil {
  405. line, _ := p.s.r.ReadString('\n')
  406. c.literal += line
  407. return c, err
  408. }
  409. if c.typ == common.TxTypeDeposit ||
  410. c.typ == common.TxTypeCreateAccountDeposit {
  411. c.loadAmount = uint64(amount)
  412. } else {
  413. c.amount = uint64(amount)
  414. }
  415. if fee {
  416. if err := p.expectChar(c, "("); err != nil {
  417. return c, err
  418. }
  419. _, lit = p.scanIgnoreWhitespace()
  420. c.literal += lit
  421. fee, err := strconv.Atoi(lit)
  422. if err != nil {
  423. line, _ := p.s.r.ReadString('\n')
  424. c.literal += line
  425. return c, err
  426. }
  427. if fee > common.MaxFeePlan-1 {
  428. line, _ := p.s.r.ReadString('\n')
  429. c.literal += line
  430. return c, fmt.Errorf("Fee %d can not be bigger than 255", fee)
  431. }
  432. c.fee = uint8(fee)
  433. if err := p.expectChar(c, ")"); err != nil {
  434. return c, err
  435. }
  436. }
  437. if tok == EOF {
  438. return nil, errof
  439. }
  440. return c, nil
  441. }
  442. func (p *parser) expectChar(c *instruction, ch string) error {
  443. _, lit := p.scanIgnoreWhitespace()
  444. c.literal += lit
  445. if lit != ch {
  446. line, _ := p.s.r.ReadString('\n')
  447. c.literal += line
  448. return fmt.Errorf("Expected '%s', found '%s'", ch, lit)
  449. }
  450. return nil
  451. }
  452. func idxTokenIDToString(idx string, tid common.TokenID) string {
  453. return idx + strconv.Itoa(int(tid))
  454. }
  455. // parse parses through reader
  456. func (p *parser) parse() (*parsedSet, error) {
  457. ps := &parsedSet{}
  458. i := 0 // lines will start counting at line 1
  459. users := make(map[string]bool)
  460. for {
  461. i++
  462. instruction, err := p.parseLine(ps.typ)
  463. if err == errof {
  464. break
  465. }
  466. if err == setTypeLine {
  467. if ps.typ != "" {
  468. 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)
  469. }
  470. if instruction.typ == "PoolL2" {
  471. ps.typ = setTypePoolL2
  472. } else if instruction.typ == "Blockchain" {
  473. ps.typ = setTypeBlockchain
  474. } else {
  475. log.Fatalf("Line %d: Invalid set type: '%s'. Valid set types: 'Blockchain', 'PoolL2'", i, instruction.typ)
  476. }
  477. continue
  478. }
  479. if err == commentLine {
  480. continue
  481. }
  482. instruction.lineNum = i
  483. if err == newEventLine {
  484. if instruction.typ == typeAddToken && instruction.tokenID == common.TokenID(0) {
  485. return ps, fmt.Errorf("Line %d: AddToken can not register TokenID 0", i)
  486. }
  487. ps.instructions = append(ps.instructions, *instruction)
  488. continue
  489. }
  490. if err != nil {
  491. return ps, fmt.Errorf("Line %d: %s, err: %s", i, instruction.literal, err.Error())
  492. }
  493. if ps.typ == "" {
  494. return ps, fmt.Errorf("Line %d: Set type not defined", i)
  495. }
  496. ps.instructions = append(ps.instructions, *instruction)
  497. users[instruction.from] = true
  498. if instruction.typ == common.TxTypeTransfer || instruction.typ == common.TxTypeTransferToEthAddr || instruction.typ == common.TxTypeTransferToBJJ { // type: Transfer
  499. users[instruction.to] = true
  500. }
  501. }
  502. for u := range users {
  503. ps.users = append(ps.users, u)
  504. }
  505. sort.Strings(ps.users)
  506. return ps, nil
  507. }