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/iden3/go-iden3-crypto/babyjub"
 15	"github.com/stretchr/testify/require"
 16)
 17
 18// Account contains the data related to a testing account
 19type Account struct {
 20	BJJ   *babyjub.PrivateKey
 21	Addr  ethCommon.Address
 22	Idx   common.Idx
 23	Nonce common.Nonce
 24}
 25
 26// GenerateKeys generates BabyJubJub & Address keys for the given list of
 27// account names in a deterministic way. This means, that for the same given
 28// 'accNames' the keys will be always the same.
 29func GenerateKeys(t *testing.T, accNames []string) map[string]*Account {
 30	acc := make(map[string]*Account)
 31	for i := 1; i < len(accNames)+1; i++ {
 32		// babyjubjub key
 33		var sk babyjub.PrivateKey
 34		copy(sk[:], []byte(strconv.Itoa(i))) // only for testing
 35
 36		// eth address
 37		var key ecdsa.PrivateKey
 38		key.D = big.NewInt(int64(i)) // only for testing
 39		key.PublicKey.X, key.PublicKey.Y = ethCrypto.S256().ScalarBaseMult(key.D.Bytes())
 40		key.Curve = ethCrypto.S256()
 41		addr := ethCrypto.PubkeyToAddress(key.PublicKey)
 42
 43		a := Account{
 44			BJJ:   &sk,
 45			Addr:  addr,
 46			Nonce: 0,
 47		}
 48		acc[accNames[i-1]] = &a
 49	}
 50	return acc
 51}
 52
 53// GenerateTestTxs generates L1Tx & PoolL2Tx in a deterministic way for the
 54// given Instructions.
 55func GenerateTestTxs(t *testing.T, instructions Instructions) ([][]common.L1Tx, [][]common.L1Tx, [][]common.PoolL2Tx, []common.Token) {
 56	accounts := GenerateKeys(t, instructions.Accounts)
 57	l1CreatedAccounts := make(map[string]*Account)
 58
 59	var batchL1Txs []common.L1Tx
 60	var batchCoordinatorL1Txs []common.L1Tx
 61	var batchPoolL2Txs []common.PoolL2Tx
 62	var l1Txs [][]common.L1Tx
 63	var coordinatorL1Txs [][]common.L1Tx
 64	var poolL2Txs [][]common.PoolL2Tx
 65	idx := 256
 66	for _, inst := range instructions.Instructions {
 67		switch inst.Type {
 68		case common.TxTypeCreateAccountDeposit:
 69			tx := common.L1Tx{
 70				// TxID
 71				FromEthAddr: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Addr,
 72				FromBJJ:     accounts[idxTokenIDToString(inst.From, inst.TokenID)].BJJ.Public(),
 73				TokenID:     inst.TokenID,
 74				Amount:      big.NewInt(0),
 75				LoadAmount:  big.NewInt(int64(inst.Amount)),
 76				Type:        common.TxTypeCreateAccountDeposit,
 77			}
 78			batchL1Txs = append(batchL1Txs, tx)
 79			if accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx == common.Idx(0) { // if account.Idx is not set yet, set it and increment idx
 80				accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx = common.Idx(idx)
 81
 82				l1CreatedAccounts[idxTokenIDToString(inst.From, inst.TokenID)] = accounts[idxTokenIDToString(inst.From, inst.TokenID)]
 83				idx++
 84			}
 85		case common.TxTypeTransfer:
 86			// if account of receiver does not exist, create a new CoordinatorL1Tx creating the account
 87			if _, ok := l1CreatedAccounts[idxTokenIDToString(inst.To, inst.TokenID)]; !ok {
 88				tx := common.L1Tx{
 89					FromEthAddr: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr,
 90					FromBJJ:     accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(),
 91					TokenID:     inst.TokenID,
 92					LoadAmount:  big.NewInt(int64(inst.Amount)),
 93					Type:        common.TxTypeCreateAccountDeposit,
 94				}
 95				accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx = common.Idx(idx)
 96				l1CreatedAccounts[idxTokenIDToString(inst.To, inst.TokenID)] = accounts[idxTokenIDToString(inst.To, inst.TokenID)]
 97				batchCoordinatorL1Txs = append(batchCoordinatorL1Txs, tx)
 98				idx++
 99			}
100			toIdx := new(common.Idx)
101			*toIdx = accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx
102			toEthAddr := new(ethCommon.Address)
103			*toEthAddr = accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr
104			rqToEthAddr := new(ethCommon.Address)
105			*rqToEthAddr = accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr
106			tx := common.PoolL2Tx{
107				FromIdx:     accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx,
108				ToIdx:       toIdx,
109				ToEthAddr:   toEthAddr,
110				ToBJJ:       accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(),
111				TokenID:     inst.TokenID,
112				Amount:      big.NewInt(int64(inst.Amount)),
113				Fee:         common.FeeSelector(inst.Fee),
114				Nonce:       accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce,
115				State:       common.PoolL2TxStatePending,
116				Timestamp:   time.Now(),
117				BatchNum:    nil,
118				RqToEthAddr: rqToEthAddr,
119				RqToBJJ:     accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(),
120				Type:        common.TxTypeTransfer,
121			}
122			nTx, err := common.NewPoolL2Tx(&tx)
123			if err != nil {
124				panic(err)
125			}
126			tx = *nTx
127			// perform signature and set it to tx.Signature
128			toSign, err := tx.HashToSign()
129			if err != nil {
130				panic(err)
131			}
132			sig := accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.SignPoseidon(toSign)
133			tx.Signature = sig
134
135			accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce++
136			batchPoolL2Txs = append(batchPoolL2Txs, tx)
137
138		case common.TxTypeExit, common.TxTypeForceExit:
139			fromIdx := new(common.Idx)
140			*fromIdx = accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx
141			tx := common.L1Tx{
142				FromIdx: fromIdx,
143				ToIdx:   common.Idx(1), // as is an Exit
144				TokenID: inst.TokenID,
145				Amount:  big.NewInt(int64(inst.Amount)),
146				Type:    common.TxTypeExit,
147			}
148			batchL1Txs = append(batchL1Txs, tx)
149		case TypeNewBatch:
150			l1Txs = append(l1Txs, batchL1Txs)
151			coordinatorL1Txs = append(coordinatorL1Txs, batchCoordinatorL1Txs)
152			poolL2Txs = append(poolL2Txs, batchPoolL2Txs)
153			batchL1Txs = []common.L1Tx{}
154			batchCoordinatorL1Txs = []common.L1Tx{}
155			batchPoolL2Txs = []common.PoolL2Tx{}
156		default:
157			continue
158		}
159	}
160	l1Txs = append(l1Txs, batchL1Txs)
161	coordinatorL1Txs = append(coordinatorL1Txs, batchCoordinatorL1Txs)
162	poolL2Txs = append(poolL2Txs, batchPoolL2Txs)
163	tokens := []common.Token{}
164	for i := 0; i < len(poolL2Txs); i++ {
165		for j := 0; j < len(poolL2Txs[i]); j++ {
166			id := poolL2Txs[i][j].TokenID
167			found := false
168			for k := 0; k < len(tokens); k++ {
169				if tokens[k].TokenID == id {
170					found = true
171					break
172				}
173			}
174			if !found {
175				tokens = append(tokens, common.Token{
176					TokenID:     id,
177					EthBlockNum: 1,
178					EthAddr:     ethCommon.BigToAddress(big.NewInt(int64(i*10000 + j))),
179				})
180			}
181		}
182	}
183	return l1Txs, coordinatorL1Txs, poolL2Txs, tokens
184}
185
186// GenerateTestTxsFromSet reurns the L1 & L2 transactions for a given Set of
187// Instructions code
188func GenerateTestTxsFromSet(t *testing.T, set string) ([][]common.L1Tx, [][]common.L1Tx, [][]common.PoolL2Tx, []common.Token) {
189	parser := NewParser(strings.NewReader(set))
190	instructions, err := parser.Parse()
191	require.Nil(t, err)
192
193	return GenerateTestTxs(t, instructions)
194}