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.

332 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. tx = nTx.L2Tx()
  169. tx.BatchNum = common.BatchNum(currBatchNum) // when converted to PoolL2Tx BatchNum parameter is lost
  170. currBatch.L2Txs = append(currBatch.L2Txs, tx)
  171. case common.TxTypeExit:
  172. tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
  173. tx := common.L2Tx{
  174. FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
  175. ToIdx: common.Idx(1), // as is an Exit
  176. Amount: big.NewInt(int64(inst.amount)),
  177. Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce,
  178. Type: common.TxTypeExit,
  179. }
  180. nTx, err := common.NewPoolL2Tx(tx.PoolL2Tx())
  181. if err != nil {
  182. panic(err)
  183. }
  184. tx = nTx.L2Tx()
  185. currBatch.L2Txs = append(currBatch.L2Txs, tx)
  186. case common.TxTypeForceExit:
  187. tx := common.L1Tx{
  188. FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
  189. ToIdx: common.Idx(1), // as is an Exit
  190. TokenID: inst.tokenID,
  191. Amount: big.NewInt(int64(inst.amount)),
  192. Type: common.TxTypeExit,
  193. }
  194. currBatch.L1UserTxs = append(currBatch.L1UserTxs, tx)
  195. case typeNewBatch:
  196. currBlock.Batches = append(currBlock.Batches, currBatch)
  197. currBatchNum++
  198. currBatch = BatchData{}
  199. case typeNewBlock:
  200. currBlock.Batches = append(currBlock.Batches, currBatch)
  201. currBatchNum++
  202. currBatch = BatchData{}
  203. blocks = append(blocks, currBlock)
  204. currBlock = BlockData{}
  205. default:
  206. log.Fatalf("Unexpected type: %s", inst.typ)
  207. }
  208. }
  209. currBlock.Batches = append(currBlock.Batches, currBatch)
  210. blocks = append(blocks, currBlock)
  211. return blocks
  212. }
  213. // GeneratePoolL2Txs returns an array of common.PoolL2Tx from a given set. It
  214. // uses the accounts (keys & nonces) of the TestContext.
  215. func (tc *TestContext) GeneratePoolL2Txs(set string) []common.PoolL2Tx {
  216. parser := newParser(strings.NewReader(set))
  217. parsedSet, err := parser.parse()
  218. require.Nil(tc.t, err)
  219. tc.Instructions = parsedSet.instructions
  220. tc.accountsNames = parsedSet.accounts
  221. tc.TokenIDs = parsedSet.tokenIDs
  222. tc.generateKeys(tc.accountsNames)
  223. txs := []common.PoolL2Tx{}
  224. for _, inst := range tc.Instructions {
  225. switch inst.typ {
  226. case common.TxTypeTransfer:
  227. if tc.Users[inst.from].Accounts[inst.tokenID] == nil {
  228. log.Fatalf("Transfer from User %s for TokenID %d while account not created yet", inst.from, inst.tokenID)
  229. }
  230. if tc.Users[inst.to].Accounts[inst.tokenID] == nil {
  231. log.Fatalf("Transfer to User %s for TokenID %d while account not created yet", inst.to, inst.tokenID)
  232. }
  233. tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
  234. // if account of receiver does not exist, don't use
  235. // ToIdx, and use only ToEthAddr & ToBJJ
  236. tx := common.PoolL2Tx{
  237. FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
  238. ToIdx: tc.Users[inst.to].Accounts[inst.tokenID].Idx,
  239. ToEthAddr: tc.Users[inst.to].Addr,
  240. ToBJJ: tc.Users[inst.to].BJJ.Public(),
  241. TokenID: inst.tokenID,
  242. Amount: big.NewInt(int64(inst.amount)),
  243. Fee: common.FeeSelector(inst.fee),
  244. Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce,
  245. State: common.PoolL2TxStatePending,
  246. Timestamp: time.Now(),
  247. RqToEthAddr: common.EmptyAddr,
  248. RqToBJJ: nil,
  249. Type: common.TxTypeTransfer,
  250. }
  251. nTx, err := common.NewPoolL2Tx(&tx)
  252. if err != nil {
  253. panic(err)
  254. }
  255. tx = *nTx
  256. // perform signature and set it to tx.Signature
  257. toSign, err := tx.HashToSign()
  258. if err != nil {
  259. panic(err)
  260. }
  261. sig := tc.Users[inst.to].BJJ.SignPoseidon(toSign)
  262. tx.Signature = sig
  263. txs = append(txs, tx)
  264. case common.TxTypeExit:
  265. tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
  266. tx := common.PoolL2Tx{
  267. FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
  268. ToIdx: common.Idx(1), // as is an Exit
  269. TokenID: inst.tokenID,
  270. Amount: big.NewInt(int64(inst.amount)),
  271. Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce,
  272. Type: common.TxTypeExit,
  273. }
  274. txs = append(txs, tx)
  275. default:
  276. log.Fatalf("instruction type unrecognized: %s", inst.typ)
  277. }
  278. }
  279. return txs
  280. }
  281. // generateKeys generates BabyJubJub & Address keys for the given list of
  282. // account names in a deterministic way. This means, that for the same given
  283. // 'accNames' in a certain order, the keys will be always the same.
  284. func (tc *TestContext) generateKeys(accNames []string) {
  285. for i := 1; i < len(accNames)+1; i++ {
  286. if _, ok := tc.Users[accNames[i-1]]; ok {
  287. // account already created
  288. continue
  289. }
  290. // babyjubjub key
  291. var sk babyjub.PrivateKey
  292. copy(sk[:], []byte(strconv.Itoa(i))) // only for testing
  293. // eth address
  294. var key ecdsa.PrivateKey
  295. key.D = big.NewInt(int64(i)) // only for testing
  296. key.PublicKey.X, key.PublicKey.Y = ethCrypto.S256().ScalarBaseMult(key.D.Bytes())
  297. key.Curve = ethCrypto.S256()
  298. addr := ethCrypto.PubkeyToAddress(key.PublicKey)
  299. u := User{
  300. BJJ: &sk,
  301. Addr: addr,
  302. Accounts: make(map[common.TokenID]*Account),
  303. }
  304. tc.Users[accNames[i-1]] = &u
  305. }
  306. }