From d50de79ca7b24ea27823d53b8393646fbd30f5a4 Mon Sep 17 00:00:00 2001
From: bojie <bojie@dexon.org>
Date: Mon, 3 Dec 2018 16:30:53 +0800
Subject: app: add app test case and benchmark (#66)

---
 core/block_validator.go |  26 +-
 core/blockchain.go      |  16 +-
 core/blockchain_test.go | 239 +++++++++++++++++
 dex/app.go              |  51 ++--
 dex/app_test.go         | 689 ++++++++++++++++++++++++++++++++++++++++++++++++
 dex/backend.go          |   2 +-
 6 files changed, 991 insertions(+), 32 deletions(-)
 create mode 100644 dex/app_test.go

diff --git a/core/block_validator.go b/core/block_validator.go
index 09539790b..660bd09f8 100644
--- a/core/block_validator.go
+++ b/core/block_validator.go
@@ -102,19 +102,23 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
 }
 
 func (v *BlockValidator) ValidateWitnessData(height uint64, data types.WitnessData) error {
-	currentBlock := v.bc.CurrentBlock()
-	if height > currentBlock.NumberU64() && height != 0 {
-		pendingBlock := v.bc.GetPendingBlockByNumber(height)
-
-		if pendingBlock.Root() != data.Root {
-			return fmt.Errorf("invalid witness root %s vs %s",
-				pendingBlock.Root().String(), data.Root.String())
-		}
-		if pendingBlock.ReceiptHash() != data.ReceiptHash {
-			return fmt.Errorf("invalid witness receipt hash %s vs %s",
-				pendingBlock.ReceiptHash().String(), data.ReceiptHash.String())
+	b := v.bc.GetPendingBlockByNumber(height)
+	if b == nil {
+		b = v.bc.GetBlockByNumber(height)
+		if b == nil {
+			return fmt.Errorf("can not find block %v either pending or confirmed block", height)
 		}
 	}
+
+	if b.Root() != data.Root {
+		return fmt.Errorf("invalid witness root %s vs %s",
+			b.Root().String(), data.Root.String())
+	}
+
+	if b.ReceiptHash() != data.ReceiptHash {
+		return fmt.Errorf("invalid witness receipt hash %s vs %s",
+			b.ReceiptHash().String(), data.ReceiptHash.String())
+	}
 	return nil
 }
 
diff --git a/core/blockchain.go b/core/blockchain.go
index 679ba1ca3..149808ff0 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -356,6 +356,18 @@ func (bc *BlockChain) GetChainLastConfirmedHeight(chainID uint32) uint64 {
 	return val.(uint64)
 }
 
+func (bc *BlockChain) GetAddressInfo(chainID uint32, address common.Address) (
+	info struct {
+		Nonce   uint64
+		Cost    *big.Int
+		Counter uint64
+	}) {
+	info.Nonce = bc.addressNonce[chainID][address]
+	info.Cost = bc.addressCost[chainID][address]
+	info.Counter = bc.addressCounter[chainID][address]
+	return
+}
+
 // loadLastState loads the last known chain state from the database. This method
 // assumes that the chain manager mutex is held.
 func (bc *BlockChain) loadLastState() error {
@@ -1769,11 +1781,11 @@ func (bc *BlockChain) processPendingBlock(
 	var witnessData types.WitnessData
 	if err := rlp.Decode(bytes.NewReader(witness.Data), &witnessData); err != nil {
 		log.Error("Witness rlp decode failed", "error", err)
-		panic(err)
+		return nil, nil, nil, fmt.Errorf("rlp decode fail: %v", err)
 	}
 
 	if err := bc.Validator().ValidateWitnessData(witness.Height, witnessData); err != nil {
-		return nil, nil, nil, fmt.Errorf("valiadte witness data error: %v", err)
+		return nil, nil, nil, fmt.Errorf("validate witness data error: %v", err)
 	}
 
 	currentBlock := bc.CurrentBlock()
diff --git a/core/blockchain_test.go b/core/blockchain_test.go
index 684f7885b..8251df6dd 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -20,12 +20,16 @@ import (
 	"fmt"
 	"math/big"
 	"math/rand"
+	"strings"
 	"sync"
 	"testing"
 	"time"
 
+	coreTypes "github.com/dexon-foundation/dexon-consensus/core/types"
+
 	"github.com/dexon-foundation/dexon/common"
 	"github.com/dexon-foundation/dexon/consensus"
+	"github.com/dexon-foundation/dexon/consensus/dexcon"
 	"github.com/dexon-foundation/dexon/consensus/ethash"
 	"github.com/dexon-foundation/dexon/core/rawdb"
 	"github.com/dexon-foundation/dexon/core/state"
@@ -34,6 +38,7 @@ import (
 	"github.com/dexon-foundation/dexon/crypto"
 	"github.com/dexon-foundation/dexon/ethdb"
 	"github.com/dexon-foundation/dexon/params"
+	"github.com/dexon-foundation/dexon/rlp"
 )
 
 // So we can deterministically seed different blockchains
@@ -42,6 +47,8 @@ var (
 	forkSeed      = 2
 )
 
+const processNum = 9
+
 // newCanonical creates a chain database, and injects a deterministic canonical
 // chain. Depending on the full flag, if creates either a full block chain or a
 // header only chain.
@@ -1388,6 +1395,238 @@ func TestLargeReorgTrieGC(t *testing.T) {
 	}
 }
 
+type dexconTest struct {
+	dexcon.Dexcon
+
+	blockReward *big.Int
+	numChains   uint32
+}
+
+// Finalize for skip governance access.
+func (d *dexconTest) Finalize(chain consensus.ChainReader, header *types.Header,
+	state *state.StateDB, txs []*types.Transaction, uncles []*types.Header,
+	receipts []*types.Receipt) (*types.Block, error) {
+	reward := new(big.Int).Div(d.blockReward, big.NewInt(int64(d.numChains)))
+	state.AddBalance(header.Coinbase, reward)
+
+	header.Reward = reward
+	header.Root = state.IntermediateRoot(true)
+	return types.NewBlock(header, txs, uncles, receipts), nil
+}
+
+func TestProcessPendingBlock(t *testing.T) {
+	db := ethdb.NewMemDatabase()
+
+	key, err := crypto.GenerateKey()
+	if err != nil {
+		t.Fatalf("new private key error: %v", err)
+	}
+
+	addr := crypto.PubkeyToAddress(key.PublicKey)
+	gspec := &Genesis{
+		Config: params.TestnetChainConfig,
+		Alloc: GenesisAlloc{
+			addr: {
+				Balance: big.NewInt(10000000000),
+				Staked:  big.NewInt(0),
+			}},
+	}
+
+	chainConfig, _, genesisErr := SetupGenesisBlock(db, gspec)
+	if genesisErr != nil {
+		t.Fatalf("set up genesis block error: %v", genesisErr)
+	}
+
+	engine := &dexconTest{
+		blockReward: chainConfig.Dexcon.BlockReward,
+		numChains:   chainConfig.Dexcon.NumChains,
+	}
+	chain, err := NewBlockChain(db, nil, chainConfig, engine, vm.Config{}, nil)
+	if err != nil {
+		t.Fatalf("failed to create tester chain: %v", err)
+	}
+
+	// Process 1 ~ N success blocks.
+	for i := 0; i < processNum; i++ {
+		var witnessDataBytes []byte
+		if i == 0 {
+			witnessData := types.WitnessData{
+				Root:        gspec.ToBlock(nil).Root(),
+				TxHash:      gspec.ToBlock(nil).TxHash(),
+				ReceiptHash: gspec.ToBlock(nil).ReceiptHash(),
+			}
+			witnessDataBytes, err = rlp.EncodeToBytes(&witnessData)
+			if err != nil {
+				t.Fatalf("rlp encode fail: %v", err)
+			}
+		} else {
+			witnessData := types.WitnessData{
+				Root:        chain.pendingBlocks[uint64(i)].block.Root(),
+				TxHash:      chain.pendingBlocks[uint64(i)].block.TxHash(),
+				ReceiptHash: chain.pendingBlocks[uint64(i)].block.ReceiptHash(),
+			}
+			witnessDataBytes, err = rlp.EncodeToBytes(&witnessData)
+			if err != nil {
+				t.Fatalf("rlp encode fail: %v", err)
+			}
+		}
+
+		tx := types.NewTransaction(uint64(i), common.Address{9}, big.NewInt(1),
+			21000, big.NewInt(1), nil)
+		signer := types.NewEIP155Signer(chainConfig.ChainID)
+		tx, err = types.SignTx(tx, signer, key)
+		if err != nil {
+			t.Fatalf("sign tx error: %v", err)
+		}
+
+		_, err = chain.ProcessPendingBlock(types.NewBlock(&types.Header{
+			Number:     new(big.Int).SetUint64(uint64(i) + 1),
+			Time:       big.NewInt(time.Now().UnixNano() / 1000000),
+			GasLimit:   10000,
+			Difficulty: big.NewInt(1),
+			Round:      0,
+		}, types.Transactions{tx}, nil, nil), &coreTypes.Witness{
+			Height: uint64(i),
+			Data:   witnessDataBytes,
+		})
+		if err != nil {
+			t.Fatalf("process pending block error: %v", err)
+		}
+
+		if chain.CurrentBlock().NumberU64() != uint64(i) {
+			t.Fatalf("expect current height %v but %v", uint64(i), chain.CurrentBlock().NumberU64())
+		}
+	}
+
+	// Witness rlp decode fail.
+	_, err = chain.ProcessPendingBlock(types.NewBlock(&types.Header{
+		Number:     new(big.Int).SetUint64(processNum + 1),
+		Time:       big.NewInt(time.Now().UnixNano() / 1000000),
+		GasLimit:   10000,
+		Difficulty: big.NewInt(1),
+		Round:      0,
+	}, nil, nil, nil), &coreTypes.Witness{
+		Height: processNum,
+	})
+	if err == nil || !strings.Contains(err.Error(), "rlp decode fail") {
+		t.Fatalf("not expected fail: %v", err)
+	}
+
+	// Validate witness fail with unknown block.
+	witnessData := types.WitnessData{
+		Root:        chain.pendingBlocks[processNum].block.Root(),
+		TxHash:      chain.pendingBlocks[processNum].block.TxHash(),
+		ReceiptHash: chain.pendingBlocks[processNum].block.ReceiptHash(),
+	}
+	witnessDataBytes, err := rlp.EncodeToBytes(&witnessData)
+	if err != nil {
+		t.Fatalf("rlp encode fail: %v", err)
+	}
+	_, err = chain.ProcessPendingBlock(types.NewBlock(&types.Header{
+		Number:     new(big.Int).SetUint64(processNum + 1),
+		Time:       big.NewInt(time.Now().UnixNano() / 1000000),
+		GasLimit:   10000,
+		Difficulty: big.NewInt(1),
+		Round:      0,
+	}, nil, nil, nil), &coreTypes.Witness{
+		Height: processNum + 1,
+		Data:   witnessDataBytes,
+	})
+	if err == nil || !strings.Contains(err.Error(), "can not find block") {
+		t.Fatalf("not expected fail: %v", err)
+	}
+
+	// Validate witness fail with unexpected root.
+	witnessData = types.WitnessData{
+		Root:        chain.pendingBlocks[processNum].block.Root(),
+		TxHash:      chain.pendingBlocks[processNum].block.TxHash(),
+		ReceiptHash: chain.pendingBlocks[processNum].block.ReceiptHash(),
+	}
+	witnessDataBytes, err = rlp.EncodeToBytes(&witnessData)
+	if err != nil {
+		t.Fatalf("rlp encode fail: %v", err)
+	}
+	_, err = chain.ProcessPendingBlock(types.NewBlock(&types.Header{
+		Number:     new(big.Int).SetUint64(processNum + 1),
+		Time:       big.NewInt(time.Now().UnixNano() / 1000000),
+		GasLimit:   10000,
+		Difficulty: big.NewInt(1),
+		Round:      0,
+	}, nil, nil, nil), &coreTypes.Witness{
+		Height: processNum - 1,
+		Data:   witnessDataBytes,
+	})
+	if err == nil || !strings.Contains(err.Error(), "invalid witness root") {
+		t.Fatalf("not expected fail: %v", err)
+	}
+
+	// Apply transaction fail with insufficient fund.
+	witnessData = types.WitnessData{
+		Root:        chain.pendingBlocks[processNum].block.Root(),
+		TxHash:      chain.pendingBlocks[processNum].block.TxHash(),
+		ReceiptHash: chain.pendingBlocks[processNum].block.ReceiptHash(),
+	}
+	witnessDataBytes, err = rlp.EncodeToBytes(&witnessData)
+	if err != nil {
+		t.Fatalf("rlp encode fail: %v", err)
+	}
+
+	tx := types.NewTransaction(processNum, common.Address{9}, big.NewInt(99999999999999),
+		21000, big.NewInt(1), nil)
+	signer := types.NewEIP155Signer(chainConfig.ChainID)
+	tx, err = types.SignTx(tx, signer, key)
+	if err != nil {
+		t.Fatalf("sign tx error: %v", err)
+	}
+
+	_, err = chain.ProcessPendingBlock(types.NewBlock(&types.Header{
+		Number:     new(big.Int).SetUint64(processNum + 1),
+		Time:       big.NewInt(time.Now().UnixNano() / 1000000),
+		GasLimit:   10000,
+		Difficulty: big.NewInt(1),
+		Round:      0,
+	}, types.Transactions{tx}, nil, nil), &coreTypes.Witness{
+		Height: processNum,
+		Data:   witnessDataBytes,
+	})
+	if err == nil || !strings.Contains(err.Error(), "apply transaction error") {
+		t.Fatalf("not expected fail: %v", err)
+	}
+
+	// Apply transaction fail with nonce too height.
+	witnessData = types.WitnessData{
+		Root:        chain.pendingBlocks[processNum].block.Root(),
+		TxHash:      chain.pendingBlocks[processNum].block.TxHash(),
+		ReceiptHash: chain.pendingBlocks[processNum].block.ReceiptHash(),
+	}
+	witnessDataBytes, err = rlp.EncodeToBytes(&witnessData)
+	if err != nil {
+		t.Fatalf("rlp encode fail: %v", err)
+	}
+
+	tx = types.NewTransaction(999, common.Address{9}, big.NewInt(1),
+		21000, big.NewInt(1), nil)
+	signer = types.NewEIP155Signer(chainConfig.ChainID)
+	tx, err = types.SignTx(tx, signer, key)
+	if err != nil {
+		t.Fatalf("sign tx error: %v", err)
+	}
+
+	_, err = chain.ProcessPendingBlock(types.NewBlock(&types.Header{
+		Number:     new(big.Int).SetUint64(processNum + 1),
+		Time:       big.NewInt(time.Now().UnixNano() / 1000000),
+		GasLimit:   10000,
+		Difficulty: big.NewInt(1),
+		Round:      0,
+	}, types.Transactions{tx}, nil, nil), &coreTypes.Witness{
+		Height: processNum,
+		Data:   witnessDataBytes,
+	})
+	if err == nil || !strings.Contains(err.Error(), "apply transaction error") {
+		t.Fatalf("not expected fail: %v", err)
+	}
+}
+
 // Benchmarks large blocks with value transfers to non-existing accounts
 func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks int, recipientFn func(uint64) common.Address, dataFn func(uint64) []byte) {
 	var (
diff --git a/dex/app.go b/dex/app.go
index 02e0a484f..cf36ad816 100644
--- a/dex/app.go
+++ b/dex/app.go
@@ -29,7 +29,6 @@ import (
 	"github.com/dexon-foundation/dexon/common"
 	"github.com/dexon-foundation/dexon/core"
 	"github.com/dexon-foundation/dexon/core/types"
-	"github.com/dexon-foundation/dexon/core/vm"
 	"github.com/dexon-foundation/dexon/ethdb"
 	"github.com/dexon-foundation/dexon/log"
 	"github.com/dexon-foundation/dexon/rlp"
@@ -46,20 +45,13 @@ type DexconApp struct {
 	gov        *DexconGovernance
 	chainDB    ethdb.Database
 	config     *Config
-	vmConfig   vm.Config
 
 	chainLocksInitMu sync.Mutex
 	chainLocks       map[uint32]*sync.RWMutex
 
-	notifyMu        sync.Mutex
-	notifyChan      sync.Map
 	chainLatestRoot sync.Map
 }
 
-type notify struct {
-	results []chan uint64
-}
-
 type witnessData struct {
 	Root        common.Hash
 	TxHash      common.Hash
@@ -67,14 +59,13 @@ type witnessData struct {
 }
 
 func NewDexconApp(txPool *core.TxPool, blockchain *core.BlockChain, gov *DexconGovernance,
-	chainDB ethdb.Database, config *Config, vmConfig vm.Config) *DexconApp {
+	chainDB ethdb.Database, config *Config) *DexconApp {
 	return &DexconApp{
 		txPool:     txPool,
 		blockchain: blockchain,
 		gov:        gov,
 		chainDB:    chainDB,
 		config:     config,
-		vmConfig:   vmConfig,
 		chainLocks: make(map[uint32]*sync.RWMutex),
 	}
 }
@@ -275,17 +266,39 @@ func (d *DexconApp) VerifyBlock(block *coreTypes.Block) coreTypes.BlockVerifySta
 	}
 
 	// Wait until the witnessed root is seen on our local chain.
-	for i := 0; i < verifyBlockMaxRetries && err != nil; i++ {
+	for i := 0; i < verifyBlockMaxRetries; i++ {
+		if d.blockchain.GetPendingHeight() < block.Witness.Height {
+			log.Debug("Pending height < witness height")
+			time.Sleep(500 * time.Millisecond)
+			continue
+		}
+
+		b := d.blockchain.GetPendingBlockByNumber(block.Witness.Height)
+		if b == nil {
+			b = d.blockchain.GetBlockByNumber(block.Witness.Height)
+			if b == nil {
+				log.Error("Can not get block by height %v", block.Witness.Height)
+				return coreTypes.VerifyInvalidBlock
+			}
+		}
+
+		if b.Root() != witnessData.Root {
+			log.Error("Witness root not correct expect %v but %v", b.Root(), witnessData.Root)
+			return coreTypes.VerifyInvalidBlock
+		}
+
+		if b.ReceiptHash() != witnessData.ReceiptHash {
+			log.Error("Witness receipt hash not correct expect %v but %v", b.ReceiptHash(), witnessData.ReceiptHash)
+			return coreTypes.VerifyInvalidBlock
+		}
+
 		_, err = d.blockchain.StateAt(witnessData.Root)
 		if err != nil {
-			log.Debug("Witness root not found, retry in 500ms", "error", err)
-			time.Sleep(500 * time.Millisecond)
+			log.Error("Get state by root %v error: %v", witnessData.Root, err)
+			return coreTypes.VerifyInvalidBlock
 		}
-	}
 
-	if err != nil {
-		log.Error("Expected witness root not in stateDB", "err", err)
-		return coreTypes.VerifyRetryLater
+		break
 	}
 
 	d.chainRLock(block.Position.ChainID)
@@ -457,5 +470,7 @@ func (d *DexconApp) BlockConfirmed(block coreTypes.Block) {
 	d.chainLock(block.Position.ChainID)
 	defer d.chainUnlock(block.Position.ChainID)
 
-	d.blockchain.AddConfirmedBlock(&block)
+	if err := d.blockchain.AddConfirmedBlock(&block); err != nil {
+		panic(err)
+	}
 }
diff --git a/dex/app_test.go b/dex/app_test.go
new file mode 100644
index 000000000..cbd29d0dc
--- /dev/null
+++ b/dex/app_test.go
@@ -0,0 +1,689 @@
+package dex
+
+import (
+	"crypto/ecdsa"
+	"fmt"
+	"math/big"
+	"testing"
+	"time"
+
+	coreCommon "github.com/dexon-foundation/dexon-consensus/common"
+	coreTypes "github.com/dexon-foundation/dexon-consensus/core/types"
+
+	"github.com/dexon-foundation/dexon/common"
+	"github.com/dexon-foundation/dexon/consensus/dexcon"
+	"github.com/dexon-foundation/dexon/core"
+	"github.com/dexon-foundation/dexon/core/types"
+	"github.com/dexon-foundation/dexon/core/vm"
+	"github.com/dexon-foundation/dexon/crypto"
+	"github.com/dexon-foundation/dexon/ethdb"
+	"github.com/dexon-foundation/dexon/params"
+	"github.com/dexon-foundation/dexon/rlp"
+)
+
+func TestPreparePayload(t *testing.T) {
+	key, err := crypto.GenerateKey()
+	if err != nil {
+		t.Errorf("hex to ecdsa error: %v", err)
+	}
+
+	dex, err := newTestDexonWithGenesis(key)
+	if err != nil {
+		t.Errorf("new test dexon error: %v", err)
+	}
+
+	signer := types.NewEIP155Signer(dex.chainConfig.ChainID)
+
+	var expectTx types.Transactions
+	for i := 0; i < 5; i++ {
+		tx, err := addTx(dex, i, signer, key)
+		if err != nil {
+			t.Errorf("add tx error: %v", err)
+		}
+		expectTx = append(expectTx, tx)
+	}
+
+	// This transaction will not be included.
+	_, err = addTx(dex, 100, signer, key)
+	if err != nil {
+		t.Errorf("add tx error: %v", err)
+	}
+
+	chainID := new(big.Int).Mod(crypto.PubkeyToAddress(key.PublicKey).Big(),
+		big.NewInt(int64(dex.chainConfig.Dexcon.NumChains)))
+	var chainNum uint32
+	for chainNum = 0; chainNum < dex.chainConfig.Dexcon.NumChains; chainNum++ {
+
+		payload, err := dex.app.PreparePayload(coreTypes.Position{ChainID: chainNum})
+		if err != nil {
+			t.Errorf("prepare payload error: %v", err)
+		}
+
+		var transactions types.Transactions
+		err = rlp.DecodeBytes(payload, &transactions)
+		if err != nil {
+			t.Errorf("rlp decode error: %v", err)
+		}
+
+		// Only one chain id allow prepare transactions.
+		if chainID.Uint64() == uint64(chainNum) && len(transactions) != 5 {
+			t.Errorf("incorrect transaction num expect %v but %v", 5, len(transactions))
+		} else if chainID.Uint64() != uint64(chainNum) && len(transactions) != 0 {
+			t.Errorf("expect empty slice but %v", len(transactions))
+		}
+
+		for i, tx := range transactions {
+			if expectTx[i].Gas() != tx.Gas() {
+				t.Errorf("unexpected gas expect %v but %v", expectTx[i].Gas(), tx.Gas())
+			}
+
+			if expectTx[i].Hash() != tx.Hash() {
+				t.Errorf("unexpected hash expect %v but %v", expectTx[i].Hash(), tx.Hash())
+			}
+
+			if expectTx[i].Nonce() != tx.Nonce() {
+				t.Errorf("unexpected nonce expect %v but %v", expectTx[i].Nonce(), tx.Nonce())
+			}
+
+			if expectTx[i].GasPrice().Uint64() != tx.GasPrice().Uint64() {
+				t.Errorf("unexpected gas price expect %v but %v",
+					expectTx[i].GasPrice().Uint64(), tx.GasPrice().Uint64())
+			}
+		}
+	}
+}
+
+func TestPrepareWitness(t *testing.T) {
+	key, err := crypto.GenerateKey()
+	if err != nil {
+		t.Errorf("hex to ecdsa error: %v", err)
+	}
+
+	dex, err := newTestDexonWithGenesis(key)
+	if err != nil {
+		t.Errorf("new test dexon error: %v", err)
+	}
+
+	currentBlock := dex.blockchain.CurrentBlock()
+
+	witness, err := dex.app.PrepareWitness(0)
+	if err != nil {
+		t.Errorf("prepare witness error: %v", err)
+	}
+
+	if witness.Height != currentBlock.NumberU64() {
+		t.Errorf("unexpeted witness height %v", witness.Height)
+	}
+
+	var witnessData witnessData
+	err = rlp.DecodeBytes(witness.Data, &witnessData)
+	if err != nil {
+		t.Errorf("rlp decode error: %v", err)
+	}
+
+	if witnessData.TxHash != currentBlock.TxHash() {
+		t.Errorf("expect tx hash %v but %v", currentBlock.TxHash(), witnessData.TxHash)
+	}
+
+	if witnessData.Root != currentBlock.Root() {
+		t.Errorf("expect root %v but %v", currentBlock.Root(), witnessData.Root)
+	}
+
+	if witnessData.ReceiptHash != currentBlock.ReceiptHash() {
+		t.Errorf("expect receipt hash %v but %v", currentBlock.ReceiptHash(), witnessData.ReceiptHash)
+	}
+
+	if _, err := dex.app.PrepareWitness(999); err == nil {
+		t.Errorf("it must be get error from prepare")
+	} else {
+		t.Logf("Nice error: %v", err)
+	}
+}
+
+func TestVerifyBlock(t *testing.T) {
+	key, err := crypto.GenerateKey()
+	if err != nil {
+		t.Errorf("hex to ecdsa error: %v", err)
+	}
+
+	dex, err := newTestDexonWithGenesis(key)
+	if err != nil {
+		t.Errorf("new test dexon error: %v", err)
+	}
+
+	// Prepare first confirmed block.
+	prepareConfirmedBlocks(dex, []*ecdsa.PrivateKey{key}, 0)
+
+	chainID := new(big.Int).Mod(crypto.PubkeyToAddress(key.PublicKey).Big(),
+		big.NewInt(int64(dex.chainConfig.Dexcon.NumChains)))
+
+	// Prepare normal block.
+	block := coreTypes.NewBlock()
+	block.Hash = coreCommon.NewRandomHash()
+	block.Position.ChainID = uint32(chainID.Uint64())
+	block.Position.Height = 1
+	block.Payload, block.Witness, err = prepareDataWithoutTxPool(dex, key, 0, 100)
+	if err != nil {
+		t.Errorf("prepare data error: %v", err)
+	}
+
+	// Expect ok.
+	status := dex.app.VerifyBlock(block)
+	if status != coreTypes.VerifyOK {
+		t.Errorf("verify fail: %v", status)
+	}
+
+	// Prepare invalid nonce tx.
+	block = coreTypes.NewBlock()
+	block.Hash = coreCommon.NewRandomHash()
+	block.Position.ChainID = uint32(chainID.Uint64())
+	block.Position.Height = 1
+	block.Payload, block.Witness, err = prepareDataWithoutTxPool(dex, key, 1, 100)
+	if err != nil {
+		t.Errorf("prepare data error: %v", err)
+	}
+
+	// Expect invalid block.
+	status = dex.app.VerifyBlock(block)
+	if status != coreTypes.VerifyInvalidBlock {
+		t.Errorf("verify fail: %v", status)
+	}
+
+	// Prepare invalid block height.
+	block = coreTypes.NewBlock()
+	block.Hash = coreCommon.NewRandomHash()
+	block.Position.ChainID = uint32(chainID.Uint64())
+	block.Position.Height = 2
+	block.Payload, block.Witness, err = prepareDataWithoutTxPool(dex, key, 0, 100)
+	if err != nil {
+		t.Errorf("prepare data error: %v", err)
+	}
+
+	// Expect retry later.
+	status = dex.app.VerifyBlock(block)
+	if status != coreTypes.VerifyRetryLater {
+		t.Errorf("verify fail expect retry later but get %v", status)
+	}
+
+	// Prepare reach block limit.
+	block = coreTypes.NewBlock()
+	block.Hash = coreCommon.NewRandomHash()
+	block.Position.ChainID = uint32(chainID.Uint64())
+	block.Position.Height = 1
+	block.Payload, block.Witness, err = prepareDataWithoutTxPool(dex, key, 0, 10000)
+	if err != nil {
+		t.Errorf("prepare data error: %v", err)
+	}
+
+	// Expect invalid block.
+	status = dex.app.VerifyBlock(block)
+	if status != coreTypes.VerifyInvalidBlock {
+		t.Errorf("verify fail expect invalid block but get %v", status)
+	}
+
+	// Prepare insufficient funds.
+	block = coreTypes.NewBlock()
+	block.Hash = coreCommon.NewRandomHash()
+	block.Position.ChainID = uint32(chainID.Uint64())
+	block.Position.Height = 1
+	_, block.Witness, err = prepareDataWithoutTxPool(dex, key, 0, 0)
+	if err != nil {
+		t.Errorf("prepare data error: %v", err)
+	}
+
+	signer := types.NewEIP155Signer(dex.chainConfig.ChainID)
+	tx := types.NewTransaction(
+		0,
+		common.BytesToAddress([]byte{9}),
+		big.NewInt(50000000000000001),
+		params.TxGas,
+		big.NewInt(1),
+		nil)
+	tx, err = types.SignTx(tx, signer, key)
+	if err != nil {
+		return
+	}
+
+	block.Payload, err = rlp.EncodeToBytes(types.Transactions{tx})
+	if err != nil {
+		return
+	}
+
+	// expect invalid block
+	status = dex.app.VerifyBlock(block)
+	if status != coreTypes.VerifyInvalidBlock {
+		t.Errorf("verify fail expect invalid block but get %v", status)
+	}
+
+	// Prepare invalid intrinsic gas.
+	block = coreTypes.NewBlock()
+	block.Hash = coreCommon.NewRandomHash()
+	block.Position.ChainID = uint32(chainID.Uint64())
+	block.Position.Height = 1
+	_, block.Witness, err = prepareDataWithoutTxPool(dex, key, 0, 0)
+	if err != nil {
+		t.Errorf("prepare data error: %v", err)
+	}
+
+	signer = types.NewEIP155Signer(dex.chainConfig.ChainID)
+	tx = types.NewTransaction(
+		0,
+		common.BytesToAddress([]byte{9}),
+		big.NewInt(1),
+		params.TxGas,
+		big.NewInt(1),
+		make([]byte, 1))
+	tx, err = types.SignTx(tx, signer, key)
+	if err != nil {
+		return
+	}
+
+	block.Payload, err = rlp.EncodeToBytes(types.Transactions{tx})
+	if err != nil {
+		return
+	}
+
+	// Expect invalid block.
+	status = dex.app.VerifyBlock(block)
+	if status != coreTypes.VerifyInvalidBlock {
+		t.Errorf("verify fail expect invalid block but get %v", status)
+	}
+
+	// Prepare invalid transactions with nonce.
+	block = coreTypes.NewBlock()
+	block.Hash = coreCommon.NewRandomHash()
+	block.Position.ChainID = uint32(chainID.Uint64())
+	block.Position.Height = 1
+	_, block.Witness, err = prepareDataWithoutTxPool(dex, key, 0, 0)
+	if err != nil {
+		t.Errorf("prepare data error: %v", err)
+	}
+
+	signer = types.NewEIP155Signer(dex.chainConfig.ChainID)
+	tx1 := types.NewTransaction(
+		0,
+		common.BytesToAddress([]byte{9}),
+		big.NewInt(1),
+		params.TxGas,
+		big.NewInt(1),
+		make([]byte, 1))
+	tx1, err = types.SignTx(tx, signer, key)
+	if err != nil {
+		return
+	}
+
+	// Invalid nonce.
+	tx2 := types.NewTransaction(
+		2,
+		common.BytesToAddress([]byte{9}),
+		big.NewInt(1),
+		params.TxGas,
+		big.NewInt(1),
+		make([]byte, 1))
+	tx2, err = types.SignTx(tx, signer, key)
+	if err != nil {
+		return
+	}
+
+	block.Payload, err = rlp.EncodeToBytes(types.Transactions{tx1, tx2})
+	if err != nil {
+		return
+	}
+
+	// Expect invalid block.
+	status = dex.app.VerifyBlock(block)
+	if status != coreTypes.VerifyInvalidBlock {
+		t.Errorf("verify fail expect invalid block but get %v", status)
+	}
+}
+
+func TestBlockConfirmed(t *testing.T) {
+	key, err := crypto.GenerateKey()
+	if err != nil {
+		t.Errorf("hex to ecdsa error: %v", err)
+	}
+
+	dex, err := newTestDexonWithGenesis(key)
+	if err != nil {
+		t.Errorf("new test dexon error: %v", err)
+	}
+
+	chainID := new(big.Int).Mod(crypto.PubkeyToAddress(key.PublicKey).Big(),
+		big.NewInt(int64(dex.chainConfig.Dexcon.NumChains)))
+
+	var (
+		expectCost    big.Int
+		expectNonce   uint64
+		expectCounter uint64
+	)
+	for i := 0; i < 10; i++ {
+		var startNonce int
+		if expectNonce != 0 {
+			startNonce = int(expectNonce) + 1
+		}
+		payload, witness, cost, nonce, err := prepareData(dex, key, startNonce, 50)
+		if err != nil {
+			t.Errorf("prepare data error: %v", err)
+		}
+		expectCost.Add(&expectCost, &cost)
+		expectNonce = nonce
+
+		block := coreTypes.NewBlock()
+		block.Hash = coreCommon.NewRandomHash()
+		block.Witness = witness
+		block.Payload = payload
+		block.Position.ChainID = uint32(chainID.Uint64())
+
+		dex.app.BlockConfirmed(*block)
+		expectCounter++
+	}
+
+	info := dex.app.blockchain.GetAddressInfo(uint32(chainID.Uint64()),
+		crypto.PubkeyToAddress(key.PublicKey))
+
+	if info.Counter != expectCounter {
+		t.Errorf("expect address counter is %v but %v", expectCounter, info.Counter)
+	}
+
+	if info.Cost.Cmp(&expectCost) != 0 {
+		t.Errorf("expect address cost is %v but %v", expectCost.Uint64(), info.Cost.Uint64())
+	}
+
+	if info.Nonce != expectNonce {
+		t.Errorf("expect address nonce is %v but %v", expectNonce, info.Nonce)
+	}
+}
+
+func TestBlockDelivered(t *testing.T) {
+	key, err := crypto.GenerateKey()
+	if err != nil {
+		t.Errorf("hex to ecdsa error: %v", err)
+	}
+
+	dex, err := newTestDexonWithGenesis(key)
+	if err != nil {
+		t.Errorf("new test dexon error: %v", err)
+	}
+
+	address := crypto.PubkeyToAddress(key.PublicKey)
+	firstBlocksInfo, err := prepareConfirmedBlocks(dex, []*ecdsa.PrivateKey{key}, 50)
+	if err != nil {
+		t.Errorf("preapare confirmed block error: %v", err)
+	}
+
+	dex.app.BlockDelivered(firstBlocksInfo[0].Block.Hash, firstBlocksInfo[0].Block.Position,
+		coreTypes.FinalizationResult{
+			Timestamp: time.Now(),
+			Height:    1,
+		})
+
+	pendingBlock, pendingState := dex.blockchain.GetPending()
+
+	r, ok := dex.app.chainLatestRoot.Load(firstBlocksInfo[0].Block.Position.ChainID)
+	if !ok {
+		t.Errorf("lastest root cache not exist")
+	}
+
+	if *r.(*common.Hash) != pendingBlock.Root() {
+		t.Errorf("incorrect pending root")
+	}
+
+	currentBlock := dex.blockchain.CurrentBlock()
+	if currentBlock.NumberU64() != 0 {
+		t.Errorf("unexpected current block number %v", currentBlock.NumberU64())
+	}
+
+	pendingNonce := pendingState.GetNonce(address)
+	if pendingNonce != firstBlocksInfo[0].Nonce+1 {
+		t.Errorf("unexpected pending state nonce %v", pendingNonce)
+	}
+
+	balance := pendingState.GetBalance(address)
+	if new(big.Int).Add(balance, &firstBlocksInfo[0].Cost).Cmp(big.NewInt(50000000000000000)) != 0 {
+		t.Errorf("unexpected pending state balance %v", balance)
+	}
+
+	// prepare second block to witness first block
+	secondBlocksInfo, err := prepareConfirmedBlocks(dex, []*ecdsa.PrivateKey{key}, 25)
+	if err != nil {
+		t.Errorf("preapare confirmed block error: %v", err)
+	}
+
+	dex.app.BlockDelivered(secondBlocksInfo[0].Block.Hash, secondBlocksInfo[0].Block.Position,
+		coreTypes.FinalizationResult{
+			Timestamp: time.Now(),
+			Height:    2,
+		})
+
+	// second block witness first block, so current block number should be 1
+	currentBlock = dex.blockchain.CurrentBlock()
+	if currentBlock.NumberU64() != 1 {
+		t.Errorf("unexpected current block number %v", currentBlock.NumberU64())
+	}
+
+	currentState, err := dex.blockchain.State()
+	if err != nil {
+		t.Errorf("current state error: %v", err)
+	}
+
+	currentNonce := currentState.GetNonce(address)
+	if currentNonce != firstBlocksInfo[0].Nonce+1 {
+		t.Errorf("unexpected current state nonce %v", currentNonce)
+	}
+
+	balance = currentState.GetBalance(address)
+	if new(big.Int).Add(balance, &firstBlocksInfo[0].Cost).Cmp(big.NewInt(50000000000000000)) != 0 {
+		t.Errorf("unexpected current state balance %v", balance)
+	}
+}
+
+func BenchmarkBlockDeliveredFlow(b *testing.B) {
+	key, err := crypto.GenerateKey()
+	if err != nil {
+		b.Errorf("hex to ecdsa error: %v", err)
+		return
+	}
+
+	dex, err := newTestDexonWithGenesis(key)
+	if err != nil {
+		b.Errorf("new test dexon error: %v", err)
+	}
+
+	b.ResetTimer()
+	for i := 1; i <= b.N; i++ {
+		blocksInfo, err := prepareConfirmedBlocks(dex, []*ecdsa.PrivateKey{key}, 100)
+		if err != nil {
+			b.Errorf("preapare confirmed block error: %v", err)
+			return
+		}
+
+		dex.app.BlockDelivered(blocksInfo[0].Block.Hash, blocksInfo[0].Block.Position,
+			coreTypes.FinalizationResult{
+				Timestamp: time.Now(),
+				Height:    uint64(i),
+			})
+	}
+}
+
+func newTestDexonWithGenesis(allocKey *ecdsa.PrivateKey) (*Dexon, error) {
+	db := ethdb.NewMemDatabase()
+
+	testBankAddress := crypto.PubkeyToAddress(allocKey.PublicKey)
+	genesis := core.DefaultTestnetGenesisBlock()
+	genesis.Alloc = core.GenesisAlloc{
+		testBankAddress: {
+			Balance: big.NewInt(100000000000000000),
+			Staked:  big.NewInt(50000000000000000),
+		},
+	}
+	chainConfig, _, err := core.SetupGenesisBlock(db, genesis)
+	if err != nil {
+		return nil, err
+	}
+
+	key, err := crypto.GenerateKey()
+	if err != nil {
+		return nil, err
+	}
+
+	config := Config{PrivateKey: key}
+	vmConfig := vm.Config{IsBlockProposer: true}
+
+	engine := dexcon.New()
+
+	dex := &Dexon{
+		chainDb:     db,
+		chainConfig: chainConfig,
+		networkID:   config.NetworkId,
+		engine:      engine,
+	}
+
+	dex.blockchain, err = core.NewBlockChain(db, nil, chainConfig, engine, vmConfig, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	txPoolConfig := core.DefaultTxPoolConfig
+	dex.txPool = core.NewTxPool(txPoolConfig, chainConfig, dex.blockchain, true)
+
+	dex.APIBackend = &DexAPIBackend{dex, nil}
+	dex.governance = NewDexconGovernance(dex.APIBackend, dex.chainConfig, config.PrivateKey)
+	engine.SetConfigFetcher(dex.governance)
+	dex.app = NewDexconApp(dex.txPool, dex.blockchain, dex.governance, db, &config)
+
+	return dex, nil
+}
+
+// Add tx into tx pool.
+func addTx(dex *Dexon, nonce int, signer types.Signer, key *ecdsa.PrivateKey) (
+	*types.Transaction, error) {
+	tx := types.NewTransaction(
+		uint64(nonce),
+		common.BytesToAddress([]byte{9}),
+		big.NewInt(int64(nonce*1)),
+		params.TxGas,
+		big.NewInt(1),
+		nil)
+	tx, err := types.SignTx(tx, signer, key)
+	if err != nil {
+		return nil, err
+	}
+
+	if err := dex.txPool.AddRemote(tx); err != nil {
+		return nil, err
+	}
+
+	return tx, nil
+}
+
+// Prepare data with given transaction number and start nonce.
+func prepareData(dex *Dexon, key *ecdsa.PrivateKey, startNonce, txNum int) (
+	payload []byte, witness coreTypes.Witness, cost big.Int, nonce uint64, err error) {
+	signer := types.NewEIP155Signer(dex.chainConfig.ChainID)
+	chainID := new(big.Int).Mod(crypto.PubkeyToAddress(key.PublicKey).Big(),
+		big.NewInt(int64(dex.chainConfig.Dexcon.NumChains)))
+
+	for n := startNonce; n < startNonce+txNum; n++ {
+		var tx *types.Transaction
+		tx, err = addTx(dex, n, signer, key)
+		if err != nil {
+			return
+		}
+
+		cost.Add(&cost, tx.Cost())
+		nonce = uint64(n)
+	}
+
+	payload, err = dex.app.PreparePayload(coreTypes.Position{ChainID: uint32(chainID.Uint64())})
+	if err != nil {
+		return
+	}
+
+	witness, err = dex.app.PrepareWitness(0)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func prepareDataWithoutTxPool(dex *Dexon, key *ecdsa.PrivateKey, startNonce, txNum int) (
+	payload []byte, witness coreTypes.Witness, err error) {
+	signer := types.NewEIP155Signer(dex.chainConfig.ChainID)
+
+	var transactions types.Transactions
+	for n := startNonce; n < startNonce+txNum; n++ {
+		tx := types.NewTransaction(
+			uint64(n),
+			common.BytesToAddress([]byte{9}),
+			big.NewInt(int64(n*1)),
+			params.TxGas,
+			big.NewInt(1),
+			nil)
+		tx, err = types.SignTx(tx, signer, key)
+		if err != nil {
+			return
+		}
+		transactions = append(transactions, tx)
+	}
+
+	payload, err = rlp.EncodeToBytes(&transactions)
+	if err != nil {
+		return
+	}
+
+	witness, err = dex.app.PrepareWitness(0)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func prepareConfirmedBlocks(dex *Dexon, keys []*ecdsa.PrivateKey, txNum int) (blocksInfo []struct {
+	Block *coreTypes.Block
+	Cost  big.Int
+	Nonce uint64
+}, err error) {
+	for _, key := range keys {
+		address := crypto.PubkeyToAddress(key.PublicKey)
+		chainID := new(big.Int).Mod(address.Big(),
+			big.NewInt(int64(dex.chainConfig.Dexcon.NumChains)))
+
+		// Prepare one block for pending.
+		var (
+			payload []byte
+			witness coreTypes.Witness
+			cost    big.Int
+			nonce   uint64
+		)
+		startNonce := dex.txPool.State().GetNonce(address)
+		payload, witness, cost, nonce, err = prepareData(dex, key, int(startNonce), txNum)
+		if err != nil {
+			err = fmt.Errorf("prepare data error: %v", err)
+			return
+		}
+
+		block := coreTypes.NewBlock()
+		block.Hash = coreCommon.NewRandomHash()
+		block.Witness = witness
+		block.Payload = payload
+		block.Position.ChainID = uint32(chainID.Uint64())
+
+		status := dex.app.VerifyBlock(block)
+		if status != coreTypes.VerifyOK {
+			err = fmt.Errorf("verify fail: %v", status)
+			return
+		}
+
+		dex.app.BlockConfirmed(*block)
+
+		blocksInfo = append(blocksInfo, struct {
+			Block *coreTypes.Block
+			Cost  big.Int
+			Nonce uint64
+		}{Block: block, Cost: cost, Nonce: nonce})
+	}
+
+	return
+}
diff --git a/dex/backend.go b/dex/backend.go
index bd95b3b70..17bf542a2 100644
--- a/dex/backend.go
+++ b/dex/backend.go
@@ -150,7 +150,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Dexon, error) {
 
 	// Dexcon related objects.
 	dex.governance = NewDexconGovernance(dex.APIBackend, dex.chainConfig, config.PrivateKey)
-	dex.app = NewDexconApp(dex.txPool, dex.blockchain, dex.governance, chainDb, config, vmConfig)
+	dex.app = NewDexconApp(dex.txPool, dex.blockchain, dex.governance, chainDb, config)
 
 	// Set config fetcher so engine can fetch current system configuration from state.
 	engine.SetConfigFetcher(dex.governance)
-- 
cgit v1.2.3