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 transakcio
  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. // TestContext contains the data of the test
  16. type TestContext 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. // NewTestContext returns a new TestContext
  33. func NewTestContext(rollupConstMaxL1UserTx int) *TestContext {
  34. return &TestContext{
  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. CreatedAccounts []common.Account
  76. }
  77. // L1Tx is the data structure used internally for transaction test generation,
  78. // which contains a common.L1Tx data plus some intermediate data for the
  79. // transaction generation.
  80. type L1Tx struct {
  81. lineNum int
  82. fromIdxName string
  83. toIdxName string
  84. L1Tx common.L1Tx
  85. }
  86. // L2Tx is the data structure used internally for transaction test generation,
  87. // which contains a common.L2Tx data plus some intermediate data for the
  88. // transaction generation.
  89. type L2Tx struct {
  90. lineNum int
  91. fromIdxName string
  92. toIdxName string
  93. tokenID common.TokenID
  94. L2Tx common.L2Tx
  95. }
  96. // GenerateBlocks returns an array of BlockData for a given set. It uses the
  97. // accounts (keys & nonces) of the TestContext.
  98. func (tc *TestContext) GenerateBlocks(set string) ([]BlockData, error) {
  99. parser := newParser(strings.NewReader(set))
  100. parsedSet, err := parser.parse()
  101. if err != nil {
  102. return nil, err
  103. }
  104. tc.Instructions = parsedSet.instructions
  105. tc.accountsNames = parsedSet.accounts
  106. tc.generateKeys(tc.accountsNames)
  107. var blocks []BlockData
  108. for _, inst := range parsedSet.instructions {
  109. switch inst.typ {
  110. case txTypeCreateAccountDepositCoordinator:
  111. if err := tc.checkIfTokenIsRegistered(inst); err != nil {
  112. log.Error(err)
  113. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  114. }
  115. tx := common.L1Tx{
  116. FromEthAddr: tc.Users[inst.from].Addr,
  117. FromBJJ: tc.Users[inst.from].BJJ.Public(),
  118. TokenID: inst.tokenID,
  119. LoadAmount: big.NewInt(int64(inst.loadAmount)),
  120. Type: common.TxTypeCreateAccountDeposit, // as txTypeCreateAccountDepositCoordinator is not valid oustide Transakcio package
  121. }
  122. testTx := L1Tx{
  123. lineNum: inst.lineNum,
  124. fromIdxName: inst.from,
  125. L1Tx: tx,
  126. }
  127. tc.currBatch.testL1CoordinatorTxs = append(tc.currBatch.testL1CoordinatorTxs, testTx)
  128. case common.TxTypeCreateAccountDeposit, common.TxTypeCreateAccountDepositTransfer:
  129. if err := tc.checkIfTokenIsRegistered(inst); err != nil {
  130. log.Error(err)
  131. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  132. }
  133. tx := common.L1Tx{
  134. FromEthAddr: tc.Users[inst.from].Addr,
  135. FromBJJ: tc.Users[inst.from].BJJ.Public(),
  136. TokenID: inst.tokenID,
  137. LoadAmount: big.NewInt(int64(inst.loadAmount)),
  138. Type: inst.typ,
  139. }
  140. if inst.typ == common.TxTypeCreateAccountDepositTransfer {
  141. tx.Amount = big.NewInt(int64(inst.amount))
  142. }
  143. testTx := L1Tx{
  144. lineNum: inst.lineNum,
  145. fromIdxName: inst.from,
  146. toIdxName: inst.to,
  147. L1Tx: tx,
  148. }
  149. tc.addToL1Queue(testTx)
  150. case common.TxTypeDeposit, common.TxTypeDepositTransfer:
  151. if err := tc.checkIfTokenIsRegistered(inst); err != nil {
  152. log.Error(err)
  153. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  154. }
  155. if err := tc.checkIfAccountExists(inst.from, inst); err != nil {
  156. log.Error(err)
  157. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  158. }
  159. tx := common.L1Tx{
  160. TokenID: inst.tokenID,
  161. LoadAmount: big.NewInt(int64(inst.loadAmount)),
  162. Type: inst.typ,
  163. }
  164. if inst.typ == common.TxTypeDepositTransfer {
  165. tx.Amount = big.NewInt(int64(inst.amount))
  166. }
  167. testTx := L1Tx{
  168. lineNum: inst.lineNum,
  169. fromIdxName: inst.from,
  170. toIdxName: inst.to,
  171. L1Tx: tx,
  172. }
  173. tc.addToL1Queue(testTx)
  174. case common.TxTypeTransfer:
  175. if err := tc.checkIfTokenIsRegistered(inst); err != nil {
  176. log.Error(err)
  177. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  178. }
  179. tx := common.L2Tx{
  180. Amount: big.NewInt(int64(inst.amount)),
  181. Fee: common.FeeSelector(inst.fee),
  182. Type: common.TxTypeTransfer,
  183. }
  184. nTx, err := common.NewPoolL2Tx(tx.PoolL2Tx())
  185. if err != nil {
  186. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  187. }
  188. tx = nTx.L2Tx()
  189. tx.BatchNum = common.BatchNum(tc.currBatchNum) // when converted to PoolL2Tx BatchNum parameter is lost
  190. testTx := L2Tx{
  191. lineNum: inst.lineNum,
  192. fromIdxName: inst.from,
  193. toIdxName: inst.to,
  194. tokenID: inst.tokenID,
  195. L2Tx: tx,
  196. }
  197. tc.currBatch.testL2Txs = append(tc.currBatch.testL2Txs, testTx)
  198. case common.TxTypeExit:
  199. if err := tc.checkIfTokenIsRegistered(inst); err != nil {
  200. log.Error(err)
  201. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  202. }
  203. tx := common.L2Tx{
  204. ToIdx: common.Idx(1), // as is an Exit
  205. Amount: big.NewInt(int64(inst.amount)),
  206. Type: common.TxTypeExit,
  207. }
  208. tx.BatchNum = common.BatchNum(tc.currBatchNum) // when converted to PoolL2Tx BatchNum parameter is lost
  209. testTx := L2Tx{
  210. lineNum: inst.lineNum,
  211. fromIdxName: inst.from,
  212. toIdxName: inst.to,
  213. tokenID: inst.tokenID,
  214. L2Tx: tx,
  215. }
  216. tc.currBatch.testL2Txs = append(tc.currBatch.testL2Txs, testTx)
  217. case common.TxTypeForceExit:
  218. if err := tc.checkIfTokenIsRegistered(inst); err != nil {
  219. log.Error(err)
  220. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  221. }
  222. tx := common.L1Tx{
  223. ToIdx: common.Idx(1), // as is an Exit
  224. TokenID: inst.tokenID,
  225. Amount: big.NewInt(int64(inst.amount)),
  226. Type: common.TxTypeExit,
  227. }
  228. testTx := L1Tx{
  229. lineNum: inst.lineNum,
  230. fromIdxName: inst.from,
  231. toIdxName: inst.to,
  232. L1Tx: tx,
  233. }
  234. tc.addToL1Queue(testTx)
  235. case typeNewBatch:
  236. if err = tc.calculateIdxForL1Txs(true, tc.currBatch.testL1CoordinatorTxs); err != nil {
  237. return nil, err
  238. }
  239. if err = tc.setIdxs(); err != nil {
  240. log.Error(err)
  241. return nil, err
  242. }
  243. case typeNewBatchL1:
  244. // for each L1UserTx of the queues[ToForgeNum], calculate the Idx
  245. if err = tc.calculateIdxForL1Txs(false, tc.queues[tc.toForgeNum]); err != nil {
  246. return nil, err
  247. }
  248. if err = tc.calculateIdxForL1Txs(true, tc.currBatch.testL1CoordinatorTxs); err != nil {
  249. return nil, err
  250. }
  251. // once Idxs are calculated, update transactions to use the new Idxs
  252. for i := 0; i < len(tc.queues[tc.toForgeNum]); i++ {
  253. testTx := &tc.queues[tc.toForgeNum][i]
  254. if testTx.L1Tx.Type != common.TxTypeCreateAccountDeposit && testTx.L1Tx.Type != common.TxTypeCreateAccountDepositTransfer {
  255. testTx.L1Tx.FromIdx = tc.Users[testTx.fromIdxName].Accounts[testTx.L1Tx.TokenID].Idx
  256. }
  257. testTx.L1Tx.FromEthAddr = tc.Users[testTx.fromIdxName].Addr
  258. testTx.L1Tx.FromBJJ = tc.Users[testTx.fromIdxName].BJJ.Public()
  259. if testTx.toIdxName == "" {
  260. testTx.L1Tx.ToIdx = common.Idx(0)
  261. } else {
  262. testTx.L1Tx.ToIdx = tc.Users[testTx.toIdxName].Accounts[testTx.L1Tx.TokenID].Idx
  263. }
  264. if testTx.L1Tx.Type == common.TxTypeExit {
  265. testTx.L1Tx.ToIdx = common.Idx(1)
  266. }
  267. bn := common.BatchNum(tc.currBatchNum)
  268. testTx.L1Tx.BatchNum = &bn
  269. nTx, err := common.NewL1Tx(&testTx.L1Tx)
  270. if err != nil {
  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 *TestContext) 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 Transakcio)")
  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 *TestContext) 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 err
  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 *TestContext) 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 *TestContext) 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 *TestContext) 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 TestContext.
  386. func (tc *TestContext) 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 *TestContext) 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. }