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.

430 lines
9.8 KiB

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