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.

523 lines
13 KiB

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