From d9121bf5a62043800d8a04a1e7a3b76007a3b820 Mon Sep 17 00:00:00 2001 From: Arnau B Date: Wed, 2 Dec 2020 16:01:17 +0100 Subject: [PATCH] Allow using til with instructions --- test/til/lang.go | 262 ++++++++++++++++++------------------- test/til/lang_test.go | 4 +- test/til/txs.go | 296 +++++++++++++++++++++++++----------------- test/til/txs_test.go | 207 +++++++++++++++++++++++++++++ 4 files changed, 516 insertions(+), 253 deletions(-) diff --git a/test/til/lang.go b/test/til/lang.go index 7104ef8..ee2eb40 100644 --- a/test/til/lang.go +++ b/test/til/lang.go @@ -23,30 +23,32 @@ var setTypeLine = fmt.Errorf("setTypeLine") //nolint:golint // setType defines the type of the set type setType string -// setTypeBlockchain defines the type 'Blockchain' of the set -var setTypeBlockchain = setType("Blockchain") +// SetTypeBlockchain defines the type 'Blockchain' of the set +var SetTypeBlockchain = setType("Blockchain") -// setTypePoolL2 defines the type 'PoolL2' of the set -var setTypePoolL2 = setType("PoolL2") +// SetTypePoolL2 defines the type 'PoolL2' of the set +var SetTypePoolL2 = setType("PoolL2") -// typeNewBatch is used for testing purposes only, and represents the +// TypeNewBatch is used for testing purposes only, and represents the // common.TxType of a new batch -var typeNewBatch common.TxType = "InstrTypeNewBatch" +var TypeNewBatch common.TxType = "InstrTypeNewBatch" -// typeNewBatchL1 is used for testing purposes only, and represents the +// TypeNewBatchL1 is used for testing purposes only, and represents the // common.TxType of a new batch -var typeNewBatchL1 common.TxType = "InstrTypeNewBatchL1" +var TypeNewBatchL1 common.TxType = "InstrTypeNewBatchL1" -// typeNewBlock is used for testing purposes only, and represents the +// TypeNewBlock is used for testing purposes only, and represents the // common.TxType of a new ethereum block -var typeNewBlock common.TxType = "InstrTypeNewBlock" +var TypeNewBlock common.TxType = "InstrTypeNewBlock" -// typeAddToken is used for testing purposes only, and represents the +// TypeAddToken is used for testing purposes only, and represents the // common.TxType of a new Token regsitration // It has 'nolint:gosec' as the string 'Token' triggers gosec as a potential leaked Token (which is not the case) -var typeAddToken common.TxType = "InstrTypeAddToken" //nolint:gosec +var TypeAddToken common.TxType = "InstrTypeAddToken" //nolint:gosec -var txTypeCreateAccountDepositCoordinator common.TxType = "TypeCreateAccountDepositCoordinator" +// TxTypeCreateAccountDepositCoordinator is used for testing purposes only, and represents the +// common.TxType of a create acount deposit made by the coordinator +var TxTypeCreateAccountDepositCoordinator common.TxType = "TypeCreateAccountDepositCoordinator" //nolint const ( @@ -57,75 +59,75 @@ const ( IDENT // val ) -// instruction is the data structure that represents one line of code -type instruction struct { - lineNum int - literal string - from string - to string - amount *big.Int - loadAmount *big.Int - fee uint8 - tokenID common.TokenID - typ common.TxType // D: Deposit, T: Transfer, E: ForceExit +// Instruction is the data structure that represents one line of code +type Instruction struct { + LineNum int + Literal string + From string + To string + Amount *big.Int + LoadAmount *big.Int + Fee uint8 + TokenID common.TokenID + Typ common.TxType // D: Deposit, T: Transfer, E: ForceExit } // parsedSet contains the full Set of Instructions representing a full code type parsedSet struct { typ setType - instructions []instruction + instructions []Instruction users []string } -func (i instruction) String() string { +func (i Instruction) String() string { buf := bytes.NewBufferString("") - fmt.Fprintf(buf, "Type: %s, ", i.typ) - fmt.Fprintf(buf, "From: %s, ", i.from) - if i.typ == common.TxTypeTransfer || - i.typ == common.TxTypeDepositTransfer || - i.typ == common.TxTypeCreateAccountDepositTransfer { - fmt.Fprintf(buf, "To: %s, ", i.to) + fmt.Fprintf(buf, "Type: %s, ", i.Typ) + fmt.Fprintf(buf, "From: %s, ", i.From) + if i.Typ == common.TxTypeTransfer || + i.Typ == common.TxTypeDepositTransfer || + i.Typ == common.TxTypeCreateAccountDepositTransfer { + fmt.Fprintf(buf, "To: %s, ", i.To) } - if i.typ == common.TxTypeDeposit || - i.typ == common.TxTypeDepositTransfer || - i.typ == common.TxTypeCreateAccountDepositTransfer { - fmt.Fprintf(buf, "LoadAmount: %d, ", i.loadAmount) + if i.Typ == common.TxTypeDeposit || + i.Typ == common.TxTypeDepositTransfer || + i.Typ == common.TxTypeCreateAccountDepositTransfer { + fmt.Fprintf(buf, "LoadAmount: %d, ", i.LoadAmount) } - if i.typ != common.TxTypeDeposit { - fmt.Fprintf(buf, "Amount: %d, ", i.amount) + if i.Typ != common.TxTypeDeposit { + fmt.Fprintf(buf, "Amount: %d, ", i.Amount) } - if i.typ == common.TxTypeTransfer || - i.typ == common.TxTypeDepositTransfer || - i.typ == common.TxTypeCreateAccountDepositTransfer { - fmt.Fprintf(buf, "Fee: %d, ", i.fee) + if i.Typ == common.TxTypeTransfer || + i.Typ == common.TxTypeDepositTransfer || + i.Typ == common.TxTypeCreateAccountDepositTransfer { + fmt.Fprintf(buf, "Fee: %d, ", i.Fee) } - fmt.Fprintf(buf, "TokenID: %d\n", i.tokenID) + fmt.Fprintf(buf, "TokenID: %d\n", i.TokenID) return buf.String() } // Raw returns a string with the raw representation of the Instruction -func (i instruction) raw() string { +func (i Instruction) raw() string { buf := bytes.NewBufferString("") - fmt.Fprintf(buf, "%s", i.typ) - fmt.Fprintf(buf, "(%d)", i.tokenID) - fmt.Fprintf(buf, "%s", i.from) - if i.typ == common.TxTypeTransfer || - i.typ == common.TxTypeDepositTransfer || - i.typ == common.TxTypeCreateAccountDepositTransfer { - fmt.Fprintf(buf, "-%s", i.to) + fmt.Fprintf(buf, "%s", i.Typ) + fmt.Fprintf(buf, "(%d)", i.TokenID) + fmt.Fprintf(buf, "%s", i.From) + if i.Typ == common.TxTypeTransfer || + i.Typ == common.TxTypeDepositTransfer || + i.Typ == common.TxTypeCreateAccountDepositTransfer { + fmt.Fprintf(buf, "-%s", i.To) } fmt.Fprintf(buf, ":") - if i.typ == common.TxTypeDeposit || - i.typ == common.TxTypeDepositTransfer || - i.typ == common.TxTypeCreateAccountDepositTransfer { - fmt.Fprintf(buf, "%d", i.loadAmount) + if i.Typ == common.TxTypeDeposit || + i.Typ == common.TxTypeDepositTransfer || + i.Typ == common.TxTypeCreateAccountDepositTransfer { + fmt.Fprintf(buf, "%d", i.LoadAmount) } - if i.typ != common.TxTypeDeposit { - fmt.Fprintf(buf, "%d", i.amount) + if i.Typ != common.TxTypeDeposit { + fmt.Fprintf(buf, "%d", i.Amount) } - if i.typ == common.TxTypeTransfer { - fmt.Fprintf(buf, "(%d)", i.fee) + if i.Typ == common.TxTypeTransfer { + fmt.Fprintf(buf, "(%d)", i.Fee) } return buf.String() } @@ -269,30 +271,30 @@ func (p *parser) scanIgnoreWhitespace() (tok token, lit string) { } // parseLine parses the current line -func (p *parser) parseLine(setType setType) (*instruction, error) { - c := &instruction{} +func (p *parser) parseLine(setType setType) (*Instruction, error) { + c := &Instruction{} tok, lit := p.scanIgnoreWhitespace() if tok == EOF { return nil, tracerr.Wrap(errof) } - c.literal += lit + c.Literal += lit if lit == "/" { _, _ = p.s.r.ReadString('\n') return nil, commentLine } else if lit == ">" { - if setType == setTypePoolL2 { + if setType == SetTypePoolL2 { return c, tracerr.Wrap(fmt.Errorf("Unexpected '>' at PoolL2Txs set")) } _, lit = p.scanIgnoreWhitespace() if lit == "batch" { _, _ = p.s.r.ReadString('\n') - return &instruction{typ: typeNewBatch}, newEventLine + return &Instruction{Typ: TypeNewBatch}, newEventLine } else if lit == "batchL1" { _, _ = p.s.r.ReadString('\n') - return &instruction{typ: typeNewBatchL1}, newEventLine + return &Instruction{Typ: TypeNewBatchL1}, newEventLine } else if lit == "block" { _, _ = p.s.r.ReadString('\n') - return &instruction{typ: typeNewBlock}, newEventLine + return &Instruction{Typ: TypeNewBlock}, newEventLine } else { return c, tracerr.Wrap(fmt.Errorf("Unexpected '> %s', expected '> batch' or '> block'", lit)) } @@ -302,9 +304,9 @@ func (p *parser) parseLine(setType setType) (*instruction, error) { } _, lit = p.scanIgnoreWhitespace() if lit == "Blockchain" { - return &instruction{typ: "Blockchain"}, setTypeLine + return &Instruction{Typ: "Blockchain"}, setTypeLine } else if lit == "PoolL2" { - return &instruction{typ: "PoolL2"}, setTypeLine + return &Instruction{Typ: "PoolL2"}, setTypeLine } else { return c, tracerr.Wrap(fmt.Errorf("Invalid set type: '%s'. Valid set types: 'Blockchain', 'PoolL2'", lit)) } @@ -313,20 +315,20 @@ func (p *parser) parseLine(setType setType) (*instruction, error) { return c, tracerr.Wrap(err) } _, lit = p.scanIgnoreWhitespace() - c.literal += lit + c.Literal += lit tidI, err := strconv.Atoi(lit) if err != nil { line, _ := p.s.r.ReadString('\n') - c.literal += line + c.Literal += line return c, tracerr.Wrap(err) } - c.tokenID = common.TokenID(tidI) + c.TokenID = common.TokenID(tidI) if err := p.expectChar(c, ")"); err != nil { return c, tracerr.Wrap(err) } - c.typ = typeAddToken + c.Typ = TypeAddToken line, _ := p.s.r.ReadString('\n') - c.literal += line + c.Literal += line return c, newEventLine } @@ -336,52 +338,52 @@ func (p *parser) parseLine(setType setType) (*instruction, error) { transferring := false fee := false - if setType == setTypeBlockchain { + if setType == SetTypeBlockchain { switch lit { case "Deposit": - c.typ = common.TxTypeDeposit + c.Typ = common.TxTypeDeposit case "Exit": - c.typ = common.TxTypeExit + c.Typ = common.TxTypeExit fee = true case "Transfer": - c.typ = common.TxTypeTransfer + c.Typ = common.TxTypeTransfer transferring = true fee = true case "CreateAccountDeposit": - c.typ = common.TxTypeCreateAccountDeposit + c.Typ = common.TxTypeCreateAccountDeposit case "CreateAccountDepositTransfer": - c.typ = common.TxTypeCreateAccountDepositTransfer + c.Typ = common.TxTypeCreateAccountDepositTransfer transferring = true case "CreateAccountCoordinator": - c.typ = txTypeCreateAccountDepositCoordinator + c.Typ = TxTypeCreateAccountDepositCoordinator // transferring is false, as the Coordinator tx transfer will be 0 case "DepositTransfer": - c.typ = common.TxTypeDepositTransfer + c.Typ = common.TxTypeDepositTransfer transferring = true case "ForceTransfer": - c.typ = common.TxTypeForceTransfer + c.Typ = common.TxTypeForceTransfer transferring = true case "ForceExit": - c.typ = common.TxTypeForceExit + c.Typ = common.TxTypeForceExit default: return c, tracerr.Wrap(fmt.Errorf("Unexpected Blockchain tx type: %s", lit)) } - } else if setType == setTypePoolL2 { + } else if setType == SetTypePoolL2 { switch lit { case "PoolTransfer": - c.typ = common.TxTypeTransfer + c.Typ = common.TxTypeTransfer transferring = true fee = true case "PoolTransferToEthAddr": - c.typ = common.TxTypeTransferToEthAddr + c.Typ = common.TxTypeTransferToEthAddr transferring = true fee = true case "PoolTransferToBJJ": - c.typ = common.TxTypeTransferToBJJ + c.Typ = common.TxTypeTransferToBJJ transferring = true fee = true case "PoolExit": - c.typ = common.TxTypeExit + c.Typ = common.TxTypeExit fee = true default: return c, tracerr.Wrap(fmt.Errorf("Unexpected PoolL2 tx type: %s", lit)) @@ -394,90 +396,90 @@ func (p *parser) parseLine(setType setType) (*instruction, error) { return c, tracerr.Wrap(err) } _, lit = p.scanIgnoreWhitespace() - c.literal += lit + c.Literal += lit tidI, err := strconv.Atoi(lit) if err != nil { line, _ := p.s.r.ReadString('\n') - c.literal += line + c.Literal += line return c, tracerr.Wrap(err) } - c.tokenID = common.TokenID(tidI) + c.TokenID = common.TokenID(tidI) if err := p.expectChar(c, ")"); err != nil { return c, tracerr.Wrap(err) } _, lit = p.scanIgnoreWhitespace() - c.literal += lit - c.from = lit - if c.typ == txTypeCreateAccountDepositCoordinator { + c.Literal += lit + c.From = lit + if c.Typ == TxTypeCreateAccountDepositCoordinator { line, _ := p.s.r.ReadString('\n') - c.literal += line + c.Literal += line return c, nil } _, lit = p.scanIgnoreWhitespace() - c.literal += lit + c.Literal += lit if transferring { if lit != "-" { return c, tracerr.Wrap(fmt.Errorf("Expected '-', found '%s'", lit)) } _, lit = p.scanIgnoreWhitespace() - c.literal += lit - c.to = lit + c.Literal += lit + c.To = lit _, lit = p.scanIgnoreWhitespace() - c.literal += lit + c.Literal += lit } if lit != ":" { line, _ := p.s.r.ReadString('\n') - c.literal += line + c.Literal += line return c, tracerr.Wrap(fmt.Errorf("Expected ':', found '%s'", lit)) } - if c.typ == common.TxTypeDepositTransfer || - c.typ == common.TxTypeCreateAccountDepositTransfer { + if c.Typ == common.TxTypeDepositTransfer || + c.Typ == common.TxTypeCreateAccountDepositTransfer { // deposit case _, lit = p.scanIgnoreWhitespace() - c.literal += lit + c.Literal += lit loadAmount, ok := new(big.Int).SetString(lit, 10) if !ok { line, _ := p.s.r.ReadString('\n') - c.literal += line + c.Literal += line return c, tracerr.Wrap(fmt.Errorf("Can not parse number for LoadAmount")) } - c.loadAmount = loadAmount + c.LoadAmount = loadAmount if err := p.expectChar(c, ","); err != nil { return c, tracerr.Wrap(err) } } _, lit = p.scanIgnoreWhitespace() - c.literal += lit + c.Literal += lit amount, ok := new(big.Int).SetString(lit, 10) if !ok { line, _ := p.s.r.ReadString('\n') - c.literal += line + c.Literal += line return c, tracerr.Wrap(fmt.Errorf("Can not parse number for Amount: %s", lit)) } - if c.typ == common.TxTypeDeposit || - c.typ == common.TxTypeCreateAccountDeposit { - c.loadAmount = amount + if c.Typ == common.TxTypeDeposit || + c.Typ == common.TxTypeCreateAccountDeposit { + c.LoadAmount = amount } else { - c.amount = amount + c.Amount = amount } if fee { if err := p.expectChar(c, "("); err != nil { return c, tracerr.Wrap(err) } _, lit = p.scanIgnoreWhitespace() - c.literal += lit + c.Literal += lit fee, err := strconv.Atoi(lit) if err != nil { line, _ := p.s.r.ReadString('\n') - c.literal += line + c.Literal += line return c, tracerr.Wrap(err) } if fee > common.MaxFeePlan-1 { line, _ := p.s.r.ReadString('\n') - c.literal += line + c.Literal += line return c, tracerr.Wrap(fmt.Errorf("Fee %d can not be bigger than 255", fee)) } - c.fee = uint8(fee) + c.Fee = uint8(fee) if err := p.expectChar(c, ")"); err != nil { return c, tracerr.Wrap(err) @@ -490,12 +492,12 @@ func (p *parser) parseLine(setType setType) (*instruction, error) { return c, nil } -func (p *parser) expectChar(c *instruction, ch string) error { +func (p *parser) expectChar(c *Instruction, ch string) error { _, lit := p.scanIgnoreWhitespace() - c.literal += lit + c.Literal += lit if lit != ch { line, _ := p.s.r.ReadString('\n') - c.literal += line + c.Literal += line return tracerr.Wrap(fmt.Errorf("Expected '%s', found '%s'", ch, lit)) } return nil @@ -518,38 +520,38 @@ func (p *parser) parse() (*parsedSet, error) { } if tracerr.Unwrap(err) == setTypeLine { if ps.typ != "" { - return ps, tracerr.Wrap(fmt.Errorf("Line %d: Instruction of 'Type: %s' when there is already a previous instruction 'Type: %s' defined", i, instruction.typ, ps.typ)) + return ps, tracerr.Wrap(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" { - ps.typ = setTypePoolL2 - } else if instruction.typ == "Blockchain" { - ps.typ = setTypeBlockchain + if instruction.Typ == "PoolL2" { + ps.typ = SetTypePoolL2 + } else if instruction.Typ == "Blockchain" { + ps.typ = SetTypeBlockchain } 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) } continue } if tracerr.Unwrap(err) == commentLine { continue } - instruction.lineNum = i + instruction.LineNum = i if tracerr.Unwrap(err) == newEventLine { - if instruction.typ == typeAddToken && instruction.tokenID == common.TokenID(0) { + if instruction.Typ == TypeAddToken && instruction.TokenID == common.TokenID(0) { return ps, tracerr.Wrap(fmt.Errorf("Line %d: AddToken can not register TokenID 0", i)) } ps.instructions = append(ps.instructions, *instruction) continue } if err != nil { - return ps, tracerr.Wrap(fmt.Errorf("Line %d: %s, err: %s", i, instruction.literal, err.Error())) + return ps, tracerr.Wrap(fmt.Errorf("Line %d: %s, err: %s", i, instruction.Literal, err.Error())) } if ps.typ == "" { return ps, tracerr.Wrap(fmt.Errorf("Line %d: Set type not defined", i)) } ps.instructions = append(ps.instructions, *instruction) - users[instruction.from] = true - if instruction.typ == common.TxTypeTransfer || instruction.typ == common.TxTypeTransferToEthAddr || instruction.typ == common.TxTypeTransferToBJJ { // type: Transfer - users[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 u := range users { diff --git a/test/til/lang_test.go b/test/til/lang_test.go index a4346d1..f039c83 100644 --- a/test/til/lang_test.go +++ b/test/til/lang_test.go @@ -69,8 +69,8 @@ func TestParseBlockchainTxs(t *testing.T) { } } - assert.Equal(t, txTypeCreateAccountDepositCoordinator, instructions.instructions[7].typ) - assert.Equal(t, typeNewBatch, instructions.instructions[11].typ) + assert.Equal(t, TxTypeCreateAccountDepositCoordinator, instructions.instructions[7].Typ) + assert.Equal(t, TypeNewBatch, instructions.instructions[11].Typ) assert.Equal(t, "Deposit(1)User0:20", instructions.instructions[16].raw()) assert.Equal(t, "Type: DepositTransfer, From: A, To: B, LoadAmount: 15, Amount: 10, Fee: 0, TokenID: 1\n", instructions.instructions[13].String()) assert.Equal(t, "Type: Transfer, From: User1, To: User0, Amount: 15, Fee: 1, TokenID: 3\n", instructions.instructions[19].String()) diff --git a/test/til/txs.go b/test/til/txs.go index f933d33..2b92a1f 100644 --- a/test/til/txs.go +++ b/test/til/txs.go @@ -49,7 +49,7 @@ type contextExtra struct { // Context contains the data of the test type Context struct { - Instructions []instruction + instructions []Instruction userNames []string Users map[string]*User // Name -> *User UsersByIdx map[int]*User @@ -146,7 +146,7 @@ type L2Tx struct { L2Tx common.L2Tx } -// GenerateBlocks returns an array of BlockData for a given set. It uses the +// GenerateBlocks returns an array of BlockData for a given set made of a string. It uses the // users (keys & nonces) of the Context. func (tc *Context) GenerateBlocks(set string) ([]common.BlockData, error) { parser := newParser(strings.NewReader(set)) @@ -154,34 +154,60 @@ func (tc *Context) GenerateBlocks(set string) ([]common.BlockData, error) { if err != nil { return nil, tracerr.Wrap(err) } - if parsedSet.typ != setTypeBlockchain { - return nil, tracerr.Wrap(fmt.Errorf("Expected set type: %s, found: %s", setTypeBlockchain, parsedSet.typ)) + if parsedSet.typ != SetTypeBlockchain { + return nil, tracerr.Wrap(fmt.Errorf("Expected set type: %s, found: %s", SetTypeBlockchain, parsedSet.typ)) } - tc.Instructions = parsedSet.instructions + tc.instructions = parsedSet.instructions tc.userNames = parsedSet.users + return tc.generateBlocks() +} + +// GenerateBlocksFromInstructions returns an array of BlockData for a given set made of instructions. It uses the +// users (keys & nonces) of the Context. +func (tc *Context) GenerateBlocksFromInstructions(set []Instruction) ([]common.BlockData, error) { + userNames := []string{} + addedNames := make(map[string]bool) + for _, inst := range set { + if _, ok := addedNames[inst.From]; !ok { + // If the name wasn't already added + userNames = append(userNames, inst.From) + addedNames[inst.From] = true + } + if _, ok := addedNames[inst.To]; !ok { + // If the name wasn't already added + userNames = append(userNames, inst.To) + addedNames[inst.To] = true + } + } + tc.userNames = userNames + tc.instructions = set + return tc.generateBlocks() +} + +func (tc *Context) generateBlocks() ([]common.BlockData, error) { tc.generateKeys(tc.userNames) var blocks []common.BlockData - for _, inst := range parsedSet.instructions { - switch inst.typ { - case txTypeCreateAccountDepositCoordinator: // tx source: L1CoordinatorTx + for _, inst := range tc.instructions { + switch inst.Typ { + case TxTypeCreateAccountDepositCoordinator: // tx source: L1CoordinatorTx if err := tc.checkIfTokenIsRegistered(inst); err != nil { log.Error(err) - return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())) + return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error())) } tx := common.L1Tx{ - FromEthAddr: tc.Users[inst.from].Addr, - FromBJJ: tc.Users[inst.from].BJJ.Public(), - TokenID: inst.tokenID, + FromEthAddr: tc.Users[inst.From].Addr, + FromBJJ: tc.Users[inst.From].BJJ.Public(), + TokenID: inst.TokenID, Amount: big.NewInt(0), LoadAmount: big.NewInt(0), - Type: common.TxTypeCreateAccountDeposit, // as txTypeCreateAccountDepositCoordinator is not valid oustide Til package + Type: common.TxTypeCreateAccountDeposit, // as TxTypeCreateAccountDepositCoordinator is not valid oustide Til package } testTx := L1Tx{ - lineNum: inst.lineNum, - fromIdxName: inst.from, + lineNum: inst.LineNum, + fromIdxName: inst.From, L1Tx: tx, } @@ -189,23 +215,23 @@ func (tc *Context) GenerateBlocks(set string) ([]common.BlockData, error) { case common.TxTypeCreateAccountDeposit, common.TxTypeCreateAccountDepositTransfer: // tx source: L1UserTx if err := tc.checkIfTokenIsRegistered(inst); err != nil { log.Error(err) - return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())) + return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error())) } tx := common.L1Tx{ - FromEthAddr: tc.Users[inst.from].Addr, - FromBJJ: tc.Users[inst.from].BJJ.Public(), - TokenID: inst.tokenID, + FromEthAddr: tc.Users[inst.From].Addr, + FromBJJ: tc.Users[inst.From].BJJ.Public(), + TokenID: inst.TokenID, Amount: big.NewInt(0), - LoadAmount: inst.loadAmount, - Type: inst.typ, + LoadAmount: inst.LoadAmount, + Type: inst.Typ, } - if inst.typ == common.TxTypeCreateAccountDepositTransfer { - tx.Amount = inst.amount + if inst.Typ == common.TxTypeCreateAccountDepositTransfer { + tx.Amount = inst.Amount } testTx := L1Tx{ - lineNum: inst.lineNum, - fromIdxName: inst.from, - toIdxName: inst.to, + lineNum: inst.LineNum, + fromIdxName: inst.From, + toIdxName: inst.To, L1Tx: tx, } if err := tc.addToL1UserQueue(testTx); err != nil { @@ -214,25 +240,25 @@ func (tc *Context) GenerateBlocks(set string) ([]common.BlockData, error) { case common.TxTypeDeposit, common.TxTypeDepositTransfer: // tx source: L1UserTx if err := tc.checkIfTokenIsRegistered(inst); err != nil { log.Error(err) - return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())) + return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error())) } - if err := tc.checkIfAccountExists(inst.from, inst); err != nil { + if err := tc.checkIfAccountExists(inst.From, inst); err != nil { log.Error(err) - return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())) + return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error())) } tx := common.L1Tx{ - TokenID: inst.tokenID, + TokenID: inst.TokenID, Amount: big.NewInt(0), - LoadAmount: inst.loadAmount, - Type: inst.typ, + LoadAmount: inst.LoadAmount, + Type: inst.Typ, } - if inst.typ == common.TxTypeDepositTransfer { - tx.Amount = inst.amount + if inst.Typ == common.TxTypeDepositTransfer { + tx.Amount = inst.Amount } testTx := L1Tx{ - lineNum: inst.lineNum, - fromIdxName: inst.from, - toIdxName: inst.to, + lineNum: inst.LineNum, + fromIdxName: inst.From, + toIdxName: inst.To, L1Tx: tx, } if err := tc.addToL1UserQueue(testTx); err != nil { @@ -241,38 +267,38 @@ func (tc *Context) GenerateBlocks(set string) ([]common.BlockData, error) { case common.TxTypeTransfer: // L2Tx if err := tc.checkIfTokenIsRegistered(inst); err != nil { log.Error(err) - return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())) + return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error())) } tx := common.L2Tx{ - Amount: inst.amount, - Fee: common.FeeSelector(inst.fee), + Amount: inst.Amount, + Fee: common.FeeSelector(inst.Fee), Type: common.TxTypeTransfer, EthBlockNum: tc.blockNum, } tx.BatchNum = common.BatchNum(tc.currBatchNum) // when converted to PoolL2Tx BatchNum parameter is lost testTx := L2Tx{ - lineNum: inst.lineNum, - fromIdxName: inst.from, - toIdxName: inst.to, - tokenID: inst.tokenID, + lineNum: inst.LineNum, + fromIdxName: inst.From, + toIdxName: inst.To, + tokenID: inst.TokenID, L2Tx: tx, } tc.currBatchTest.l2Txs = append(tc.currBatchTest.l2Txs, testTx) case common.TxTypeForceTransfer: // tx source: L1UserTx if err := tc.checkIfTokenIsRegistered(inst); err != nil { log.Error(err) - return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())) + return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error())) } tx := common.L1Tx{ - TokenID: inst.tokenID, - Amount: inst.amount, + TokenID: inst.TokenID, + Amount: inst.Amount, LoadAmount: big.NewInt(0), Type: common.TxTypeForceTransfer, } testTx := L1Tx{ - lineNum: inst.lineNum, - fromIdxName: inst.from, - toIdxName: inst.to, + lineNum: inst.LineNum, + fromIdxName: inst.From, + toIdxName: inst.To, L1Tx: tx, } if err := tc.addToL1UserQueue(testTx); err != nil { @@ -281,63 +307,63 @@ func (tc *Context) GenerateBlocks(set string) ([]common.BlockData, error) { case common.TxTypeExit: // tx source: L2Tx if err := tc.checkIfTokenIsRegistered(inst); err != nil { log.Error(err) - return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())) + return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error())) } tx := common.L2Tx{ ToIdx: common.Idx(1), // as is an Exit - Fee: common.FeeSelector(inst.fee), - Amount: inst.amount, + Fee: common.FeeSelector(inst.Fee), + Amount: inst.Amount, Type: common.TxTypeExit, EthBlockNum: tc.blockNum, } tx.BatchNum = common.BatchNum(tc.currBatchNum) // when converted to PoolL2Tx BatchNum parameter is lost testTx := L2Tx{ - lineNum: inst.lineNum, - fromIdxName: inst.from, - toIdxName: inst.to, - tokenID: inst.tokenID, + lineNum: inst.LineNum, + fromIdxName: inst.From, + toIdxName: inst.To, + tokenID: inst.TokenID, L2Tx: tx, } tc.currBatchTest.l2Txs = append(tc.currBatchTest.l2Txs, testTx) case common.TxTypeForceExit: // tx source: L1UserTx if err := tc.checkIfTokenIsRegistered(inst); err != nil { log.Error(err) - return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())) + return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error())) } tx := common.L1Tx{ ToIdx: common.Idx(1), // as is an Exit - TokenID: inst.tokenID, - Amount: inst.amount, + TokenID: inst.TokenID, + Amount: inst.Amount, LoadAmount: big.NewInt(0), Type: common.TxTypeForceExit, } testTx := L1Tx{ - lineNum: inst.lineNum, - fromIdxName: inst.from, - toIdxName: inst.to, + lineNum: inst.LineNum, + fromIdxName: inst.From, + toIdxName: inst.To, L1Tx: tx, } if err := tc.addToL1UserQueue(testTx); err != nil { return nil, tracerr.Wrap(err) } - case typeNewBatch: - if err = tc.calculateIdxForL1Txs(true, tc.currBatchTest.l1CoordinatorTxs); err != nil { + case TypeNewBatch: + if err := tc.calculateIdxForL1Txs(true, tc.currBatchTest.l1CoordinatorTxs); err != nil { return nil, tracerr.Wrap(err) } - if err = tc.setIdxs(); err != nil { + if err := tc.setIdxs(); err != nil { log.Error(err) return nil, tracerr.Wrap(err) } - case typeNewBatchL1: + case TypeNewBatchL1: // for each L1UserTx of the Queues[ToForgeNum], calculate the Idx - if err = tc.calculateIdxForL1Txs(false, tc.Queues[tc.ToForgeNum]); err != nil { + if err := tc.calculateIdxForL1Txs(false, tc.Queues[tc.ToForgeNum]); err != nil { return nil, tracerr.Wrap(err) } - if err = tc.calculateIdxForL1Txs(true, tc.currBatchTest.l1CoordinatorTxs); err != nil { + if err := tc.calculateIdxForL1Txs(true, tc.currBatchTest.l1CoordinatorTxs); err != nil { return nil, tracerr.Wrap(err) } tc.currBatch.L1Batch = true - if err = tc.setIdxs(); err != nil { + if err := tc.setIdxs(); err != nil { log.Error(err) return nil, tracerr.Wrap(err) } @@ -350,26 +376,26 @@ func (tc *Context) GenerateBlocks(set string) ([]common.BlockData, error) { newQueue := []L1Tx{} tc.Queues = append(tc.Queues, newQueue) } - case typeNewBlock: + case TypeNewBlock: blocks = append(blocks, tc.currBlock) tc.blockNum++ tc.currBlock = newBlock(tc.blockNum) - case typeAddToken: + case TypeAddToken: newToken := common.Token{ - EthAddr: ethCommon.BigToAddress(big.NewInt(int64(inst.tokenID * 100))), //nolint:gomnd - // Name: fmt.Sprintf("Token %d", inst.tokenID), - // Symbol: fmt.Sprintf("TK%d", inst.tokenID), + EthAddr: ethCommon.BigToAddress(big.NewInt(int64(inst.TokenID * 100))), //nolint:gomnd + // Name: fmt.Sprintf("Token %d", inst.TokenID), + // Symbol: fmt.Sprintf("TK%d", inst.TokenID), // Decimals: 18, - TokenID: inst.tokenID, + TokenID: inst.TokenID, EthBlockNum: tc.blockNum, } - if inst.tokenID != tc.LastRegisteredTokenID+1 { - return nil, tracerr.Wrap(fmt.Errorf("Line %d: AddToken TokenID should be sequential, expected TokenID: %d, defined TokenID: %d", inst.lineNum, tc.LastRegisteredTokenID+1, inst.tokenID)) + if inst.TokenID != tc.LastRegisteredTokenID+1 { + return nil, tracerr.Wrap(fmt.Errorf("Line %d: AddToken TokenID should be sequential, expected TokenID: %d, defined TokenID: %d", inst.LineNum, tc.LastRegisteredTokenID+1, inst.TokenID)) } tc.LastRegisteredTokenID++ tc.currBlock.Rollup.AddedTokens = append(tc.currBlock.Rollup.AddedTokens, newToken) default: - return nil, tracerr.Wrap(fmt.Errorf("Line %d: Unexpected type: %s", inst.lineNum, inst.typ)) + return nil, tracerr.Wrap(fmt.Errorf("Line %d: Unexpected type: %s", inst.LineNum, inst.Typ)) } } @@ -497,20 +523,21 @@ func (tc *Context) addToL1UserQueue(tx L1Tx) error { return nil } -func (tc *Context) checkIfAccountExists(tf string, inst instruction) error { - if tc.Users[tf].Accounts[inst.tokenID] == nil { - return tracerr.Wrap(fmt.Errorf("%s at User: %s, for TokenID: %d, while account not created yet", inst.typ, tf, inst.tokenID)) +func (tc *Context) checkIfAccountExists(tf string, inst Instruction) error { + if tc.Users[tf].Accounts[inst.TokenID] == nil { + return tracerr.Wrap(fmt.Errorf("%s at User: %s, for TokenID: %d, while account not created yet", inst.Typ, tf, inst.TokenID)) } return nil } -func (tc *Context) checkIfTokenIsRegistered(inst instruction) error { - if inst.tokenID > tc.LastRegisteredTokenID { - return tracerr.Wrap(fmt.Errorf("Can not process %s: TokenID %d not registered, last registered TokenID: %d", inst.typ, inst.tokenID, tc.LastRegisteredTokenID)) + +func (tc *Context) checkIfTokenIsRegistered(inst Instruction) error { + if inst.TokenID > tc.LastRegisteredTokenID { + return tracerr.Wrap(fmt.Errorf("Can not process %s: TokenID %d not registered, last registered TokenID: %d", inst.Typ, inst.TokenID, tc.LastRegisteredTokenID)) } return nil } -// GeneratePoolL2Txs returns an array of common.PoolL2Tx from a given set. It +// GeneratePoolL2Txs returns an array of common.PoolL2Tx from a given set made of a string. It // uses the users (keys) of the Context. func (tc *Context) GeneratePoolL2Txs(set string) ([]common.PoolL2Tx, error) { parser := newParser(strings.NewReader(set)) @@ -518,98 +545,125 @@ func (tc *Context) GeneratePoolL2Txs(set string) ([]common.PoolL2Tx, error) { if err != nil { return nil, tracerr.Wrap(err) } - if parsedSet.typ != setTypePoolL2 { - return nil, tracerr.Wrap(fmt.Errorf("Expected set type: %s, found: %s", setTypePoolL2, parsedSet.typ)) + if parsedSet.typ != SetTypePoolL2 { + return nil, tracerr.Wrap(fmt.Errorf("Expected set type: %s, found: %s", SetTypePoolL2, parsedSet.typ)) } - tc.Instructions = parsedSet.instructions + tc.instructions = parsedSet.instructions tc.userNames = parsedSet.users + return tc.generatePoolL2Txs() +} + +// GeneratePoolL2TxsFromInstructions returns an array of common.PoolL2Tx from a given set made of instructions. It +// uses the users (keys) of the Context. +func (tc *Context) GeneratePoolL2TxsFromInstructions(set []Instruction) ([]common.PoolL2Tx, error) { + userNames := []string{} + addedNames := make(map[string]bool) + for _, inst := range set { + if _, ok := addedNames[inst.From]; !ok { + // If the name wasn't already added + userNames = append(userNames, inst.From) + addedNames[inst.From] = true + } + if _, ok := addedNames[inst.To]; !ok { + // If the name wasn't already added + userNames = append(userNames, inst.To) + addedNames[inst.To] = true + } + } + tc.userNames = userNames + tc.instructions = set + + return tc.generatePoolL2Txs() +} + +func (tc *Context) generatePoolL2Txs() ([]common.PoolL2Tx, error) { tc.generateKeys(tc.userNames) txs := []common.PoolL2Tx{} - for _, inst := range tc.Instructions { - switch inst.typ { + for _, inst := range tc.instructions { + switch inst.Typ { 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) - return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())) + return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error())) } - if inst.typ == common.TxTypeTransfer { + if inst.Typ == common.TxTypeTransfer { // if TxTypeTransfer, need to exist the ToIdx account - if err := tc.checkIfAccountExists(inst.to, inst); err != nil { + if err := tc.checkIfAccountExists(inst.To, inst); err != nil { log.Error(err) - return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())) + return nil, tracerr.Wrap(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 // ToIdx, and use only ToEthAddr & ToBJJ tx := common.PoolL2Tx{ - FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx, - TokenID: inst.tokenID, - Amount: inst.amount, - Fee: common.FeeSelector(inst.fee), - Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce, + FromIdx: tc.Users[inst.From].Accounts[inst.TokenID].Idx, + TokenID: inst.TokenID, + Amount: inst.Amount, + Fee: common.FeeSelector(inst.Fee), + Nonce: tc.Users[inst.From].Accounts[inst.TokenID].Nonce, State: common.PoolL2TxStatePending, Timestamp: time.Now(), RqToEthAddr: common.EmptyAddr, RqToBJJ: nil, - Type: inst.typ, + 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() + 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 + 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() + tx.ToBJJ = tc.Users[inst.To].BJJ.Public() } nTx, err := common.NewPoolL2Tx(&tx) if err != nil { - return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())) + return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error())) } tx = *nTx // perform signature and set it to tx.Signature toSign, err := tx.HashToSign() if err != nil { - return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())) + return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error())) } - sig := tc.Users[inst.from].BJJ.SignPoseidon(toSign) + sig := tc.Users[inst.From].BJJ.SignPoseidon(toSign) tx.Signature = sig.Compress() txs = append(txs, tx) case common.TxTypeExit: - tc.Users[inst.from].Accounts[inst.tokenID].Nonce++ + tc.Users[inst.From].Accounts[inst.TokenID].Nonce++ tx := common.PoolL2Tx{ - FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx, + FromIdx: tc.Users[inst.From].Accounts[inst.TokenID].Idx, ToIdx: common.Idx(1), // as is an Exit - Fee: common.FeeSelector(inst.fee), - TokenID: inst.tokenID, - Amount: inst.amount, - Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce, + Fee: common.FeeSelector(inst.Fee), + TokenID: inst.TokenID, + Amount: inst.Amount, + Nonce: tc.Users[inst.From].Accounts[inst.TokenID].Nonce, State: common.PoolL2TxStatePending, Type: common.TxTypeExit, } nTx, err := common.NewPoolL2Tx(&tx) if err != nil { - return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())) + return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error())) } tx = *nTx // perform signature and set it to tx.Signature toSign, err := tx.HashToSign() if err != nil { - return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())) + return nil, tracerr.Wrap(fmt.Errorf("Line %d: %s", inst.LineNum, err.Error())) } - sig := tc.Users[inst.from].BJJ.SignPoseidon(toSign) + sig := tc.Users[inst.From].BJJ.SignPoseidon(toSign) tx.Signature = sig.Compress() txs = append(txs, tx) default: - return nil, tracerr.Wrap(fmt.Errorf("Line %d: instruction type unrecognized: %s", inst.lineNum, inst.typ)) + return nil, tracerr.Wrap(fmt.Errorf("Line %d: instruction type unrecognized: %s", inst.LineNum, inst.Typ)) } } diff --git a/test/til/txs_test.go b/test/til/txs_test.go index 0cc4782..2ff9a53 100644 --- a/test/til/txs_test.go +++ b/test/til/txs_test.go @@ -271,6 +271,67 @@ func TestGeneratePoolL2Txs(t *testing.T) { require.NotNil(t, err) } +func TestGeneratePoolL2TxsFromInstructions(t *testing.T) { + // Generate necessary L1 data + set := ` + Type: Blockchain + AddToken(1) + + CreateAccountCoordinator(1) A + CreateAccountDeposit(1) B: 7 + > batchL1 + > batchL1 + ` + tc := NewContext(common.RollupConstMaxL1UserTx) + _, err := tc.GenerateBlocks(set) + require.Nil(t, err) + + // Generate Pool txs using instructions + instructionSet := []Instruction{} + i := 0 + a := big.NewInt(3) + instructionSet = append(instructionSet, Instruction{ + LineNum: i, + // Literal: "PoolTransferToEthAddr(1) B-A: 3 (1)", + Typ: common.TxTypeTransferToEthAddr, + From: "B", + To: "A", + TokenID: 1, + Amount: a, + Fee: 1, + }) + i++ + instructionSet = append(instructionSet, Instruction{ + LineNum: i, + // Literal: "PoolTransferToBJJ(1) B-A: 3 (1)", + Typ: common.TxTypeTransferToBJJ, + From: "B", + To: "A", + TokenID: 1, + Amount: a, + Fee: 1, + }) + txsFromInstructions, err := tc.GeneratePoolL2TxsFromInstructions(instructionSet) + require.Nil(t, err) + // Generate Pool txs using string + tc = NewContext(common.RollupConstMaxL1UserTx) + _, err = tc.GenerateBlocks(set) + require.Nil(t, err) + stringSet := ` + Type: PoolL2 + PoolTransferToEthAddr(1) B-A: 3 (1) + PoolTransferToBJJ(1) B-A: 3 (1) + ` + txsFromString, err := tc.GeneratePoolL2Txs(stringSet) + require.Nil(t, err) + // Compare generated txs from instructions and string + // timestamps will be different + for i := 0; i < len(txsFromString); i++ { + txsFromInstructions[i].Timestamp = txsFromString[i].Timestamp + } + assert.Equal(t, txsFromString, txsFromInstructions) +} + func TestGenerateErrors(t *testing.T) { // unregistered token set := `Type: Blockchain @@ -359,3 +420,149 @@ func TestGenerateErrors(t *testing.T) { assert.Equal(t, common.Nonce(1), tc.Users["B"].Accounts[common.TokenID(1)].Nonce) assert.Equal(t, common.Idx(257), tc.Users["B"].Accounts[common.TokenID(1)].Idx) } + +func TestGenerateFromInstructions(t *testing.T) { + // Generate block from instructions + setInst := []Instruction{} + i := 0 + setInst = append(setInst, Instruction{ + LineNum: i, + // Literal: "AddToken(1)", + Typ: TypeAddToken, + TokenID: 1, + }) + i++ + la := big.NewInt(10) + setInst = append(setInst, Instruction{ + LineNum: i, + // Literal: "CreateAccountDeposit(1) A: 10", + Typ: common.TxTypeCreateAccountDeposit, + From: "A", + TokenID: 1, + LoadAmount: la, + }) + i++ + setInst = append(setInst, Instruction{ + LineNum: i, + // Literal: "> batchL1", + Typ: TypeNewBatchL1, + }) + i++ + setInst = append(setInst, Instruction{ + LineNum: i, + // Literal: "CreateAccountCoordinator(1) B", + Typ: TxTypeCreateAccountDepositCoordinator, + From: "B", + TokenID: 1, + }) + i++ + setInst = append(setInst, Instruction{ + LineNum: i, + // Literal: "> batchL1", + Typ: TypeNewBatchL1, + }) + i++ + a := big.NewInt(6) + setInst = append(setInst, Instruction{ + LineNum: i, // 5 + // Literal: "Transfer(1) A-B: 6 (1)", + Typ: common.TxTypeTransfer, + From: "A", + To: "B", + TokenID: 1, + Amount: a, + Fee: 1, + }) + i++ + setInst = append(setInst, Instruction{ + LineNum: i, + // Literal: "Transfer(1) A-B: 6 (1)", + Typ: common.TxTypeTransfer, + From: "A", + To: "B", + TokenID: 1, + Amount: a, + Fee: 1, + }) + i++ + setInst = append(setInst, Instruction{ + LineNum: i, + // Literal: "Transfer(1) B-A: 6 (1)", + Typ: common.TxTypeTransfer, + From: "B", + To: "A", + TokenID: 1, + Amount: a, + Fee: 1, + }) + i++ + a = big.NewInt(3) + setInst = append(setInst, Instruction{ + LineNum: i, + // Literal: "Exit(1) A: 3 (1)", + Typ: common.TxTypeExit, + From: "A", + TokenID: 1, + Amount: a, + Fee: 1, + }) + i++ + setInst = append(setInst, Instruction{ + LineNum: i, + // Literal: "> batch", + Typ: TypeNewBatch, + }) + setInst = append(setInst, Instruction{ + LineNum: i, + // Literal: "> block", + Typ: TypeNewBlock, + }) + + tc := NewContext(common.RollupConstMaxL1UserTx) + blockFromInstructions, err := tc.GenerateBlocksFromInstructions(setInst) + assert.NoError(t, err) + require.Nil(t, err) + + // Generate block from string + setString := ` + Type: Blockchain + AddToken(1) + CreateAccountDeposit(1) A: 10 + > batchL1 + CreateAccountCoordinator(1) B + > batchL1 + Transfer(1) A-B: 6 (1) + Transfer(1) A-B: 6 (1) // on purpose this is moving more money that what it has in the account, Til should not fail + Transfer(1) B-A: 6 (1) + Exit(1) A: 3 (1) + > batch + > block + ` + tc = NewContext(common.RollupConstMaxL1UserTx) + blockFromString, err := tc.GenerateBlocks(setString) + require.Nil(t, err) + + // Generated data should be equivalent, except for Eth Addrs and BJJs + for i, strBatch := range blockFromString[0].Rollup.Batches { + // instBatch := blockFromInstructions[0].Rollup.Batches[i] + for j := 0; j < len(strBatch.L1CoordinatorTxs); j++ { + blockFromInstructions[0].Rollup.Batches[i].L1CoordinatorTxs[j].FromEthAddr = + blockFromString[0].Rollup.Batches[i].L1CoordinatorTxs[j].FromEthAddr + blockFromInstructions[0].Rollup.Batches[i].L1CoordinatorTxs[j].FromBJJ = + blockFromString[0].Rollup.Batches[i].L1CoordinatorTxs[j].FromBJJ + } + for j := 0; j < len(strBatch.L1UserTxs); j++ { + blockFromInstructions[0].Rollup.Batches[i].L1UserTxs[j].FromEthAddr = + blockFromString[0].Rollup.Batches[i].L1UserTxs[j].FromEthAddr + blockFromInstructions[0].Rollup.Batches[i].L1UserTxs[j].FromBJJ = + blockFromString[0].Rollup.Batches[i].L1UserTxs[j].FromBJJ + } + } + for i := 0; i < len(blockFromString[0].Rollup.L1UserTxs); i++ { + blockFromInstructions[0].Rollup.L1UserTxs[i].FromEthAddr = + blockFromString[0].Rollup.L1UserTxs[i].FromEthAddr + blockFromInstructions[0].Rollup.L1UserTxs[i].FromBJJ = + blockFromString[0].Rollup.L1UserTxs[i].FromBJJ + } + assert.Equal(t, blockFromString, blockFromInstructions) +}