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.

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