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.

507 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. // set real Idx
  255. testTx.L1Tx.FromIdx = tc.Users[testTx.fromIdxName].Accounts[testTx.L1Tx.TokenID].Idx
  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. tc.currBlock.L1UserTxs = append(tc.currBlock.L1UserTxs, testTx.L1Tx)
  264. }
  265. if err = tc.setIdxs(); err != nil {
  266. log.Error(err)
  267. return nil, err
  268. }
  269. // advance batch
  270. tc.toForgeNum++
  271. if tc.toForgeNum == tc.openToForge {
  272. tc.openToForge++
  273. newQueue := []L1Tx{}
  274. tc.queues = append(tc.queues, newQueue)
  275. }
  276. case typeNewBlock:
  277. blocks = append(blocks, tc.currBlock)
  278. tc.currBlock = BlockData{}
  279. case typeRegisterToken:
  280. newToken := common.Token{
  281. TokenID: inst.tokenID,
  282. EthBlockNum: int64(len(blocks)),
  283. }
  284. if inst.tokenID != tc.lastRegisteredTokenID+1 {
  285. return nil, fmt.Errorf("Line %d: RegisterToken TokenID should be sequential, expected TokenID: %d, defined TokenID: %d", inst.lineNum, tc.lastRegisteredTokenID+1, inst.tokenID)
  286. }
  287. tc.lastRegisteredTokenID++
  288. tc.currBlock.RegisteredTokens = append(tc.currBlock.RegisteredTokens, newToken)
  289. default:
  290. return nil, fmt.Errorf("Line %d: Unexpected type: %s", inst.lineNum, inst.typ)
  291. }
  292. }
  293. return blocks, nil
  294. }
  295. // calculateIdxsForL1Txs calculates new Idx for new created accounts. If
  296. // 'isCoordinatorTxs==true', adds the tx to tc.currBatch.L1CoordinatorTxs.
  297. func (tc *TestContext) calculateIdxForL1Txs(isCoordinatorTxs bool, txs []L1Tx) error {
  298. // for each batch.L1CoordinatorTxs of the queues[ToForgeNum], calculate the Idx
  299. for i := 0; i < len(txs); i++ {
  300. tx := txs[i]
  301. if tx.L1Tx.Type == common.TxTypeCreateAccountDeposit || tx.L1Tx.Type == common.TxTypeCreateAccountDepositTransfer {
  302. if tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID] != nil { // if account already exists, return error
  303. return fmt.Errorf("Can not create same account twice (same User & same TokenID) (this is a design property of Transakcio)")
  304. }
  305. tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID] = &Account{
  306. Idx: common.Idx(tc.idx),
  307. Nonce: common.Nonce(0),
  308. }
  309. tc.l1CreatedAccounts[idxTokenIDToString(tx.fromIdxName, tx.L1Tx.TokenID)] = tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID]
  310. tc.idx++
  311. }
  312. if isCoordinatorTxs {
  313. tc.currBatch.L1CoordinatorTxs = append(tc.currBatch.L1CoordinatorTxs, tx.L1Tx)
  314. }
  315. }
  316. return nil
  317. }
  318. // setIdxs sets the Idxs to the transactions of the tc.currBatch
  319. func (tc *TestContext) setIdxs() error {
  320. // once Idxs are calculated, update transactions to use the new Idxs
  321. for i := 0; i < len(tc.currBatch.testL2Txs); i++ {
  322. testTx := &tc.currBatch.testL2Txs[i]
  323. if tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID] == nil {
  324. 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)
  325. }
  326. if testTx.L2Tx.Type == common.TxTypeTransfer {
  327. if _, ok := tc.l1CreatedAccounts[idxTokenIDToString(testTx.toIdxName, testTx.tokenID)]; !ok {
  328. 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)
  329. }
  330. }
  331. tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Nonce++
  332. testTx.L2Tx.Nonce = tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Nonce
  333. // set real Idx
  334. testTx.L2Tx.FromIdx = tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Idx
  335. if testTx.L2Tx.Type == common.TxTypeTransfer {
  336. testTx.L2Tx.ToIdx = tc.Users[testTx.toIdxName].Accounts[testTx.tokenID].Idx
  337. }
  338. nTx, err := common.NewL2Tx(&testTx.L2Tx)
  339. if err != nil {
  340. return err
  341. }
  342. testTx.L2Tx = *nTx
  343. tc.currBatch.L2Txs = append(tc.currBatch.L2Txs, testTx.L2Tx)
  344. }
  345. tc.currBlock.Batches = append(tc.currBlock.Batches, tc.currBatch)
  346. tc.currBatchNum++
  347. tc.currBatch = BatchData{}
  348. return nil
  349. }
  350. // addToL1Queue adds the L1Tx into the queue that is open and has space
  351. func (tc *TestContext) addToL1Queue(tx L1Tx) {
  352. if len(tc.queues[tc.openToForge]) >= tc.rollupConstMaxL1UserTx {
  353. // if current OpenToForge queue reached its Max, move into a
  354. // new queue
  355. tc.openToForge++
  356. newQueue := []L1Tx{}
  357. tc.queues = append(tc.queues, newQueue)
  358. }
  359. tc.queues[tc.openToForge] = append(tc.queues[tc.openToForge], tx)
  360. }
  361. func (tc *TestContext) checkIfAccountExists(tf string, inst instruction) error {
  362. if tc.Users[tf].Accounts[inst.tokenID] == nil {
  363. return fmt.Errorf("%s at User: %s, for TokenID: %d, while account not created yet", inst.typ, tf, inst.tokenID)
  364. }
  365. return nil
  366. }
  367. func (tc *TestContext) checkIfTokenIsRegistered(inst instruction) error {
  368. if inst.tokenID > tc.lastRegisteredTokenID {
  369. return fmt.Errorf("Can not process %s: TokenID %d not registered, last registered TokenID: %d", inst.typ, inst.tokenID, tc.lastRegisteredTokenID)
  370. }
  371. return nil
  372. }
  373. // GeneratePoolL2Txs returns an array of common.PoolL2Tx from a given set. It
  374. // uses the accounts (keys & nonces) of the TestContext.
  375. func (tc *TestContext) GeneratePoolL2Txs(set string) ([]common.PoolL2Tx, error) {
  376. parser := newParser(strings.NewReader(set))
  377. parsedSet, err := parser.parse()
  378. if err != nil {
  379. return nil, err
  380. }
  381. tc.Instructions = parsedSet.instructions
  382. tc.accountsNames = parsedSet.accounts
  383. tc.generateKeys(tc.accountsNames)
  384. txs := []common.PoolL2Tx{}
  385. for _, inst := range tc.Instructions {
  386. switch inst.typ {
  387. case common.TxTypeTransfer:
  388. if err := tc.checkIfAccountExists(inst.from, inst); err != nil {
  389. log.Error(err)
  390. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  391. }
  392. if err := tc.checkIfAccountExists(inst.to, inst); err != nil {
  393. log.Error(err)
  394. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  395. }
  396. tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
  397. // if account of receiver does not exist, don't use
  398. // ToIdx, and use only ToEthAddr & ToBJJ
  399. tx := common.PoolL2Tx{
  400. FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
  401. ToIdx: tc.Users[inst.to].Accounts[inst.tokenID].Idx,
  402. ToEthAddr: tc.Users[inst.to].Addr,
  403. ToBJJ: tc.Users[inst.to].BJJ.Public(),
  404. TokenID: inst.tokenID,
  405. Amount: big.NewInt(int64(inst.amount)),
  406. Fee: common.FeeSelector(inst.fee),
  407. Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce,
  408. State: common.PoolL2TxStatePending,
  409. Timestamp: time.Now(),
  410. RqToEthAddr: common.EmptyAddr,
  411. RqToBJJ: nil,
  412. Type: common.TxTypeTransfer,
  413. }
  414. nTx, err := common.NewPoolL2Tx(&tx)
  415. if err != nil {
  416. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  417. }
  418. tx = *nTx
  419. // perform signature and set it to tx.Signature
  420. toSign, err := tx.HashToSign()
  421. if err != nil {
  422. return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
  423. }
  424. sig := tc.Users[inst.to].BJJ.SignPoseidon(toSign)
  425. tx.Signature = sig
  426. txs = append(txs, tx)
  427. case common.TxTypeExit:
  428. tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
  429. tx := common.PoolL2Tx{
  430. FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
  431. ToIdx: common.Idx(1), // as is an Exit
  432. TokenID: inst.tokenID,
  433. Amount: big.NewInt(int64(inst.amount)),
  434. Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce,
  435. Type: common.TxTypeExit,
  436. }
  437. txs = append(txs, tx)
  438. default:
  439. return nil, fmt.Errorf("Line %d: instruction type unrecognized: %s", inst.lineNum, inst.typ)
  440. }
  441. }
  442. return txs, nil
  443. }
  444. // generateKeys generates BabyJubJub & Address keys for the given list of
  445. // account names in a deterministic way. This means, that for the same given
  446. // 'accNames' in a certain order, the keys will be always the same.
  447. func (tc *TestContext) generateKeys(accNames []string) {
  448. for i := 1; i < len(accNames)+1; i++ {
  449. if _, ok := tc.Users[accNames[i-1]]; ok {
  450. // account already created
  451. continue
  452. }
  453. // babyjubjub key
  454. var sk babyjub.PrivateKey
  455. copy(sk[:], []byte(strconv.Itoa(i))) // only for testing
  456. // eth address
  457. var key ecdsa.PrivateKey
  458. key.D = big.NewInt(int64(i)) // only for testing
  459. key.PublicKey.X, key.PublicKey.Y = ethCrypto.S256().ScalarBaseMult(key.D.Bytes())
  460. key.Curve = ethCrypto.S256()
  461. addr := ethCrypto.PubkeyToAddress(key.PublicKey)
  462. u := User{
  463. BJJ: &sk,
  464. Addr: addr,
  465. Accounts: make(map[common.TokenID]*Account),
  466. }
  467. tc.Users[accNames[i-1]] = &u
  468. }
  469. }