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.

545 lines
13 KiB

  1. package transakcio
  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. // typeRegisterToken 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 typeRegisterToken common.TxType = "InstrTypeRegisterToken" //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. // type string
  59. instructions []instruction
  60. accounts []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 == "RegisterToken" {
  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 = typeRegisterToken
  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 "PoolExit":
  324. c.typ = common.TxTypeExit
  325. default:
  326. return c, fmt.Errorf("Unexpected PoolL2 tx type: %s", lit)
  327. }
  328. } else {
  329. return c, fmt.Errorf("Invalid set type: '%s'. Valid set types: 'Blockchain', 'PoolL2'", setType)
  330. }
  331. if err := p.expectChar(c, "("); err != nil {
  332. return c, err
  333. }
  334. _, lit = p.scanIgnoreWhitespace()
  335. c.literal += lit
  336. tidI, err := strconv.Atoi(lit)
  337. if err != nil {
  338. line, _ := p.s.r.ReadString('\n')
  339. c.literal += line
  340. return c, err
  341. }
  342. c.tokenID = common.TokenID(tidI)
  343. if err := p.expectChar(c, ")"); err != nil {
  344. return c, err
  345. }
  346. _, lit = p.scanIgnoreWhitespace()
  347. c.literal += lit
  348. c.from = lit
  349. if c.typ == txTypeCreateAccountDepositCoordinator {
  350. line, _ := p.s.r.ReadString('\n')
  351. c.literal += line
  352. return c, nil
  353. }
  354. _, lit = p.scanIgnoreWhitespace()
  355. c.literal += lit
  356. if transferring {
  357. if lit != "-" {
  358. return c, fmt.Errorf("Expected '-', found '%s'", lit)
  359. }
  360. _, lit = p.scanIgnoreWhitespace()
  361. c.literal += lit
  362. c.to = lit
  363. _, lit = p.scanIgnoreWhitespace()
  364. c.literal += lit
  365. }
  366. if lit != ":" {
  367. line, _ := p.s.r.ReadString('\n')
  368. c.literal += line
  369. return c, fmt.Errorf("Expected ':', found '%s'", lit)
  370. }
  371. if c.typ == common.TxTypeDepositTransfer ||
  372. c.typ == common.TxTypeCreateAccountDepositTransfer {
  373. // deposit case
  374. _, lit = p.scanIgnoreWhitespace()
  375. c.literal += lit
  376. loadAmount, err := strconv.Atoi(lit)
  377. if err != nil {
  378. line, _ := p.s.r.ReadString('\n')
  379. c.literal += line
  380. return c, err
  381. }
  382. c.loadAmount = uint64(loadAmount)
  383. if err := p.expectChar(c, ","); err != nil {
  384. return c, err
  385. }
  386. }
  387. _, lit = p.scanIgnoreWhitespace()
  388. c.literal += lit
  389. amount, err := strconv.Atoi(lit)
  390. if err != nil {
  391. line, _ := p.s.r.ReadString('\n')
  392. c.literal += line
  393. return c, err
  394. }
  395. if c.typ == common.TxTypeDeposit ||
  396. c.typ == common.TxTypeCreateAccountDeposit {
  397. c.loadAmount = uint64(amount)
  398. } else {
  399. c.amount = uint64(amount)
  400. }
  401. if transferring {
  402. if err := p.expectChar(c, "("); err != nil {
  403. return c, err
  404. }
  405. _, lit = p.scanIgnoreWhitespace()
  406. c.literal += lit
  407. fee, err := strconv.Atoi(lit)
  408. if err != nil {
  409. line, _ := p.s.r.ReadString('\n')
  410. c.literal += line
  411. return c, err
  412. }
  413. if fee > common.MaxFeePlan-1 {
  414. line, _ := p.s.r.ReadString('\n')
  415. c.literal += line
  416. return c, fmt.Errorf("Fee %d can not be bigger than 255", fee)
  417. }
  418. c.fee = uint8(fee)
  419. if err := p.expectChar(c, ")"); err != nil {
  420. return c, err
  421. }
  422. }
  423. if tok == EOF {
  424. return nil, errof
  425. }
  426. return c, nil
  427. }
  428. func (p *parser) expectChar(c *instruction, ch string) error {
  429. _, lit := p.scanIgnoreWhitespace()
  430. c.literal += lit
  431. if lit != ch {
  432. line, _ := p.s.r.ReadString('\n')
  433. c.literal += line
  434. return fmt.Errorf("Expected '%s', found '%s'", ch, lit)
  435. }
  436. return nil
  437. }
  438. func idxTokenIDToString(idx string, tid common.TokenID) string {
  439. return idx + strconv.Itoa(int(tid))
  440. }
  441. // parse parses through reader
  442. func (p *parser) parse() (*parsedSet, error) {
  443. ps := &parsedSet{}
  444. i := 0 // lines will start counting at line 1
  445. accounts := make(map[string]bool)
  446. var setTypeOfSet setType
  447. for {
  448. i++
  449. instruction, err := p.parseLine(setTypeOfSet)
  450. if err == errof {
  451. break
  452. }
  453. if err == setTypeLine {
  454. if setTypeOfSet != "" {
  455. return ps, fmt.Errorf("Line %d: Instruction of 'Type: %s' when there is already a previous instruction 'Type: %s' defined", i, instruction.typ, setTypeOfSet)
  456. }
  457. if instruction.typ == "PoolL2" {
  458. setTypeOfSet = setTypePoolL2
  459. } else if instruction.typ == "Blockchain" {
  460. setTypeOfSet = setTypeBlockchain
  461. } else {
  462. log.Fatalf("Line %d: Invalid set type: '%s'. Valid set types: 'Blockchain', 'PoolL2'", i, instruction.typ)
  463. }
  464. continue
  465. }
  466. if err == commentLine {
  467. continue
  468. }
  469. instruction.lineNum = i
  470. if err == newEventLine {
  471. if instruction.typ == typeRegisterToken && instruction.tokenID == common.TokenID(0) {
  472. return ps, fmt.Errorf("Line %d: RegisterToken can not register TokenID 0", i)
  473. }
  474. ps.instructions = append(ps.instructions, *instruction)
  475. continue
  476. }
  477. if err != nil {
  478. return ps, fmt.Errorf("Line %d: %s, err: %s", i, instruction.literal, err.Error())
  479. }
  480. if setTypeOfSet == "" {
  481. return ps, fmt.Errorf("Line %d: Set type not defined", i)
  482. }
  483. ps.instructions = append(ps.instructions, *instruction)
  484. accounts[instruction.from] = true
  485. if instruction.typ == common.TxTypeTransfer { // type: Transfer
  486. accounts[instruction.to] = true
  487. }
  488. }
  489. for a := range accounts {
  490. ps.accounts = append(ps.accounts, a)
  491. }
  492. sort.Strings(ps.accounts)
  493. return ps, nil
  494. }