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