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.

297 lines
10 KiB

  1. package test
  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. type TestContext struct {
  17. t *testing.T
  18. Instructions []Instruction
  19. accountsNames []string
  20. accounts map[string]*Account
  21. TokenIDs []common.TokenID
  22. l1CreatedAccounts map[string]*Account
  23. }
  24. func NewTestContext(t *testing.T) *TestContext {
  25. return &TestContext{
  26. t: t,
  27. accounts: make(map[string]*Account),
  28. l1CreatedAccounts: make(map[string]*Account),
  29. }
  30. }
  31. // Account contains the data related to a testing account
  32. type Account struct {
  33. BJJ *babyjub.PrivateKey
  34. Addr ethCommon.Address
  35. Idx common.Idx
  36. Nonce common.Nonce
  37. }
  38. // func (tc *TestContext) GenerateBlocks() []BlockData {
  39. //
  40. // return nil
  41. // }
  42. // GeneratePoolL2Txs returns an array of common.PoolL2Tx from a given set. It
  43. // uses the accounts (keys & nonces) of the TestContext.
  44. func (tc *TestContext) GeneratePoolL2Txs(set string) []common.PoolL2Tx {
  45. parser := NewParser(strings.NewReader(set))
  46. parsedSet, err := parser.Parse()
  47. require.Nil(tc.t, err)
  48. tc.Instructions = parsedSet.Instructions
  49. tc.accountsNames = parsedSet.Accounts
  50. tc.TokenIDs = parsedSet.TokenIDs
  51. tc.generateKeys(tc.accountsNames)
  52. txs := []common.PoolL2Tx{}
  53. for _, inst := range tc.Instructions {
  54. switch inst.Type {
  55. case common.TxTypeTransfer:
  56. tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce++
  57. // if account of receiver does not exist, don't use
  58. // ToIdx, and use only ToEthAddr & ToBJJ
  59. toIdx := new(common.Idx)
  60. if _, ok := tc.l1CreatedAccounts[idxTokenIDToString(inst.To, inst.TokenID)]; !ok {
  61. *toIdx = 0
  62. } else {
  63. *toIdx = tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx
  64. }
  65. // TODO once common.L{x}Txs parameter pointers is undone, update this lines related to pointers usage
  66. toEthAddr := new(ethCommon.Address)
  67. *toEthAddr = tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr
  68. rqToEthAddr := new(ethCommon.Address)
  69. *rqToEthAddr = tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr
  70. tx := common.PoolL2Tx{
  71. FromIdx: tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx,
  72. ToIdx: toIdx,
  73. ToEthAddr: toEthAddr,
  74. ToBJJ: tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(),
  75. TokenID: inst.TokenID,
  76. Amount: big.NewInt(int64(inst.Amount)),
  77. Fee: common.FeeSelector(inst.Fee),
  78. Nonce: tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce,
  79. State: common.PoolL2TxStatePending,
  80. Timestamp: time.Now(),
  81. BatchNum: nil,
  82. RqToEthAddr: rqToEthAddr,
  83. RqToBJJ: tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(),
  84. Type: common.TxTypeTransfer,
  85. }
  86. nTx, err := common.NewPoolL2Tx(&tx)
  87. if err != nil {
  88. panic(err)
  89. }
  90. tx = *nTx
  91. // perform signature and set it to tx.Signature
  92. toSign, err := tx.HashToSign()
  93. if err != nil {
  94. panic(err)
  95. }
  96. sig := tc.accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.SignPoseidon(toSign)
  97. tx.Signature = sig
  98. txs = append(txs, tx)
  99. case common.TxTypeExit:
  100. tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce++
  101. // TODO once common.L{x}Txs parameter pointers is undone, update this lines related to pointers usage
  102. toIdx := new(common.Idx)
  103. *toIdx = common.Idx(1) // as is an Exit
  104. tx := common.PoolL2Tx{
  105. FromIdx: tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx,
  106. ToIdx: toIdx, // as is an Exit
  107. TokenID: inst.TokenID,
  108. Amount: big.NewInt(int64(inst.Amount)),
  109. Nonce: tc.accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce,
  110. Type: common.TxTypeExit,
  111. }
  112. txs = append(txs, tx)
  113. default:
  114. log.Warnf("instruction type unrecognized: %s", inst.Type)
  115. continue
  116. }
  117. }
  118. return txs
  119. }
  120. // generateKeys generates BabyJubJub & Address keys for the given list of
  121. // account names in a deterministic way. This means, that for the same given
  122. // 'accNames' in a certain order, the keys will be always the same.
  123. func (tc *TestContext) generateKeys(accNames []string) map[string]*Account {
  124. acc := make(map[string]*Account)
  125. for i := 1; i < len(accNames)+1; i++ {
  126. if _, ok := tc.accounts[accNames[i-1]]; ok {
  127. // account already created
  128. continue
  129. }
  130. // babyjubjub key
  131. var sk babyjub.PrivateKey
  132. copy(sk[:], []byte(strconv.Itoa(i))) // only for testing
  133. // eth address
  134. var key ecdsa.PrivateKey
  135. key.D = big.NewInt(int64(i)) // only for testing
  136. key.PublicKey.X, key.PublicKey.Y = ethCrypto.S256().ScalarBaseMult(key.D.Bytes())
  137. key.Curve = ethCrypto.S256()
  138. addr := ethCrypto.PubkeyToAddress(key.PublicKey)
  139. a := Account{
  140. BJJ: &sk,
  141. Addr: addr,
  142. Nonce: 0,
  143. }
  144. tc.accounts[accNames[i-1]] = &a
  145. }
  146. return acc
  147. }
  148. /*
  149. // GenerateTestTxs generates L1Tx & PoolL2Tx in a deterministic way for the
  150. // given ParsedSet.
  151. func GenerateTestTxs(t *testing.T, parsedSet *ParsedSet) ([][]common.L1Tx, [][]common.L1Tx, [][]common.PoolL2Tx, []common.Token) {
  152. accounts := generateKeys(t, parsedSet.Accounts)
  153. l1CreatedAccounts := make(map[string]*Account)
  154. var batchL1Txs []common.L1Tx
  155. var batchCoordinatorL1Txs []common.L1Tx
  156. var batchPoolL2Txs []common.PoolL2Tx
  157. var l1Txs [][]common.L1Tx
  158. var coordinatorL1Txs [][]common.L1Tx
  159. var poolL2Txs [][]common.PoolL2Tx
  160. idx := 256
  161. for _, inst := range parsedSet.Instructions {
  162. switch inst.Type {
  163. case common.TxTypeCreateAccountDeposit:
  164. tx := common.L1Tx{
  165. // TxID
  166. FromEthAddr: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Addr,
  167. FromBJJ: accounts[idxTokenIDToString(inst.From, inst.TokenID)].BJJ.Public(),
  168. TokenID: inst.TokenID,
  169. Amount: big.NewInt(0),
  170. LoadAmount: big.NewInt(int64(inst.Amount)),
  171. Type: common.TxTypeCreateAccountDeposit,
  172. }
  173. batchL1Txs = append(batchL1Txs, tx)
  174. if accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx == common.Idx(0) { // if account.Idx is not set yet, set it and increment idx
  175. accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx = common.Idx(idx)
  176. l1CreatedAccounts[idxTokenIDToString(inst.From, inst.TokenID)] = accounts[idxTokenIDToString(inst.From, inst.TokenID)]
  177. idx++
  178. }
  179. case common.TxTypeTransfer:
  180. // if account of receiver does not exist, create a new CoordinatorL1Tx creating the account
  181. if _, ok := l1CreatedAccounts[idxTokenIDToString(inst.To, inst.TokenID)]; !ok {
  182. tx := common.L1Tx{
  183. FromEthAddr: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr,
  184. FromBJJ: accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(),
  185. TokenID: inst.TokenID,
  186. LoadAmount: big.NewInt(int64(inst.Amount)),
  187. Type: common.TxTypeCreateAccountDeposit,
  188. }
  189. accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx = common.Idx(idx)
  190. l1CreatedAccounts[idxTokenIDToString(inst.To, inst.TokenID)] = accounts[idxTokenIDToString(inst.To, inst.TokenID)]
  191. batchCoordinatorL1Txs = append(batchCoordinatorL1Txs, tx)
  192. idx++
  193. }
  194. tx := common.PoolL2Tx{
  195. FromIdx: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx,
  196. ToIdx: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx,
  197. ToEthAddr: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr,
  198. ToBJJ: accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(),
  199. TokenID: inst.TokenID,
  200. Amount: big.NewInt(int64(inst.Amount)),
  201. Fee: common.FeeSelector(inst.Fee),
  202. Nonce: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce,
  203. State: common.PoolL2TxStatePending,
  204. RqToEthAddr: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr,
  205. RqToBJJ: accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(),
  206. Type: common.TxTypeTransfer,
  207. }
  208. nTx, err := common.NewPoolL2Tx(&tx)
  209. if err != nil {
  210. panic(err)
  211. }
  212. tx = *nTx
  213. // perform signature and set it to tx.Signature
  214. toSign, err := tx.HashToSign()
  215. if err != nil {
  216. panic(err)
  217. }
  218. sig := accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.SignPoseidon(toSign)
  219. tx.Signature = sig
  220. accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce++
  221. batchPoolL2Txs = append(batchPoolL2Txs, tx)
  222. case common.TxTypeExit, common.TxTypeForceExit:
  223. tx := common.L1Tx{
  224. FromIdx: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx,
  225. ToIdx: common.Idx(1), // as is an Exit
  226. TokenID: inst.TokenID,
  227. Amount: big.NewInt(int64(inst.Amount)),
  228. Type: common.TxTypeExit,
  229. }
  230. batchL1Txs = append(batchL1Txs, tx)
  231. case TypeNewBatch:
  232. l1Txs = append(l1Txs, batchL1Txs)
  233. coordinatorL1Txs = append(coordinatorL1Txs, batchCoordinatorL1Txs)
  234. poolL2Txs = append(poolL2Txs, batchPoolL2Txs)
  235. batchL1Txs = []common.L1Tx{}
  236. batchCoordinatorL1Txs = []common.L1Tx{}
  237. batchPoolL2Txs = []common.PoolL2Tx{}
  238. default:
  239. continue
  240. }
  241. }
  242. l1Txs = append(l1Txs, batchL1Txs)
  243. coordinatorL1Txs = append(coordinatorL1Txs, batchCoordinatorL1Txs)
  244. poolL2Txs = append(poolL2Txs, batchPoolL2Txs)
  245. tokens := []common.Token{}
  246. for i := 0; i < len(poolL2Txs); i++ {
  247. for j := 0; j < len(poolL2Txs[i]); j++ {
  248. id := poolL2Txs[i][j].TokenID
  249. found := false
  250. for k := 0; k < len(tokens); k++ {
  251. if tokens[k].TokenID == id {
  252. found = true
  253. break
  254. }
  255. }
  256. if !found {
  257. tokens = append(tokens, common.Token{
  258. TokenID: id,
  259. EthBlockNum: 1,
  260. EthAddr: ethCommon.BigToAddress(big.NewInt(int64(i*10000 + j))),
  261. })
  262. }
  263. }
  264. }
  265. return l1Txs, coordinatorL1Txs, poolL2Txs, tokens
  266. }
  267. // GenerateTestTxsFromSet reurns the L1 & L2 transactions for a given Set of
  268. // Instructions code
  269. func GenerateTestTxsFromSet(t *testing.T, set string) ([][]common.L1Tx, [][]common.L1Tx, [][]common.PoolL2Tx, []common.Token) {
  270. parser := NewParser(strings.NewReader(set))
  271. parsedSet, err := parser.Parse()
  272. require.Nil(t, err)
  273. return GenerateTestTxs(t, parsedSet)
  274. }
  275. */