aboutsummaryrefslogblamecommitdiffstats
path: root/dex/app_test.go
blob: d2837b1749edbacad745aaa1e4c2386e36394ef3 (plain) (tree)
1
2
3
4
5
6
7





                      
                 





                                                                          
                                                        













                                                            
                                                       



                                                
                                                         







                                                                
                                                         






                                                 
                                                 






                                                                                   

                                                            

                                                                                             
                                                                  




                                                             
                                                             



                                                                                   
                                                                                                    
                                                                                          
                                                                                



                                                          
                                                                                                        


                                                            
                                                                                                           


                                                              
                                                                                                              


                                                                                      
                                                                                 








                                                                                                
                                                       



                                                
                                                         





                                                     
                                                          


                                                       
                                                                       

         

                                                              
                       
                                                     

         

                                                                                        


                                                              
                                                             







                                             
                                                       



                                                
                                                         

         


                                                                                








                                                                         
                                
                                   


                                                         
                                                                     

                                                                                      
                                                       




                                            
                                                   


                                    
                                  


                                                         
                                                                     

                                                                                      
                                                       




                                                   
                                                   


                                        
                                  


                                                         
                                                                     

                                                                                      
                                                       




                                                 
                                                                             


                                     
                                  


                                                         
                                                                     

                                                                                        
                                                       




                                                   
                                                                               


                                      
                                  


                                                         
                                                                     

                                                                        
                                                       






















                                                                      
                                                                               


                                         
                                  


                                                         
                                                                     

                                                                        
                                                       






















                                                                      
                                                                               


                                                   
                                  


                                                         
                                                                     

                                                                        
                                                       



































                                                                            
                                                                               





                                        
                                                       



                                                
                                                         




                                                                                


                                                                











                                                                                           
                                                               



                                                  
                                           


                                                       
                                                                             









                                                                           
                                                                                            


                                            
                                                                                                     


                                      
                                                                                      





                                        
                                                       



                                                
                                                         

         





                                                                                


                                                                                         
                                                                   







                                                                                                
                                                   
                       
                                                    
         
                                                     
                                          
                                                                                        



                                                       
                                                                           

         
                                                   
                                                                                                        
                                                                        


         


                                        
                                                       





                                                                                      
                                                         

         





                                                                                

                                                                           
                                                        




                                                           














                                                                    

                                                                        
                                                                             



                                                                                      
                                                        






                                                          

                                                                                     
                                                        









                                                                         
                                                        









                                                                                   
                                                        

                                 
                                                     









                                                                                   
                                                        

                                 
                                                                       




                                                                                         
                                                               








                                                          



                                                                                   
                                                        

                                 
                                                     



                                                          
                                     


                  


                                                
                                                       




                                                
                                                         





                                                                                             
                                                                           













                                                                                              




                                        



                                                                     


                                                                       






                                                                  





















                                                                                                
                                                 

























































                                                                                                    


                                                                                                         
                                                                                             


















































                                                                                                                      
























































                                                                                                   
                                           



                                                                 
                                                                             

















                                                                   
package dex

import (
    "crypto/ecdsa"
    "fmt"
    "math/big"
    "strings"
    "testing"
    "time"

    coreCommon "github.com/dexon-foundation/dexon-consensus/common"
    coreTypes "github.com/dexon-foundation/dexon-consensus/core/types"

    "github.com/dexon-foundation/dexon/accounts/abi"
    "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.Fatalf("hex to ecdsa error: %v", err)
    }

    dex, err := newTestDexonWithGenesis(key)
    if err != nil {
        t.Fatalf("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.Fatalf("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.Fatalf("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++ {

        root := dex.blockchain.CurrentBlock().Root()
        dex.app.chainRoot.Store(chainNum, &root)
        payload, err := dex.app.PreparePayload(coreTypes.Position{ChainID: chainNum})
        if err != nil {
            t.Fatalf("prepare payload error: %v", err)
        }

        var transactions types.Transactions
        err = rlp.DecodeBytes(payload, &transactions)
        if err != nil {
            t.Fatalf("rlp decode error: %v", err)
        }

        // Only one chain id allow prepare transactions.
        if chainID.Uint64() == uint64(chainNum) && len(transactions) != 5 {
            t.Fatalf("incorrect transaction num expect %v but %v", 5, len(transactions))
        } else if chainID.Uint64() != uint64(chainNum) && len(transactions) != 0 {
            t.Fatalf("expect empty slice but %v", len(transactions))
        }

        for i, tx := range transactions {
            if expectTx[i].Gas() != tx.Gas() {
                t.Fatalf("unexpected gas expect %v but %v", expectTx[i].Gas(), tx.Gas())
            }

            if expectTx[i].Hash() != tx.Hash() {
                t.Fatalf("unexpected hash expect %v but %v", expectTx[i].Hash(), tx.Hash())
            }

            if expectTx[i].Nonce() != tx.Nonce() {
                t.Fatalf("unexpected nonce expect %v but %v", expectTx[i].Nonce(), tx.Nonce())
            }

            if expectTx[i].GasPrice().Uint64() != tx.GasPrice().Uint64() {
                t.Fatalf("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.Fatalf("hex to ecdsa error: %v", err)
    }

    dex, err := newTestDexonWithGenesis(key)
    if err != nil {
        t.Fatalf("new test dexon error: %v", err)
    }

    currentBlock := dex.blockchain.CurrentBlock()

    witness, err := dex.app.PrepareWitness(0)
    if err != nil {
        t.Fatalf("prepare witness error: %v", err)
    }

    if witness.Height != currentBlock.NumberU64() {
        t.Fatalf("unexpeted witness height %v", witness.Height)
    }

    var witnessBlockHash common.Hash
    err = rlp.DecodeBytes(witness.Data, &witnessBlockHash)
    if err != nil {
        t.Fatalf("rlp decode error: %v", err)
    }

    if witnessBlockHash != currentBlock.Hash() {
        t.Fatalf("expect root %v but %v", currentBlock.Hash(), witnessBlockHash)
    }

    if _, err := dex.app.PrepareWitness(999); err == nil {
        t.Fatalf("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.Fatalf("hex to ecdsa error: %v", err)
    }

    dex, err := newTestDexonWithGenesis(key)
    if err != nil {
        t.Fatalf("new test dexon error: %v", err)
    }

    chainID := new(big.Int).Mod(crypto.PubkeyToAddress(key.PublicKey).Big(),
        big.NewInt(int64(dex.chainConfig.Dexcon.NumChains)))

    root := dex.blockchain.CurrentBlock().Root()
    dex.app.chainRoot.Store(uint32(chainID.Uint64()), &root)

    // Prepare first confirmed block.
    _, err = prepareConfirmedBlocks(dex, []*ecdsa.PrivateKey{key}, 0)
    if err != nil {
        t.Fatalf("prepare confirmed blocks error: %v", err)
    }

    // Prepare normal block.
    block := &coreTypes.Block{}
    block.Hash = coreCommon.NewRandomHash()
    block.Position.ChainID = uint32(chainID.Uint64())
    block.Position.Height = 1
    block.ProposerID = coreTypes.NodeID{coreCommon.Hash{1, 2, 3}}
    block.Payload, block.Witness, err = prepareDataWithoutTxPool(dex, key, 0, 100)
    if err != nil {
        t.Fatalf("prepare data error: %v", err)
    }

    // Expect ok.
    status := dex.app.VerifyBlock(block)
    if status != coreTypes.VerifyOK {
        t.Fatalf("verify fail: %v", status)
    }

    // Prepare invalid nonce tx.
    block = &coreTypes.Block{}
    block.Hash = coreCommon.NewRandomHash()
    block.Position.ChainID = uint32(chainID.Uint64())
    block.Position.Height = 1
    block.ProposerID = coreTypes.NodeID{coreCommon.Hash{1, 2, 3}}
    block.Payload, block.Witness, err = prepareDataWithoutTxPool(dex, key, 1, 100)
    if err != nil {
        t.Fatalf("prepare data error: %v", err)
    }

    // Expect invalid block.
    status = dex.app.VerifyBlock(block)
    if status != coreTypes.VerifyInvalidBlock {
        t.Fatalf("verify fail: %v", status)
    }

    // Prepare invalid block height.
    block = &coreTypes.Block{}
    block.Hash = coreCommon.NewRandomHash()
    block.Position.ChainID = uint32(chainID.Uint64())
    block.Position.Height = 2
    block.ProposerID = coreTypes.NodeID{coreCommon.Hash{1, 2, 3}}
    block.Payload, block.Witness, err = prepareDataWithoutTxPool(dex, key, 0, 100)
    if err != nil {
        t.Fatalf("prepare data error: %v", err)
    }

    // Expect retry later.
    status = dex.app.VerifyBlock(block)
    if status != coreTypes.VerifyRetryLater {
        t.Fatalf("verify fail expect retry later but get %v", status)
    }

    // Prepare reach block limit.
    block = &coreTypes.Block{}
    block.Hash = coreCommon.NewRandomHash()
    block.Position.ChainID = uint32(chainID.Uint64())
    block.Position.Height = 1
    block.ProposerID = coreTypes.NodeID{coreCommon.Hash{1, 2, 3}}
    block.Payload, block.Witness, err = prepareDataWithoutTxPool(dex, key, 0, 10000)
    if err != nil {
        t.Fatalf("prepare data error: %v", err)
    }

    // Expect invalid block.
    status = dex.app.VerifyBlock(block)
    if status != coreTypes.VerifyInvalidBlock {
        t.Fatalf("verify fail expect invalid block but get %v", status)
    }

    // Prepare insufficient funds.
    block = &coreTypes.Block{}
    block.Hash = coreCommon.NewRandomHash()
    block.Position.ChainID = uint32(chainID.Uint64())
    block.Position.Height = 1
    block.ProposerID = coreTypes.NodeID{coreCommon.Hash{1, 2, 3}}
    _, block.Witness, err = prepareDataWithoutTxPool(dex, key, 0, 0)
    if err != nil {
        t.Fatalf("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.Fatalf("verify fail expect invalid block but get %v", status)
    }

    // Prepare invalid intrinsic gas.
    block = &coreTypes.Block{}
    block.Hash = coreCommon.NewRandomHash()
    block.Position.ChainID = uint32(chainID.Uint64())
    block.Position.Height = 1
    block.ProposerID = coreTypes.NodeID{coreCommon.Hash{1, 2, 3}}
    _, block.Witness, err = prepareDataWithoutTxPool(dex, key, 0, 0)
    if err != nil {
        t.Fatalf("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.Fatalf("verify fail expect invalid block but get %v", status)
    }

    // Prepare invalid transactions with nonce.
    block = &coreTypes.Block{}
    block.Hash = coreCommon.NewRandomHash()
    block.Position.ChainID = uint32(chainID.Uint64())
    block.Position.Height = 1
    block.ProposerID = coreTypes.NodeID{coreCommon.Hash{1, 2, 3}}
    _, block.Witness, err = prepareDataWithoutTxPool(dex, key, 0, 0)
    if err != nil {
        t.Fatalf("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.Fatalf("verify fail expect invalid block but get %v", status)
    }
}

func TestBlockConfirmed(t *testing.T) {
    key, err := crypto.GenerateKey()
    if err != nil {
        t.Fatalf("hex to ecdsa error: %v", err)
    }

    dex, err := newTestDexonWithGenesis(key)
    if err != nil {
        t.Fatalf("new test dexon error: %v", err)
    }

    chainID := new(big.Int).Mod(crypto.PubkeyToAddress(key.PublicKey).Big(),
        big.NewInt(int64(dex.chainConfig.Dexcon.NumChains)))

    root := dex.blockchain.CurrentBlock().Root()
    dex.app.chainRoot.Store(uint32(chainID.Uint64()), &root)

    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.Fatalf("prepare data error: %v", err)
        }
        expectCost.Add(&expectCost, &cost)
        expectNonce = nonce

        block := &coreTypes.Block{}
        block.Hash = coreCommon.NewRandomHash()
        block.Witness = witness
        block.Payload = payload
        block.ProposerID = coreTypes.NodeID{coreCommon.Hash{1, 2, 3}}
        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.Fatalf("expect address counter is %v but %v", expectCounter, info.Counter)
    }

    if info.Cost.Cmp(&expectCost) != 0 {
        t.Fatalf("expect address cost is %v but %v", expectCost.Uint64(), info.Cost.Uint64())
    }

    if info.Nonce != expectNonce {
        t.Fatalf("expect address nonce is %v but %v", expectNonce, info.Nonce)
    }
}

func TestBlockDelivered(t *testing.T) {
    key, err := crypto.GenerateKey()
    if err != nil {
        t.Fatalf("hex to ecdsa error: %v", err)
    }

    dex, err := newTestDexonWithGenesis(key)
    if err != nil {
        t.Fatalf("new test dexon error: %v", err)
    }

    chainID := new(big.Int).Mod(crypto.PubkeyToAddress(key.PublicKey).Big(),
        big.NewInt(int64(dex.chainConfig.Dexcon.NumChains)))

    root := dex.blockchain.CurrentBlock().Root()
    dex.app.chainRoot.Store(uint32(chainID.Uint64()), &root)

    address := crypto.PubkeyToAddress(key.PublicKey)
    firstBlocksInfo, err := prepareConfirmedBlocks(dex, []*ecdsa.PrivateKey{key}, 50)
    if err != nil {
        t.Fatalf("preapare confirmed block error: %v", err)
    }

    dex.app.BlockDelivered(firstBlocksInfo[0].Block.Hash, firstBlocksInfo[0].Block.Position,
        coreTypes.FinalizationResult{
            Timestamp: time.Now(),
            Height:    1,
        })

    currentState, err := dex.blockchain.State()
    if err != nil {
        t.Fatalf("get state error: %v", err)
    }
    currentBlock := dex.blockchain.CurrentBlock()
    if currentBlock.NumberU64() != 1 {
        t.Fatalf("unexpected current block number %v", currentBlock.NumberU64())
    }

    currentNonce := currentState.GetNonce(address)
    if currentNonce != firstBlocksInfo[0].Nonce+1 {
        t.Fatalf("unexpected pending state nonce %v", currentNonce)
    }

    balance := currentState.GetBalance(address)
    if new(big.Int).Add(balance, &firstBlocksInfo[0].Cost).Cmp(big.NewInt(50000000000000000)) != 0 {
        t.Fatalf("unexpected pending state balance %v", balance)
    }
}

func TestNumChainsChange(t *testing.T) {
    key, err := crypto.GenerateKey()
    if err != nil {
        t.Fatalf("hex to ecdsa error: %v", err)
    }

    params.TestnetChainConfig.Dexcon.Owner = crypto.PubkeyToAddress(key.PublicKey)

    dex, err := newTestDexonWithGenesis(key)
    if err != nil {
        t.Fatalf("new test dexon error: %v", err)
    }

    chainID := new(big.Int).Mod(crypto.PubkeyToAddress(key.PublicKey).Big(),
        big.NewInt(int64(dex.chainConfig.Dexcon.NumChains)))

    root := dex.blockchain.CurrentBlock().Root()
    dex.app.chainRoot.Store(uint32(chainID.Uint64()), &root)

    abiObject, err := abi.JSON(strings.NewReader(vm.GovernanceABIJSON))
    if err != nil {
        t.Fatalf("get abi object fail: %v", err)
    }

    // Update config in round 1 and height 1.
    // Config will affect in round 3.
    input, err := abiObject.Pack("updateConfiguration",
        new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e4)),
        big.NewInt(2000),
        big.NewInt(0.1875*1e8),
        big.NewInt(1e18),
        big.NewInt(1e18),
        big.NewInt(9000000),
        big.NewInt(3),
        big.NewInt(500),
        big.NewInt(5000),
        big.NewInt(1),
        big.NewInt(700000),
        big.NewInt(5),
        big.NewInt(5),
        big.NewInt(700000),
        big.NewInt(1000),
        []*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1)})
    if err != nil {
        t.Fatalf("updateConfiguration abiObject pack error: %v", err)
    }

    block, err := prepareConfirmedBlockWithTxAndData(dex, key, [][]byte{input}, 1)
    if err != nil {
        t.Fatalf("prepare block error: %v", err)
    }
    dex.app.BlockDelivered(block.Hash, block.Position,
        coreTypes.FinalizationResult{
            Timestamp: time.Now(),
            Height:    1,
        })

    block, err = prepareConfirmedBlockWithTxAndData(dex, key, [][]byte{input}, 1)
    if err != nil {
        t.Fatalf("prepare block error: %v", err)
    }
    dex.app.BlockDelivered(block.Hash, block.Position,
        coreTypes.FinalizationResult{
            Timestamp: time.Now(),
            Height:    2,
        })

    // Make round 1 height 2 block write into chain.
    block, err = prepareConfirmedBlockWithTxAndData(dex, key, nil, 1)
    if err != nil {
        t.Fatalf("prepare block error: %v", err)
    }
    dex.app.BlockDelivered(block.Hash, block.Position,
        coreTypes.FinalizationResult{
            Timestamp: time.Now(),
            Height:    3,
        })

    // Round 2 will keep prepare tx as usual.
    block, err = prepareConfirmedBlockWithTxAndData(dex, key, [][]byte{{1}}, 2)
    if err != nil {
        t.Fatalf("prepare block error: %v", err)
    }
    if block.Payload == nil {
        t.Fatalf("payload should not be nil")
    }
    dex.app.BlockDelivered(block.Hash, block.Position,
        coreTypes.FinalizationResult{
            Timestamp: time.Now(),
            Height:    4,
        })

    // It will prepare empty payload until witness any block in round 3.
    block, err = prepareConfirmedBlockWithTxAndData(dex, key, [][]byte{{1}}, 3)
    if err != nil {
        t.Fatalf("prepare block error: %v", err)
    }
    if block.Payload != nil {
        t.Fatalf("payload should be nil but %v", block.Payload)
    }

    // Test non-empty payload.
    block.Payload = []byte{1}
    if status := dex.app.VerifyBlock(block); status != coreTypes.VerifyInvalidBlock {
        t.Fatalf("unexpected verify status %v", status)
    }
    block.Payload = nil

    dex.app.BlockDelivered(block.Hash, block.Position,
        coreTypes.FinalizationResult{
            Timestamp: time.Now(),
            Height:    5,
        })

    // Empty block in round 3 has been delivered.
    // Now can prepare payload as usual.
    block, err = prepareConfirmedBlockWithTxAndData(dex, key, [][]byte{{1}}, 3)
    if err != nil {
        t.Fatalf("prepare block error: %v", err)
    }
    if block.Payload == nil {
        t.Fatalf("payload should not be nil")
    }
    dex.app.BlockDelivered(block.Hash, block.Position,
        coreTypes.FinalizationResult{
            Timestamp: time.Now(),
            Height:    6,
        })
}

func BenchmarkBlockDeliveredFlow(b *testing.B) {
    key, err := crypto.GenerateKey()
    if err != nil {
        b.Fatalf("hex to ecdsa error: %v", err)
        return
    }

    dex, err := newTestDexonWithGenesis(key)
    if err != nil {
        b.Fatalf("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.Fatalf("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()

    key, err := crypto.GenerateKey()
    if err != nil {
        panic(err)
    }

    testBankAddress := crypto.PubkeyToAddress(allocKey.PublicKey)
    genesis := core.DefaultTestnetGenesisBlock()
    genesis.Alloc = core.GenesisAlloc{
        testBankAddress: {
            Balance:   big.NewInt(100000000000000000),
            Staked:    big.NewInt(50000000000000000),
            PublicKey: crypto.FromECDSAPub(&key.PublicKey),
        },
    }
    chainConfig, _, err := core.SetupGenesisBlock(db, genesis)
    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.SetGovStateFetcher(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 prepareConfirmedBlockWithTxAndData(dex *Dexon, key *ecdsa.PrivateKey, data [][]byte, round uint64) (
    Block *coreTypes.Block, err error) {
    address := crypto.PubkeyToAddress(key.PublicKey)
    numChains := dex.governance.GetGovStateHelperAtRound(round).Configuration().NumChains
    chainID := new(big.Int).Mod(address.Big(), big.NewInt(int64(numChains)))

    for _, d := range data {
        // Prepare one block for pending.
        nonce := dex.txPool.State().GetNonce(address)
        signer := types.NewEIP155Signer(dex.chainConfig.ChainID)
        tx := types.NewTransaction(uint64(nonce), vm.GovernanceContractAddress, big.NewInt(0), params.TxGas*2,
            big.NewInt(1), d)
        tx, err = types.SignTx(tx, signer, key)
        if err != nil {
            return nil, err
        }

        dex.txPool.AddRemote(tx)
        if err != nil {
            return nil, err
        }
    }
    var (
        payload []byte
        witness coreTypes.Witness
    )

    payload, err = dex.app.PreparePayload(coreTypes.Position{Round: round, ChainID: uint32(chainID.Uint64())})
    if err != nil {
        return
    }

    witness, err = dex.app.PrepareWitness(0)
    if err != nil {
        return nil, err
    }

    block := &coreTypes.Block{}
    block.Hash = coreCommon.NewRandomHash()
    block.Witness = witness
    block.Payload = payload
    block.Position.ChainID = uint32(chainID.Uint64())
    block.Position.Round = round
    block.ProposerID = coreTypes.NodeID{coreCommon.Hash{1, 2, 3}}

    status := dex.app.VerifyBlock(block)
    if status != coreTypes.VerifyOK {
        err = fmt.Errorf("verify fail: %v", status)
        return nil, err
    }

    dex.app.BlockConfirmed(*block)
    return block, nil
}

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.Block{}
        block.Hash = coreCommon.NewRandomHash()
        block.Witness = witness
        block.Payload = payload
        block.Position.ChainID = uint32(chainID.Uint64())
        block.ProposerID = coreTypes.NodeID{coreCommon.Hash{1, 2, 3}}

        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
}