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.

340 lines
11 KiB

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