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