Browse Source

Merge pull request #222 from hermeznetwork/feature/txtransfertoethaddrbjj

Add TxTypeTransferToEthAddr&ToBJJ to StateDB & Til
feature/sql-semaphore1
Eduard S 4 years ago
committed by GitHub
parent
commit
0cd7179f47
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 108 additions and 45 deletions
  1. +2
    -1
      db/statedb/txprocessors.go
  2. +21
    -16
      test/til/lang.go
  3. +2
    -2
      test/til/lang_test.go
  4. +2
    -0
      test/til/sets.go
  5. +1
    -1
      test/til/sets_test.go
  6. +40
    -22
      test/til/txs.go
  7. +40
    -3
      test/til/txs_test.go

+ 2
- 1
db/statedb/txprocessors.go

@ -331,6 +331,7 @@ func (s *StateDB) processL2Tx(exitTree *merkletree.MerkleTree, tx *common.PoolL2
var err error var err error
// if tx.ToIdx==0, get toIdx by ToEthAddr or ToBJJ // if tx.ToIdx==0, get toIdx by ToEthAddr or ToBJJ
if tx.ToIdx == common.Idx(0) && tx.AuxToIdx == common.Idx(0) { 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) tx.AuxToIdx, err = s.GetIdxByEthAddrBJJ(tx.ToEthAddr, tx.ToBJJ)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
@ -390,7 +391,7 @@ func (s *StateDB) processL2Tx(exitTree *merkletree.MerkleTree, tx *common.PoolL2
} }
switch tx.Type { switch tx.Type {
case common.TxTypeTransfer:
case common.TxTypeTransfer, common.TxTypeTransferToEthAddr, common.TxTypeTransferToBJJ:
// go to the MT account of sender and receiver, and update // go to the MT account of sender and receiver, and update
// balance & nonce // balance & nonce
err = s.applyTransfer(tx.Tx(), tx.AuxToIdx) err = s.applyTransfer(tx.Tx(), tx.AuxToIdx)

+ 21
- 16
test/til/lang.go

@ -70,9 +70,9 @@ type instruction struct {
// parsedSet contains the full Set of Instructions representing a full code // parsedSet contains the full Set of Instructions representing a full code
type parsedSet struct { type parsedSet struct {
// type string
typ setType
instructions []instruction instructions []instruction
accounts []string
users []string
} }
func (i instruction) String() string { func (i instruction) String() string {
@ -365,6 +365,12 @@ func (p *parser) parseLine(setType setType) (*instruction, error) {
case "PoolTransfer": case "PoolTransfer":
c.typ = common.TxTypeTransfer c.typ = common.TxTypeTransfer
transferring = true transferring = true
case "PoolTransferToEthAddr":
c.typ = common.TxTypeTransferToEthAddr
transferring = true
case "PoolTransferToBJJ":
c.typ = common.TxTypeTransferToBJJ
transferring = true
case "PoolExit": case "PoolExit":
c.typ = common.TxTypeExit c.typ = common.TxTypeExit
default: default:
@ -493,22 +499,21 @@ func idxTokenIDToString(idx string, tid common.TokenID) string {
func (p *parser) parse() (*parsedSet, error) { func (p *parser) parse() (*parsedSet, error) {
ps := &parsedSet{} ps := &parsedSet{}
i := 0 // lines will start counting at line 1 i := 0 // lines will start counting at line 1
accounts := make(map[string]bool)
var setTypeOfSet setType
users := make(map[string]bool)
for { for {
i++ i++
instruction, err := p.parseLine(setTypeOfSet)
instruction, err := p.parseLine(ps.typ)
if err == errof { if err == errof {
break break
} }
if err == setTypeLine { 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" { if instruction.typ == "PoolL2" {
setTypeOfSet = setTypePoolL2
ps.typ = setTypePoolL2
} else if instruction.typ == "Blockchain" { } else if instruction.typ == "Blockchain" {
setTypeOfSet = setTypeBlockchain
ps.typ = setTypeBlockchain
} else { } else {
log.Fatalf("Line %d: Invalid set type: '%s'. Valid set types: 'Blockchain', 'PoolL2'", i, instruction.typ) log.Fatalf("Line %d: Invalid set type: '%s'. Valid set types: 'Blockchain', 'PoolL2'", i, instruction.typ)
} }
@ -528,18 +533,18 @@ func (p *parser) parse() (*parsedSet, error) {
if err != nil { if err != nil {
return ps, fmt.Errorf("Line %d: %s, err: %s", i, instruction.literal, err.Error()) 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) return ps, fmt.Errorf("Line %d: Set type not defined", i)
} }
ps.instructions = append(ps.instructions, *instruction) 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 return ps, nil
} }

+ 2
- 2
test/til/lang_test.go

@ -60,7 +60,7 @@ func TestParseBlockchainTxs(t *testing.T) {
instructions, err := parser.parse() instructions, err := parser.parse()
require.Nil(t, err) require.Nil(t, err)
assert.Equal(t, 25, len(instructions.instructions)) assert.Equal(t, 25, len(instructions.instructions))
assert.Equal(t, 7, len(instructions.accounts))
assert.Equal(t, 7, len(instructions.users))
if debug { if debug {
fmt.Println(instructions) fmt.Println(instructions)
@ -94,7 +94,7 @@ func TestParsePoolTxs(t *testing.T) {
instructions, err := parser.parse() instructions, err := parser.parse()
require.Nil(t, err) require.Nil(t, err)
assert.Equal(t, 5, len(instructions.instructions)) assert.Equal(t, 5, len(instructions.instructions))
assert.Equal(t, 4, len(instructions.accounts))
assert.Equal(t, 4, len(instructions.users))
if debug { if debug {
fmt.Println(instructions) fmt.Println(instructions)

+ 2
- 0
test/til/sets.go

@ -176,4 +176,6 @@ PoolExit(1) A: 3
PoolTransfer(1) A-B: 6 (1) PoolTransfer(1) A-B: 6 (1)
PoolTransfer(1) B-C: 3 (1) PoolTransfer(1) B-C: 3 (1)
PoolTransfer(1) A-C: 3 (1) PoolTransfer(1) A-C: 3 (1)
PoolTransferToEthAddr(1) A-C: 3 (1)
PoolTransferToBJJ(1) A-C: 3 (1)
` `

+ 1
- 1
test/til/sets_test.go

@ -19,6 +19,6 @@ func TestCompileSets(t *testing.T) {
tc := NewContext(eth.RollupConstMaxL1UserTx) tc := NewContext(eth.RollupConstMaxL1UserTx)
_, err = tc.GenerateBlocks(SetBlockchain0) _, err = tc.GenerateBlocks(SetBlockchain0)
assert.Nil(t, err) assert.Nil(t, err)
_, err = tc.GenerateBlocks(SetPool0)
_, err = tc.GeneratePoolL2Txs(SetPool0)
assert.Nil(t, err) assert.Nil(t, err)
} }

+ 40
- 22
test/til/txs.go

@ -18,7 +18,7 @@ import (
// Context contains the data of the test // Context contains the data of the test
type Context struct { type Context struct {
Instructions []instruction Instructions []instruction
accountsNames []string
userNames []string
Users map[string]*User Users map[string]*User
lastRegisteredTokenID common.TokenID lastRegisteredTokenID common.TokenID
l1CreatedAccounts map[string]*Account l1CreatedAccounts map[string]*Account
@ -98,18 +98,21 @@ type L2Tx struct {
} }
// GenerateBlocks returns an array of BlockData for a given set. It uses the // 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) { func (tc *Context) GenerateBlocks(set string) ([]common.BlockData, error) {
parser := newParser(strings.NewReader(set)) parser := newParser(strings.NewReader(set))
parsedSet, err := parser.parse() parsedSet, err := parser.parse()
if err != nil { if err != nil {
return nil, err 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.Instructions = parsedSet.instructions
tc.accountsNames = parsedSet.accounts
tc.userNames = parsedSet.users
tc.generateKeys(tc.accountsNames)
tc.generateKeys(tc.userNames)
var blocks []common.BlockData var blocks []common.BlockData
for _, inst := range parsedSet.instructions { for _, inst := range parsedSet.instructions {
@ -418,39 +421,42 @@ func (tc *Context) checkIfTokenIsRegistered(inst instruction) error {
} }
// GeneratePoolL2Txs returns an array of common.PoolL2Tx from a given set. It // 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) { func (tc *Context) GeneratePoolL2Txs(set string) ([]common.PoolL2Tx, error) {
parser := newParser(strings.NewReader(set)) parser := newParser(strings.NewReader(set))
parsedSet, err := parser.parse() parsedSet, err := parser.parse()
if err != nil { if err != nil {
return nil, err 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.Instructions = parsedSet.instructions
tc.accountsNames = parsedSet.accounts
tc.userNames = parsedSet.users
tc.generateKeys(tc.accountsNames)
tc.generateKeys(tc.userNames)
txs := []common.PoolL2Tx{} txs := []common.PoolL2Tx{}
for _, inst := range tc.Instructions { for _, inst := range tc.Instructions {
switch inst.typ { switch inst.typ {
case common.TxTypeTransfer:
case common.TxTypeTransfer, common.TxTypeTransferToEthAddr, common.TxTypeTransferToBJJ:
if err := tc.checkIfAccountExists(inst.from, inst); err != nil { if err := tc.checkIfAccountExists(inst.from, inst); err != nil {
log.Error(err) log.Error(err)
return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error()) 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++ tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
// if account of receiver does not exist, don't use // if account of receiver does not exist, don't use
// ToIdx, and use only ToEthAddr & ToBJJ // ToIdx, and use only ToEthAddr & ToBJJ
tx := common.PoolL2Tx{ tx := common.PoolL2Tx{
FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx, 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, TokenID: inst.tokenID,
Amount: big.NewInt(int64(inst.amount)), Amount: big.NewInt(int64(inst.amount)),
Fee: common.FeeSelector(inst.fee), Fee: common.FeeSelector(inst.fee),
@ -459,7 +465,19 @@ func (tc *Context) GeneratePoolL2Txs(set string) ([]common.PoolL2Tx, error) {
Timestamp: time.Now(), Timestamp: time.Now(),
RqToEthAddr: common.EmptyAddr, RqToEthAddr: common.EmptyAddr,
RqToBJJ: nil, 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) nTx, err := common.NewPoolL2Tx(&tx)
if err != nil { if err != nil {
@ -506,12 +524,12 @@ func (tc *Context) GeneratePoolL2Txs(set string) ([]common.PoolL2Tx, error) {
return txs, nil 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 // account already created
continue continue
} }
@ -531,6 +549,6 @@ func (tc *Context) generateKeys(accNames []string) {
Addr: addr, Addr: addr,
Accounts: make(map[common.TokenID]*Account), Accounts: make(map[common.TokenID]*Account),
} }
tc.Users[accNames[i-1]] = &u
tc.Users[userNames[i-1]] = &u
} }
} }

+ 40
- 3
test/til/txs_test.go

@ -207,10 +207,12 @@ func TestGeneratePoolL2Txs(t *testing.T) {
PoolTransfer(3) User1-User0: 15 (1) PoolTransfer(3) User1-User0: 15 (1)
PoolTransfer(2) B-D: 3 (1) PoolTransfer(2) B-D: 3 (1)
PoolExit(1) A: 3 PoolExit(1) A: 3
PoolTransferToEthAddr(1) A-B: 1 (1)
PoolTransferToBJJ(1) A-B: 1 (1)
` `
poolL2Txs, err := tc.GeneratePoolL2Txs(set) poolL2Txs, err := tc.GeneratePoolL2Txs(set)
require.Nil(t, err) 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.TxTypeTransfer, poolL2Txs[0].Type)
assert.Equal(t, common.TxTypeExit, poolL2Txs[8].Type) assert.Equal(t, common.TxTypeExit, poolL2Txs[8].Type)
assert.Equal(t, tc.Users["B"].Addr.Hex(), poolL2Txs[0].ToEthAddr.Hex()) assert.Equal(t, tc.Users["B"].Addr.Hex(), poolL2Txs[0].ToEthAddr.Hex())
@ -222,6 +224,13 @@ func TestGeneratePoolL2Txs(t *testing.T) {
assert.Equal(t, common.Nonce(2), poolL2Txs[3].Nonce) assert.Equal(t, common.Nonce(2), poolL2Txs[3].Nonce)
assert.Equal(t, common.Nonce(3), poolL2Txs[8].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 // load another set in the same Context
set = ` set = `
Type: PoolL2 Type: PoolL2
@ -231,9 +240,37 @@ func TestGeneratePoolL2Txs(t *testing.T) {
` `
poolL2Txs, err = tc.GeneratePoolL2Txs(set) poolL2Txs, err = tc.GeneratePoolL2Txs(set)
require.Nil(t, err) 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(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) { func TestGenerateErrors(t *testing.T) {

Loading…
Cancel
Save