Add TxTypeTransferToEthAddr&ToBJJ to StateDB & Til Add TxTypeTransferToEthAddr&ToBJJ to StateDB & Til resolves #203, resolves #209
@@ -331,6 +331,7 @@ func (s *StateDB) processL2Tx(exitTree *merkletree.MerkleTree, tx *common.PoolL2Tx) (*common.Idx, *common.Account, bool, error) {
var err error // if tx.ToIdx==0, get toIdx by ToEthAddr or ToBJJ if tx.ToIdx == common.Idx(0) && tx.AuxToIdx == common.Idx(0) { + // case when tx.Type== common.TxTypeTransferToEthAddr or common.TxTypeTransferToBJJ tx.AuxToIdx, err = s.GetIdxByEthAddrBJJ(tx.ToEthAddr, tx.ToBJJ) if err != nil { log.Error(err)@@ -390,7 +391,7 @@ tx.TokenID = acc.TokenID
} switch tx.Type { - case common.TxTypeTransfer: + case common.TxTypeTransfer, common.TxTypeTransferToEthAddr, common.TxTypeTransferToBJJ: // go to the MT account of sender and receiver, and update // balance & nonce err = s.applyTransfer(tx.Tx(), tx.AuxToIdx)
@@ -70,9 +70,9 @@ }
// parsedSet contains the full Set of Instructions representing a full code type parsedSet struct { - // type string + typ setType instructions []instruction - accounts []string + users []string } func (i instruction) String() string {@@ -365,6 +365,12 @@ switch lit {
case "PoolTransfer": c.typ = common.TxTypeTransfer transferring = true + case "PoolTransferToEthAddr": + c.typ = common.TxTypeTransferToEthAddr + transferring = true + case "PoolTransferToBJJ": + c.typ = common.TxTypeTransferToBJJ + transferring = true case "PoolExit": c.typ = common.TxTypeExit default:@@ -493,22 +499,21 @@ // parse parses through reader
func (p *parser) parse() (*parsedSet, error) { ps := &parsedSet{} i := 0 // lines will start counting at line 1 - accounts := make(map[string]bool) - var setTypeOfSet setType + users := make(map[string]bool) for { i++ - instruction, err := p.parseLine(setTypeOfSet) + instruction, err := p.parseLine(ps.typ) if err == errof { break } if err == setTypeLine { - if setTypeOfSet != "" { - return ps, fmt.Errorf("Line %d: Instruction of 'Type: %s' when there is already a previous instruction 'Type: %s' defined", i, instruction.typ, setTypeOfSet) + if ps.typ != "" { + return ps, fmt.Errorf("Line %d: Instruction of 'Type: %s' when there is already a previous instruction 'Type: %s' defined", i, instruction.typ, ps.typ) } if instruction.typ == "PoolL2" { - setTypeOfSet = setTypePoolL2 + ps.typ = setTypePoolL2 } else if instruction.typ == "Blockchain" { - setTypeOfSet = setTypeBlockchain + ps.typ = setTypeBlockchain } else { log.Fatalf("Line %d: Invalid set type: '%s'. Valid set types: 'Blockchain', 'PoolL2'", i, instruction.typ) }@@ -528,18 +533,18 @@ }
if err != nil { return ps, fmt.Errorf("Line %d: %s, err: %s", i, instruction.literal, err.Error()) } - if setTypeOfSet == "" { + if ps.typ == "" { return ps, fmt.Errorf("Line %d: Set type not defined", i) } ps.instructions = append(ps.instructions, *instruction) - accounts[instruction.from] = true - if instruction.typ == common.TxTypeTransfer { // type: Transfer - accounts[instruction.to] = true + users[instruction.from] = true + if instruction.typ == common.TxTypeTransfer || instruction.typ == common.TxTypeTransferToEthAddr || instruction.typ == common.TxTypeTransferToBJJ { // type: Transfer + users[instruction.to] = true } } - for a := range accounts { - ps.accounts = append(ps.accounts, a) + for u := range users { + ps.users = append(ps.users, u) } - sort.Strings(ps.accounts) + sort.Strings(ps.users) return ps, nil }
@@ -60,7 +60,7 @@ parser := newParser(strings.NewReader(s))
instructions, err := parser.parse() require.Nil(t, err) assert.Equal(t, 25, len(instructions.instructions)) - assert.Equal(t, 7, len(instructions.accounts)) + assert.Equal(t, 7, len(instructions.users)) if debug { fmt.Println(instructions)@@ -94,7 +94,7 @@ parser := newParser(strings.NewReader(s))
instructions, err := parser.parse() require.Nil(t, err) assert.Equal(t, 5, len(instructions.instructions)) - assert.Equal(t, 4, len(instructions.accounts)) + assert.Equal(t, 4, len(instructions.users)) if debug { fmt.Println(instructions)
@@ -176,4 +176,6 @@ PoolExit(1) A: 3
PoolTransfer(1) A-B: 6 (1) PoolTransfer(1) B-C: 3 (1) PoolTransfer(1) A-C: 3 (1) +PoolTransferToEthAddr(1) A-C: 3 (1) +PoolTransferToBJJ(1) A-C: 3 (1) `
@@ -19,6 +19,6 @@
tc := NewContext(eth.RollupConstMaxL1UserTx) _, err = tc.GenerateBlocks(SetBlockchain0) assert.Nil(t, err) - _, err = tc.GenerateBlocks(SetPool0) + _, err = tc.GeneratePoolL2Txs(SetPool0) assert.Nil(t, err) }
@@ -18,7 +18,7 @@
// Context contains the data of the test type Context struct { Instructions []instruction - accountsNames []string + userNames []string Users map[string]*User lastRegisteredTokenID common.TokenID l1CreatedAccounts map[string]*Account@@ -98,18 +98,21 @@ L2Tx common.L2Tx
} // GenerateBlocks returns an array of BlockData for a given set. It uses the -// accounts (keys & nonces) of the Context. +// users (keys & nonces) of the Context. func (tc *Context) GenerateBlocks(set string) ([]common.BlockData, error) { parser := newParser(strings.NewReader(set)) parsedSet, err := parser.parse() if err != nil { return nil, err + } + if parsedSet.typ != setTypeBlockchain { + return nil, fmt.Errorf("Expected set type: %s, found: %s", setTypeBlockchain, parsedSet.typ) } tc.Instructions = parsedSet.instructions - tc.accountsNames = parsedSet.accounts + tc.userNames = parsedSet.users - tc.generateKeys(tc.accountsNames) + tc.generateKeys(tc.userNames) var blocks []common.BlockData for _, inst := range parsedSet.instructions {@@ -418,39 +421,42 @@ return nil
} // GeneratePoolL2Txs returns an array of common.PoolL2Tx from a given set. It -// uses the accounts (keys & nonces) of the Context. +// uses the users (keys) of the Context. func (tc *Context) GeneratePoolL2Txs(set string) ([]common.PoolL2Tx, error) { parser := newParser(strings.NewReader(set)) parsedSet, err := parser.parse() if err != nil { return nil, err } + if parsedSet.typ != setTypePoolL2 { + return nil, fmt.Errorf("Expected set type: %s, found: %s", setTypePoolL2, parsedSet.typ) + } tc.Instructions = parsedSet.instructions - tc.accountsNames = parsedSet.accounts + tc.userNames = parsedSet.users - tc.generateKeys(tc.accountsNames) + tc.generateKeys(tc.userNames) txs := []common.PoolL2Tx{} for _, inst := range tc.Instructions { switch inst.typ { - case common.TxTypeTransfer: + case common.TxTypeTransfer, common.TxTypeTransferToEthAddr, common.TxTypeTransferToBJJ: if err := tc.checkIfAccountExists(inst.from, inst); err != nil { log.Error(err) return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error()) } - if err := tc.checkIfAccountExists(inst.to, inst); err != nil { - log.Error(err) - return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error()) + if inst.typ == common.TxTypeTransfer { + // if TxTypeTransfer, need to exist the ToIdx account + if err := tc.checkIfAccountExists(inst.to, inst); err != nil { + log.Error(err) + return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error()) + } } tc.Users[inst.from].Accounts[inst.tokenID].Nonce++ // if account of receiver does not exist, don't use // ToIdx, and use only ToEthAddr & ToBJJ tx := common.PoolL2Tx{ FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx, - ToIdx: tc.Users[inst.to].Accounts[inst.tokenID].Idx, - ToEthAddr: tc.Users[inst.to].Addr, - ToBJJ: tc.Users[inst.to].BJJ.Public(), TokenID: inst.tokenID, Amount: big.NewInt(int64(inst.amount)), Fee: common.FeeSelector(inst.fee),@@ -459,7 +465,19 @@ State: common.PoolL2TxStatePending,
Timestamp: time.Now(), RqToEthAddr: common.EmptyAddr, RqToBJJ: nil, - Type: common.TxTypeTransfer, + Type: inst.typ, + } + if tx.Type == common.TxTypeTransfer { + tx.ToIdx = tc.Users[inst.to].Accounts[inst.tokenID].Idx + tx.ToEthAddr = tc.Users[inst.to].Addr + tx.ToBJJ = tc.Users[inst.to].BJJ.Public() + } else if tx.Type == common.TxTypeTransferToEthAddr { + tx.ToIdx = common.Idx(0) + tx.ToEthAddr = tc.Users[inst.to].Addr + } else if tx.Type == common.TxTypeTransferToBJJ { + tx.ToIdx = common.Idx(0) + tx.ToEthAddr = common.FFAddr + tx.ToBJJ = tc.Users[inst.to].BJJ.Public() } nTx, err := common.NewPoolL2Tx(&tx) if err != nil {@@ -506,12 +524,12 @@
return txs, nil } -// generateKeys generates BabyJubJub & Address keys for the given list of -// account names in a deterministic way. This means, that for the same given -// 'accNames' in a certain order, the keys will be always the same. -func (tc *Context) generateKeys(accNames []string) { - for i := 1; i < len(accNames)+1; i++ { - if _, ok := tc.Users[accNames[i-1]]; ok { +// generateKeys generates BabyJubJub & Address keys for the given list of user +// names in a deterministic way. This means, that for the same given +// 'userNames' in a certain order, the keys will be always the same. +func (tc *Context) generateKeys(userNames []string) { + for i := 1; i < len(userNames)+1; i++ { + if _, ok := tc.Users[userNames[i-1]]; ok { // account already created continue }@@ -531,6 +549,6 @@ BJJ: &sk,
Addr: addr, Accounts: make(map[common.TokenID]*Account), } - tc.Users[accNames[i-1]] = &u + tc.Users[userNames[i-1]] = &u } }
@@ -207,10 +207,12 @@ PoolTransfer(1) User0-User1: 15 (1)
PoolTransfer(3) User1-User0: 15 (1) PoolTransfer(2) B-D: 3 (1) PoolExit(1) A: 3 + PoolTransferToEthAddr(1) A-B: 1 (1) + PoolTransferToBJJ(1) A-B: 1 (1) ` poolL2Txs, err := tc.GeneratePoolL2Txs(set) require.Nil(t, err) - assert.Equal(t, 9, len(poolL2Txs)) + assert.Equal(t, 11, len(poolL2Txs)) assert.Equal(t, common.TxTypeTransfer, poolL2Txs[0].Type) assert.Equal(t, common.TxTypeExit, poolL2Txs[8].Type) assert.Equal(t, tc.Users["B"].Addr.Hex(), poolL2Txs[0].ToEthAddr.Hex())@@ -221,6 +223,13 @@
assert.Equal(t, common.Nonce(1), poolL2Txs[0].Nonce) assert.Equal(t, common.Nonce(2), poolL2Txs[3].Nonce) assert.Equal(t, common.Nonce(3), poolL2Txs[8].Nonce) + + assert.Equal(t, tc.Users["B"].Addr.Hex(), poolL2Txs[9].ToEthAddr.Hex()) + assert.Nil(t, poolL2Txs[9].ToBJJ) + assert.Equal(t, common.TxTypeTransferToEthAddr, poolL2Txs[9].Type) + assert.Equal(t, common.FFAddr, poolL2Txs[10].ToEthAddr) + assert.Equal(t, tc.Users["B"].BJJ.Public().String(), poolL2Txs[10].ToBJJ.String()) + assert.Equal(t, common.TxTypeTransferToBJJ, poolL2Txs[10].Type) // load another set in the same Context set = `@@ -231,9 +240,37 @@ PoolTransfer(1) A-C: 3 (1)
` poolL2Txs, err = tc.GeneratePoolL2Txs(set) require.Nil(t, err) - assert.Equal(t, common.Nonce(4), poolL2Txs[0].Nonce) + assert.Equal(t, common.Nonce(6), poolL2Txs[0].Nonce) assert.Equal(t, common.Nonce(2), poolL2Txs[1].Nonce) - assert.Equal(t, common.Nonce(5), poolL2Txs[2].Nonce) + assert.Equal(t, common.Nonce(7), poolL2Txs[2].Nonce) + + // check that a PoolL2Tx can be done to a non existing ToIdx + set = ` + Type: Blockchain + AddToken(1) + CreateAccountDeposit(1) A: 10 + > batchL1 + > batchL1 + > block + ` + tc = NewContext(eth.RollupConstMaxL1UserTx) + _, err = tc.GenerateBlocks(set) + require.Nil(t, err) + set = ` + Type: PoolL2 + PoolTransferToEthAddr(1) A-B: 3 (1) + PoolTransferToBJJ(1) A-C: 3 (1) + ` + _, err = tc.GeneratePoolL2Txs(set) + require.Nil(t, err) + // expect error, as FromIdx=B is still not created for TokenID=1 + set = ` + Type: PoolL2 + PoolTransferToEthAddr(1) B-A: 3 (1) + PoolTransferToBJJ(1) B-A: 3 (1) + ` + _, err = tc.GeneratePoolL2Txs(set) + require.NotNil(t, err) } func TestGenerateErrors(t *testing.T) {