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.

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