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.

519 lines
17 KiB

  1. package til
  2. import (
  3. "crypto/ecdsa"
  4. "fmt"
  5. "math/big"
  6. "strconv"
  7. "strings"
  8. "time"
  9. ethCommon "github.com/ethereum/go-ethereum/common"
  10. ethCrypto "github.com/ethereum/go-ethereum/crypto"
  11. "github.com/hermeznetwork/hermez-node/common"
  12. "github.com/hermeznetwork/hermez-node/log"
  13. "github.com/iden3/go-iden3-crypto/babyjub"
  14. )
  15. // Context contains the data of the test
  16. type Context struct {
  17. Instructions []instruction
  18. accountsNames []string
  19. Users map[string]*User
  20. lastRegisteredTokenID common.TokenID
  21. l1CreatedAccounts map[string]*Account
  22. // rollupConstMaxL1UserTx Maximum L1-user transactions allowed to be queued in a batch
  23. rollupConstMaxL1UserTx int
  24. idx int
  25. currBlock BlockData
  26. currBatch BatchData
  27. currBatchNum int
  28. queues [][]L1Tx
  29. toForgeNum int
  30. openToForge int
  31. }
  32. // NewContext returns a new Context
  33. func NewContext(rollupConstMaxL1UserTx int) *Context {
  34. return &Context{
  35. Users: make(map[string]*User),
  36. l1CreatedAccounts: make(map[string]*Account),
  37. lastRegisteredTokenID: 0,
  38. rollupConstMaxL1UserTx: rollupConstMaxL1UserTx,
  39. idx: common.UserThreshold,
  40. currBatchNum: 0,
  41. // start with 2 queues, one for toForge, and the other for openToForge
  42. queues: make([][]L1Tx, 2),
  43. toForgeNum: 0,
  44. openToForge: 1,
  45. }
  46. }
  47. // Account contains the data related to the account for a specific TokenID of a User
  48. type Account struct {
  49. Idx common.Idx
  50. Nonce common.Nonce
  51. }
  52. // User contains the data related to a testing user
  53. type User struct {
  54. BJJ *babyjub.PrivateKey
  55. Addr ethCommon.Address
  56. Accounts map[common.TokenID]*Account
  57. }
  58. // BlockData contains the information of a Block
  59. type BlockData struct {
  60. // block *common.Block // ethereum block
  61. // L1UserTxs that were accepted in the block
  62. L1UserTxs []common.L1Tx
  63. Batches []BatchData
  64. RegisteredTokens []common.Token
  65. }
  66. // BatchData contains the information of a Batch
  67. type BatchData struct {
  68. L1Batch bool // TODO: Remove once Batch.ForgeL1TxsNum is a pointer
  69. L1CoordinatorTxs []common.L1Tx
  70. testL1CoordinatorTxs []L1Tx
  71. L2Txs []common.L2Tx
  72. // testL2Tx are L2Txs without the Idx&EthAddr&BJJ setted, but with the
  73. // string that represents the account
  74. testL2Txs []L2Tx
  75. }
  76. // L1Tx is the data structure used internally for transaction test generation,
  77. // which contains a common.L1Tx data plus some intermediate data for the
  78. // transaction generation.
  79. type L1Tx struct {
  80. lineNum int
  81. fromIdxName string
  82. toIdxName string
  83. L1Tx common.L1Tx
  84. }
  85. // L2Tx is the data structure used internally for transaction test generation,
  86. // which contains a common.L2Tx data plus some intermediate data for the
  87. // transaction generation.
  88. type L2Tx struct {
  89. lineNum int
  90. fromIdxName string
  91. toIdxName string
  92. tokenID common.TokenID
  93. L2Tx common.L2Tx
  94. }
  95. // GenerateBlocks returns an array of BlockData for a given set. It uses the
  96. // accounts (keys & nonces) of the Context.
  97. func (tc *Context) GenerateBlocks(set string) ([]BlockData, error) {
  98. parser := newParser(strings.NewReader(set))
  99. parsedSet, err := parser.parse()
  100. if err != nil {
  101. return nil, err
  102. }
  103. tc.Instructions = parsedSet.instructions
  104. tc.accountsNames = parsedSet.accounts
  105. tc.generateKeys(tc.accountsNames)
  106. var blocks []BlockData
  107. for _, inst := range parsedSet.instructions {
  108. switch inst.typ {
  109. case txTypeCreateAccountDepositCoordinator:
  110. if err := tc.checkIfTokenIsRegistered(inst); err != nil {
  111. log.Error(err)
  112. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  113. }
  114. tx := common.L1Tx{
  115. FromEthAddr: tc.Users[inst.from].Addr,
  116. FromBJJ: tc.Users[inst.from].BJJ.Public(),
  117. TokenID: inst.tokenID,
  118. LoadAmount: big.NewInt(int64(inst.loadAmount)),
  119. Type: common.TxTypeCreateAccountDeposit, // as txTypeCreateAccountDepositCoordinator is not valid oustide Til package
  120. }
  121. testTx := L1Tx{
  122. lineNum: inst.lineNum,
  123. fromIdxName: inst.from,
  124. L1Tx: tx,
  125. }
  126. tc.currBatch.testL1CoordinatorTxs = append(tc.currBatch.testL1CoordinatorTxs, testTx)
  127. case common.TxTypeCreateAccountDeposit, common.TxTypeCreateAccountDepositTransfer:
  128. if err := tc.checkIfTokenIsRegistered(inst); err != nil {
  129. log.Error(err)
  130. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  131. }
  132. tx := common.L1Tx{
  133. FromEthAddr: tc.Users[inst.from].Addr,
  134. FromBJJ: tc.Users[inst.from].BJJ.Public(),
  135. TokenID: inst.tokenID,
  136. LoadAmount: big.NewInt(int64(inst.loadAmount)),
  137. Type: inst.typ,
  138. }
  139. if inst.typ == common.TxTypeCreateAccountDepositTransfer {
  140. tx.Amount = big.NewInt(int64(inst.amount))
  141. }
  142. testTx := L1Tx{
  143. lineNum: inst.lineNum,
  144. fromIdxName: inst.from,
  145. toIdxName: inst.to,
  146. L1Tx: tx,
  147. }
  148. tc.addToL1Queue(testTx)
  149. case common.TxTypeDeposit, common.TxTypeDepositTransfer:
  150. if err := tc.checkIfTokenIsRegistered(inst); err != nil {
  151. log.Error(err)
  152. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  153. }
  154. if err := tc.checkIfAccountExists(inst.from, inst); err != nil {
  155. log.Error(err)
  156. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  157. }
  158. tx := common.L1Tx{
  159. TokenID: inst.tokenID,
  160. LoadAmount: big.NewInt(int64(inst.loadAmount)),
  161. Type: inst.typ,
  162. }
  163. if inst.typ == common.TxTypeDepositTransfer {
  164. tx.Amount = big.NewInt(int64(inst.amount))
  165. }
  166. testTx := L1Tx{
  167. lineNum: inst.lineNum,
  168. fromIdxName: inst.from,
  169. toIdxName: inst.to,
  170. L1Tx: tx,
  171. }
  172. tc.addToL1Queue(testTx)
  173. case common.TxTypeTransfer:
  174. if err := tc.checkIfTokenIsRegistered(inst); err != nil {
  175. log.Error(err)
  176. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  177. }
  178. tx := common.L2Tx{
  179. Amount: big.NewInt(int64(inst.amount)),
  180. Fee: common.FeeSelector(inst.fee),
  181. Type: common.TxTypeTransfer,
  182. }
  183. nTx, err := common.NewPoolL2Tx(tx.PoolL2Tx())
  184. if err != nil {
  185. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  186. }
  187. tx = nTx.L2Tx()
  188. tx.BatchNum = common.BatchNum(tc.currBatchNum) // when converted to PoolL2Tx BatchNum parameter is lost
  189. testTx := L2Tx{
  190. lineNum: inst.lineNum,
  191. fromIdxName: inst.from,
  192. toIdxName: inst.to,
  193. tokenID: inst.tokenID,
  194. L2Tx: tx,
  195. }
  196. tc.currBatch.testL2Txs = append(tc.currBatch.testL2Txs, testTx)
  197. case common.TxTypeExit:
  198. if err := tc.checkIfTokenIsRegistered(inst); err != nil {
  199. log.Error(err)
  200. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  201. }
  202. tx := common.L2Tx{
  203. ToIdx: common.Idx(1), // as is an Exit
  204. Amount: big.NewInt(int64(inst.amount)),
  205. Type: common.TxTypeExit,
  206. }
  207. tx.BatchNum = common.BatchNum(tc.currBatchNum) // when converted to PoolL2Tx BatchNum parameter is lost
  208. testTx := L2Tx{
  209. lineNum: inst.lineNum,
  210. fromIdxName: inst.from,
  211. toIdxName: inst.to,
  212. tokenID: inst.tokenID,
  213. L2Tx: tx,
  214. }
  215. tc.currBatch.testL2Txs = append(tc.currBatch.testL2Txs, testTx)
  216. case common.TxTypeForceExit:
  217. if err := tc.checkIfTokenIsRegistered(inst); err != nil {
  218. log.Error(err)
  219. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  220. }
  221. tx := common.L1Tx{
  222. ToIdx: common.Idx(1), // as is an Exit
  223. TokenID: inst.tokenID,
  224. Amount: big.NewInt(int64(inst.amount)),
  225. Type: common.TxTypeExit,
  226. }
  227. testTx := L1Tx{
  228. lineNum: inst.lineNum,
  229. fromIdxName: inst.from,
  230. toIdxName: inst.to,
  231. L1Tx: tx,
  232. }
  233. tc.addToL1Queue(testTx)
  234. case typeNewBatch:
  235. if err = tc.calculateIdxForL1Txs(true, tc.currBatch.testL1CoordinatorTxs); err != nil {
  236. return nil, err
  237. }
  238. if err = tc.setIdxs(); err != nil {
  239. log.Error(err)
  240. return nil, err
  241. }
  242. case typeNewBatchL1:
  243. // for each L1UserTx of the queues[ToForgeNum], calculate the Idx
  244. if err = tc.calculateIdxForL1Txs(false, tc.queues[tc.toForgeNum]); err != nil {
  245. return nil, err
  246. }
  247. if err = tc.calculateIdxForL1Txs(true, tc.currBatch.testL1CoordinatorTxs); err != nil {
  248. return nil, err
  249. }
  250. // once Idxs are calculated, update transactions to use the real Idxs
  251. for i := 0; i < len(tc.queues[tc.toForgeNum]); i++ {
  252. testTx := &tc.queues[tc.toForgeNum][i]
  253. if testTx.L1Tx.Type != common.TxTypeCreateAccountDeposit && testTx.L1Tx.Type != common.TxTypeCreateAccountDepositTransfer {
  254. testTx.L1Tx.FromIdx = tc.Users[testTx.fromIdxName].Accounts[testTx.L1Tx.TokenID].Idx
  255. }
  256. testTx.L1Tx.FromEthAddr = tc.Users[testTx.fromIdxName].Addr
  257. testTx.L1Tx.FromBJJ = tc.Users[testTx.fromIdxName].BJJ.Public()
  258. if testTx.toIdxName == "" {
  259. testTx.L1Tx.ToIdx = common.Idx(0)
  260. } else {
  261. testTx.L1Tx.ToIdx = tc.Users[testTx.toIdxName].Accounts[testTx.L1Tx.TokenID].Idx
  262. }
  263. if testTx.L1Tx.Type == common.TxTypeExit {
  264. testTx.L1Tx.ToIdx = common.Idx(1)
  265. }
  266. bn := common.BatchNum(tc.currBatchNum)
  267. testTx.L1Tx.BatchNum = &bn
  268. nTx, err := common.NewL1Tx(&testTx.L1Tx)
  269. if err != nil {
  270. fmt.Println(testTx)
  271. return nil, fmt.Errorf("Line %d: %s", testTx.lineNum, err.Error())
  272. }
  273. testTx.L1Tx = *nTx
  274. tc.currBlock.L1UserTxs = append(tc.currBlock.L1UserTxs, testTx.L1Tx)
  275. }
  276. if err = tc.setIdxs(); err != nil {
  277. log.Error(err)
  278. return nil, err
  279. }
  280. // advance batch
  281. tc.toForgeNum++
  282. if tc.toForgeNum == tc.openToForge {
  283. tc.openToForge++
  284. newQueue := []L1Tx{}
  285. tc.queues = append(tc.queues, newQueue)
  286. }
  287. case typeNewBlock:
  288. blocks = append(blocks, tc.currBlock)
  289. tc.currBlock = BlockData{}
  290. case typeRegisterToken:
  291. newToken := common.Token{
  292. TokenID: inst.tokenID,
  293. EthBlockNum: int64(len(blocks)),
  294. }
  295. if inst.tokenID != tc.lastRegisteredTokenID+1 {
  296. return nil, fmt.Errorf("Line %d: RegisterToken TokenID should be sequential, expected TokenID: %d, defined TokenID: %d", inst.lineNum, tc.lastRegisteredTokenID+1, inst.tokenID)
  297. }
  298. tc.lastRegisteredTokenID++
  299. tc.currBlock.RegisteredTokens = append(tc.currBlock.RegisteredTokens, newToken)
  300. default:
  301. return nil, fmt.Errorf("Line %d: Unexpected type: %s", inst.lineNum, inst.typ)
  302. }
  303. }
  304. return blocks, nil
  305. }
  306. // calculateIdxsForL1Txs calculates new Idx for new created accounts. If
  307. // 'isCoordinatorTxs==true', adds the tx to tc.currBatch.L1CoordinatorTxs.
  308. func (tc *Context) calculateIdxForL1Txs(isCoordinatorTxs bool, txs []L1Tx) error {
  309. // for each batch.L1CoordinatorTxs of the queues[ToForgeNum], calculate the Idx
  310. for i := 0; i < len(txs); i++ {
  311. tx := txs[i]
  312. if tx.L1Tx.Type == common.TxTypeCreateAccountDeposit || tx.L1Tx.Type == common.TxTypeCreateAccountDepositTransfer {
  313. if tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID] != nil { // if account already exists, return error
  314. return fmt.Errorf("Can not create same account twice (same User & same TokenID) (this is a design property of Til)")
  315. }
  316. tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID] = &Account{
  317. Idx: common.Idx(tc.idx),
  318. Nonce: common.Nonce(0),
  319. }
  320. tc.l1CreatedAccounts[idxTokenIDToString(tx.fromIdxName, tx.L1Tx.TokenID)] = tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID]
  321. tc.idx++
  322. }
  323. if isCoordinatorTxs {
  324. tc.currBatch.L1CoordinatorTxs = append(tc.currBatch.L1CoordinatorTxs, tx.L1Tx)
  325. }
  326. }
  327. return nil
  328. }
  329. // setIdxs sets the Idxs to the transactions of the tc.currBatch
  330. func (tc *Context) setIdxs() error {
  331. // once Idxs are calculated, update transactions to use the new Idxs
  332. for i := 0; i < len(tc.currBatch.testL2Txs); i++ {
  333. testTx := &tc.currBatch.testL2Txs[i]
  334. if tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID] == nil {
  335. return fmt.Errorf("Line %d: %s from User %s for TokenID %d while account not created yet", testTx.lineNum, testTx.L2Tx.Type, testTx.fromIdxName, testTx.tokenID)
  336. }
  337. if testTx.L2Tx.Type == common.TxTypeTransfer {
  338. if _, ok := tc.l1CreatedAccounts[idxTokenIDToString(testTx.toIdxName, testTx.tokenID)]; !ok {
  339. return fmt.Errorf("Line %d: Can not create Transfer for a non existing account. Batch %d, ToIdx name: %s, TokenID: %d", testTx.lineNum, tc.currBatchNum, testTx.toIdxName, testTx.tokenID)
  340. }
  341. }
  342. tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Nonce++
  343. testTx.L2Tx.Nonce = tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Nonce
  344. // set real Idx
  345. testTx.L2Tx.FromIdx = tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Idx
  346. if testTx.L2Tx.Type == common.TxTypeTransfer {
  347. testTx.L2Tx.ToIdx = tc.Users[testTx.toIdxName].Accounts[testTx.tokenID].Idx
  348. }
  349. nTx, err := common.NewL2Tx(&testTx.L2Tx)
  350. if err != nil {
  351. return fmt.Errorf("Line %d: %s", testTx.lineNum, err.Error())
  352. }
  353. testTx.L2Tx = *nTx
  354. tc.currBatch.L2Txs = append(tc.currBatch.L2Txs, testTx.L2Tx)
  355. }
  356. tc.currBlock.Batches = append(tc.currBlock.Batches, tc.currBatch)
  357. tc.currBatchNum++
  358. tc.currBatch = BatchData{}
  359. return nil
  360. }
  361. // addToL1Queue adds the L1Tx into the queue that is open and has space
  362. func (tc *Context) addToL1Queue(tx L1Tx) {
  363. if len(tc.queues[tc.openToForge]) >= tc.rollupConstMaxL1UserTx {
  364. // if current OpenToForge queue reached its Max, move into a
  365. // new queue
  366. tc.openToForge++
  367. newQueue := []L1Tx{}
  368. tc.queues = append(tc.queues, newQueue)
  369. }
  370. tc.queues[tc.openToForge] = append(tc.queues[tc.openToForge], tx)
  371. }
  372. func (tc *Context) checkIfAccountExists(tf string, inst instruction) error {
  373. if tc.Users[tf].Accounts[inst.tokenID] == nil {
  374. return fmt.Errorf("%s at User: %s, for TokenID: %d, while account not created yet", inst.typ, tf, inst.tokenID)
  375. }
  376. return nil
  377. }
  378. func (tc *Context) checkIfTokenIsRegistered(inst instruction) error {
  379. if inst.tokenID > tc.lastRegisteredTokenID {
  380. return fmt.Errorf("Can not process %s: TokenID %d not registered, last registered TokenID: %d", inst.typ, inst.tokenID, tc.lastRegisteredTokenID)
  381. }
  382. return nil
  383. }
  384. // GeneratePoolL2Txs returns an array of common.PoolL2Tx from a given set. It
  385. // uses the accounts (keys & nonces) of the Context.
  386. func (tc *Context) GeneratePoolL2Txs(set string) ([]common.PoolL2Tx, error) {
  387. parser := newParser(strings.NewReader(set))
  388. parsedSet, err := parser.parse()
  389. if err != nil {
  390. return nil, err
  391. }
  392. tc.Instructions = parsedSet.instructions
  393. tc.accountsNames = parsedSet.accounts
  394. tc.generateKeys(tc.accountsNames)
  395. txs := []common.PoolL2Tx{}
  396. for _, inst := range tc.Instructions {
  397. switch inst.typ {
  398. case common.TxTypeTransfer:
  399. if err := tc.checkIfAccountExists(inst.from, inst); err != nil {
  400. log.Error(err)
  401. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  402. }
  403. if err := tc.checkIfAccountExists(inst.to, inst); err != nil {
  404. log.Error(err)
  405. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  406. }
  407. tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
  408. // if account of receiver does not exist, don't use
  409. // ToIdx, and use only ToEthAddr & ToBJJ
  410. tx := common.PoolL2Tx{
  411. FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
  412. ToIdx: tc.Users[inst.to].Accounts[inst.tokenID].Idx,
  413. ToEthAddr: tc.Users[inst.to].Addr,
  414. ToBJJ: tc.Users[inst.to].BJJ.Public(),
  415. TokenID: inst.tokenID,
  416. Amount: big.NewInt(int64(inst.amount)),
  417. Fee: common.FeeSelector(inst.fee),
  418. Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce,
  419. State: common.PoolL2TxStatePending,
  420. Timestamp: time.Now(),
  421. RqToEthAddr: common.EmptyAddr,
  422. RqToBJJ: nil,
  423. Type: common.TxTypeTransfer,
  424. }
  425. nTx, err := common.NewPoolL2Tx(&tx)
  426. if err != nil {
  427. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  428. }
  429. tx = *nTx
  430. // perform signature and set it to tx.Signature
  431. toSign, err := tx.HashToSign()
  432. if err != nil {
  433. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  434. }
  435. sig := tc.Users[inst.to].BJJ.SignPoseidon(toSign)
  436. tx.Signature = sig
  437. txs = append(txs, tx)
  438. case common.TxTypeExit:
  439. tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
  440. tx := common.PoolL2Tx{
  441. FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
  442. ToIdx: common.Idx(1), // as is an Exit
  443. TokenID: inst.tokenID,
  444. Amount: big.NewInt(int64(inst.amount)),
  445. Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce,
  446. Type: common.TxTypeExit,
  447. }
  448. txs = append(txs, tx)
  449. default:
  450. return nil, fmt.Errorf("Line %d: instruction type unrecognized: %s", inst.lineNum, inst.typ)
  451. }
  452. }
  453. return txs, nil
  454. }
  455. // generateKeys generates BabyJubJub & Address keys for the given list of
  456. // account names in a deterministic way. This means, that for the same given
  457. // 'accNames' in a certain order, the keys will be always the same.
  458. func (tc *Context) generateKeys(accNames []string) {
  459. for i := 1; i < len(accNames)+1; i++ {
  460. if _, ok := tc.Users[accNames[i-1]]; ok {
  461. // account already created
  462. continue
  463. }
  464. // babyjubjub key
  465. var sk babyjub.PrivateKey
  466. copy(sk[:], []byte(strconv.Itoa(i))) // only for testing
  467. // eth address
  468. var key ecdsa.PrivateKey
  469. key.D = big.NewInt(int64(i)) // only for testing
  470. key.PublicKey.X, key.PublicKey.Y = ethCrypto.S256().ScalarBaseMult(key.D.Bytes())
  471. key.Curve = ethCrypto.S256()
  472. addr := ethCrypto.PubkeyToAddress(key.PublicKey)
  473. u := User{
  474. BJJ: &sk,
  475. Addr: addr,
  476. Accounts: make(map[common.TokenID]*Account),
  477. }
  478. tc.Users[accNames[i-1]] = &u
  479. }
  480. }