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.

324 lines
11 KiB

  1. package transakcio
  2. import (
  3. "crypto/ecdsa"
  4. "math/big"
  5. "strconv"
  6. "strings"
  7. "time"
  8. ethCommon "github.com/ethereum/go-ethereum/common"
  9. ethCrypto "github.com/ethereum/go-ethereum/crypto"
  10. "github.com/hermeznetwork/hermez-node/common"
  11. "github.com/hermeznetwork/hermez-node/log"
  12. "github.com/iden3/go-iden3-crypto/babyjub"
  13. )
  14. // TestContext contains the data of the test
  15. type TestContext struct {
  16. Instructions []instruction
  17. accountsNames []string
  18. Users map[string]*User
  19. TokenIDs []common.TokenID
  20. l1CreatedAccounts map[string]*Account
  21. }
  22. // NewTestContext returns a new TestContext
  23. func NewTestContext() *TestContext {
  24. return &TestContext{
  25. Users: make(map[string]*User),
  26. l1CreatedAccounts: make(map[string]*Account),
  27. }
  28. }
  29. // Account contains the data related to the account for a specific TokenID of a User
  30. type Account struct {
  31. Idx common.Idx
  32. Nonce common.Nonce
  33. }
  34. // User contains the data related to a testing user
  35. type User struct {
  36. BJJ *babyjub.PrivateKey
  37. Addr ethCommon.Address
  38. Accounts map[common.TokenID]*Account
  39. }
  40. // BlockData contains the information of a Block
  41. type BlockData struct {
  42. // block *common.Block // ethereum block
  43. // L1UserTxs that were submitted in the block
  44. L1UserTxs []common.L1Tx
  45. Batches []BatchData
  46. RegisteredTokens []common.Token
  47. }
  48. // BatchData contains the information of a Batch
  49. type BatchData struct {
  50. L1Batch bool // TODO: Remove once Batch.ForgeL1TxsNum is a pointer
  51. // L1UserTxs that were forged in the batch
  52. L1UserTxs []common.L1Tx
  53. L1CoordinatorTxs []common.L1Tx
  54. L2Txs []common.L2Tx
  55. CreatedAccounts []common.Account
  56. ExitTree []common.ExitInfo
  57. Batch *common.Batch
  58. }
  59. // GenerateBlocks returns an array of BlockData for a given set. It uses the
  60. // accounts (keys & nonces) of the TestContext.
  61. func (tc *TestContext) GenerateBlocks(set string) []BlockData {
  62. parser := newParser(strings.NewReader(set))
  63. parsedSet, err := parser.parse()
  64. if err != nil {
  65. log.Fatal(err)
  66. }
  67. tc.Instructions = parsedSet.instructions
  68. tc.accountsNames = parsedSet.accounts
  69. tc.TokenIDs = parsedSet.tokenIDs
  70. tc.generateKeys(tc.accountsNames)
  71. var blocks []BlockData
  72. currBatchNum := 0
  73. var currBlock BlockData
  74. var currBatch BatchData
  75. idx := 256
  76. for _, inst := range parsedSet.instructions {
  77. switch inst.typ {
  78. case common.TxTypeCreateAccountDeposit, common.TxTypeCreateAccountDepositTransfer, txTypeCreateAccountDepositCoordinator:
  79. tx := common.L1Tx{
  80. // TxID
  81. FromEthAddr: tc.Users[inst.from].Addr,
  82. FromBJJ: tc.Users[inst.from].BJJ.Public(),
  83. TokenID: inst.tokenID,
  84. LoadAmount: big.NewInt(int64(inst.loadAmount)),
  85. Type: inst.typ,
  86. }
  87. if tc.Users[inst.from].Accounts[inst.tokenID] == nil { // if account is not set yet, set it and increment idx
  88. tc.Users[inst.from].Accounts[inst.tokenID] = &Account{
  89. Idx: common.Idx(idx),
  90. Nonce: common.Nonce(0),
  91. }
  92. tc.l1CreatedAccounts[idxTokenIDToString(inst.from, inst.tokenID)] = tc.Users[inst.from].Accounts[inst.tokenID]
  93. idx++
  94. }
  95. if inst.typ == common.TxTypeCreateAccountDepositTransfer {
  96. tx.Amount = big.NewInt(int64(inst.amount))
  97. }
  98. if inst.typ == txTypeCreateAccountDepositCoordinator {
  99. tx.Type = common.TxTypeCreateAccountDeposit // as txTypeCreateAccountDepositCoordinator is not valid oustide Transakcio package
  100. currBatch.L1CoordinatorTxs = append(currBatch.L1CoordinatorTxs, tx)
  101. } else {
  102. currBatch.L1UserTxs = append(currBatch.L1UserTxs, tx)
  103. }
  104. case common.TxTypeDeposit, common.TxTypeDepositTransfer:
  105. if tc.Users[inst.from].Accounts[inst.tokenID] == nil {
  106. log.Fatalf("Deposit at User %s for TokenID %d while account not created yet", inst.from, inst.tokenID)
  107. }
  108. tx := common.L1Tx{
  109. // TxID
  110. FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
  111. FromEthAddr: tc.Users[inst.from].Addr,
  112. FromBJJ: tc.Users[inst.from].BJJ.Public(),
  113. TokenID: inst.tokenID,
  114. LoadAmount: big.NewInt(int64(inst.loadAmount)),
  115. Type: inst.typ,
  116. }
  117. if tc.Users[inst.from].Accounts[inst.tokenID].Idx == common.Idx(0) {
  118. // if account.Idx is not set yet, set it and increment idx
  119. tc.Users[inst.from].Accounts[inst.tokenID].Idx = common.Idx(idx)
  120. tc.l1CreatedAccounts[idxTokenIDToString(inst.from, inst.tokenID)] = tc.Users[inst.from].Accounts[inst.tokenID]
  121. idx++
  122. }
  123. if inst.typ == common.TxTypeDepositTransfer {
  124. tx.Amount = big.NewInt(int64(inst.amount))
  125. // if ToIdx is not set yet, set it and increment idx
  126. if tc.Users[inst.to].Accounts[inst.tokenID].Idx == common.Idx(0) {
  127. tc.Users[inst.to].Accounts[inst.tokenID].Idx = common.Idx(idx)
  128. tc.l1CreatedAccounts[idxTokenIDToString(inst.to, inst.tokenID)] = tc.Users[inst.to].Accounts[inst.tokenID]
  129. tx.ToIdx = common.Idx(idx)
  130. idx++
  131. } else {
  132. // if Idx account of To already exist, use it for ToIdx
  133. tx.ToIdx = tc.Users[inst.to].Accounts[inst.tokenID].Idx
  134. }
  135. }
  136. currBatch.L1UserTxs = append(currBatch.L1UserTxs, tx)
  137. case common.TxTypeTransfer:
  138. if tc.Users[inst.from].Accounts[inst.tokenID] == nil {
  139. log.Fatalf("Transfer from User %s for TokenID %d while account not created yet", inst.from, inst.tokenID)
  140. }
  141. // if account of receiver does not exist, create a new CoordinatorL1Tx creating the account
  142. if _, ok := tc.l1CreatedAccounts[idxTokenIDToString(inst.to, inst.tokenID)]; !ok {
  143. log.Fatalf("Can not create Transfer for a non existing account. Batch %d, Instruction: %s", currBatchNum, inst)
  144. }
  145. tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
  146. tx := common.L2Tx{
  147. FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
  148. ToIdx: tc.Users[inst.to].Accounts[inst.tokenID].Idx,
  149. Amount: big.NewInt(int64(inst.amount)),
  150. Fee: common.FeeSelector(inst.fee),
  151. Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce,
  152. Type: common.TxTypeTransfer,
  153. }
  154. nTx, err := common.NewPoolL2Tx(tx.PoolL2Tx())
  155. if err != nil {
  156. log.Fatal(err)
  157. }
  158. tx = nTx.L2Tx()
  159. tx.BatchNum = common.BatchNum(currBatchNum) // when converted to PoolL2Tx BatchNum parameter is lost
  160. currBatch.L2Txs = append(currBatch.L2Txs, tx)
  161. case common.TxTypeExit:
  162. tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
  163. tx := common.L2Tx{
  164. FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
  165. ToIdx: common.Idx(1), // as is an Exit
  166. Amount: big.NewInt(int64(inst.amount)),
  167. Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce,
  168. Type: common.TxTypeExit,
  169. }
  170. nTx, err := common.NewPoolL2Tx(tx.PoolL2Tx())
  171. if err != nil {
  172. log.Fatal(err)
  173. }
  174. tx = nTx.L2Tx()
  175. currBatch.L2Txs = append(currBatch.L2Txs, tx)
  176. case common.TxTypeForceExit:
  177. tx := common.L1Tx{
  178. FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
  179. ToIdx: common.Idx(1), // as is an Exit
  180. TokenID: inst.tokenID,
  181. Amount: big.NewInt(int64(inst.amount)),
  182. Type: common.TxTypeExit,
  183. }
  184. currBatch.L1UserTxs = append(currBatch.L1UserTxs, tx)
  185. case typeNewBatch:
  186. currBlock.Batches = append(currBlock.Batches, currBatch)
  187. currBatchNum++
  188. currBatch = BatchData{}
  189. case typeNewBlock:
  190. currBlock.Batches = append(currBlock.Batches, currBatch)
  191. currBatchNum++
  192. currBatch = BatchData{}
  193. blocks = append(blocks, currBlock)
  194. currBlock = BlockData{}
  195. default:
  196. log.Fatalf("Unexpected type: %s", inst.typ)
  197. }
  198. }
  199. currBlock.Batches = append(currBlock.Batches, currBatch)
  200. blocks = append(blocks, currBlock)
  201. return blocks
  202. }
  203. // GeneratePoolL2Txs returns an array of common.PoolL2Tx from a given set. It
  204. // uses the accounts (keys & nonces) of the TestContext.
  205. func (tc *TestContext) GeneratePoolL2Txs(set string) []common.PoolL2Tx {
  206. parser := newParser(strings.NewReader(set))
  207. parsedSet, err := parser.parse()
  208. if err != nil {
  209. log.Fatal(err)
  210. }
  211. tc.Instructions = parsedSet.instructions
  212. tc.accountsNames = parsedSet.accounts
  213. tc.TokenIDs = parsedSet.tokenIDs
  214. tc.generateKeys(tc.accountsNames)
  215. txs := []common.PoolL2Tx{}
  216. for _, inst := range tc.Instructions {
  217. switch inst.typ {
  218. case common.TxTypeTransfer:
  219. if tc.Users[inst.from].Accounts[inst.tokenID] == nil {
  220. log.Fatalf("Transfer from User %s for TokenID %d while account not created yet", inst.from, inst.tokenID)
  221. }
  222. if tc.Users[inst.to].Accounts[inst.tokenID] == nil {
  223. log.Fatalf("Transfer to User %s for TokenID %d while account not created yet", inst.to, inst.tokenID)
  224. }
  225. tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
  226. // if account of receiver does not exist, don't use
  227. // ToIdx, and use only ToEthAddr & ToBJJ
  228. tx := common.PoolL2Tx{
  229. FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
  230. ToIdx: tc.Users[inst.to].Accounts[inst.tokenID].Idx,
  231. ToEthAddr: tc.Users[inst.to].Addr,
  232. ToBJJ: tc.Users[inst.to].BJJ.Public(),
  233. TokenID: inst.tokenID,
  234. Amount: big.NewInt(int64(inst.amount)),
  235. Fee: common.FeeSelector(inst.fee),
  236. Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce,
  237. State: common.PoolL2TxStatePending,
  238. Timestamp: time.Now(),
  239. RqToEthAddr: common.EmptyAddr,
  240. RqToBJJ: nil,
  241. Type: common.TxTypeTransfer,
  242. }
  243. nTx, err := common.NewPoolL2Tx(&tx)
  244. if err != nil {
  245. log.Fatal(err)
  246. }
  247. tx = *nTx
  248. // perform signature and set it to tx.Signature
  249. toSign, err := tx.HashToSign()
  250. if err != nil {
  251. log.Fatal(err)
  252. }
  253. sig := tc.Users[inst.to].BJJ.SignPoseidon(toSign)
  254. tx.Signature = sig
  255. txs = append(txs, tx)
  256. case common.TxTypeExit:
  257. tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
  258. tx := common.PoolL2Tx{
  259. FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
  260. ToIdx: common.Idx(1), // as is an Exit
  261. TokenID: inst.tokenID,
  262. Amount: big.NewInt(int64(inst.amount)),
  263. Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce,
  264. Type: common.TxTypeExit,
  265. }
  266. txs = append(txs, tx)
  267. default:
  268. log.Fatalf("instruction type unrecognized: %s", inst.typ)
  269. }
  270. }
  271. return txs
  272. }
  273. // generateKeys generates BabyJubJub & Address keys for the given list of
  274. // account names in a deterministic way. This means, that for the same given
  275. // 'accNames' in a certain order, the keys will be always the same.
  276. func (tc *TestContext) generateKeys(accNames []string) {
  277. for i := 1; i < len(accNames)+1; i++ {
  278. if _, ok := tc.Users[accNames[i-1]]; ok {
  279. // account already created
  280. continue
  281. }
  282. // babyjubjub key
  283. var sk babyjub.PrivateKey
  284. copy(sk[:], []byte(strconv.Itoa(i))) // only for testing
  285. // eth address
  286. var key ecdsa.PrivateKey
  287. key.D = big.NewInt(int64(i)) // only for testing
  288. key.PublicKey.X, key.PublicKey.Y = ethCrypto.S256().ScalarBaseMult(key.D.Bytes())
  289. key.Curve = ethCrypto.S256()
  290. addr := ethCrypto.PubkeyToAddress(key.PublicKey)
  291. u := User{
  292. BJJ: &sk,
  293. Addr: addr,
  294. Accounts: make(map[common.TokenID]*Account),
  295. }
  296. tc.Users[accNames[i-1]] = &u
  297. }
  298. }