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.

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