OSDN Git Service

Merge pull request #201 from Bytom/v0.1
[bytom/vapor.git] / wallet / wallet_test.go
index 8f325f3..823eab9 100644 (file)
 package wallet
 
 import (
+       "encoding/json"
        "io/ioutil"
        "os"
+       "reflect"
        "testing"
        "time"
 
-       dbm "github.com/tendermint/tmlibs/db"
        "github.com/vapor/account"
        "github.com/vapor/asset"
        "github.com/vapor/blockchain/pseudohsm"
        "github.com/vapor/blockchain/signers"
        "github.com/vapor/blockchain/txbuilder"
-       "github.com/vapor/common"
        "github.com/vapor/config"
        "github.com/vapor/consensus"
        "github.com/vapor/crypto/ed25519/chainkd"
-       "github.com/vapor/database/leveldb"
+       "github.com/vapor/database"
+       dbm "github.com/vapor/database/leveldb"
+       "github.com/vapor/event"
        "github.com/vapor/protocol"
        "github.com/vapor/protocol/bc"
        "github.com/vapor/protocol/bc/types"
 )
 
-func TestWalletUpdate(t *testing.T) {
-       config.CommonConfig = config.DefaultConfig()
-       consensus.SoloNetParams.Signer = "78673764e0ba91a4c5ba9ec0c8c23c69e3d73bf27970e05e0a977e81e13bde475264d3b177a96646bc0ce517ae7fd63504c183ab6d330dea184331a4cf5912d5"
-       config.CommonConfig.Consensus.SelfVoteSigners = append(config.CommonConfig.Consensus.SelfVoteSigners, "vsm1qkm743xmgnvh84pmjchq2s4tnfpgu9ae2f9slep")
-       config.CommonConfig.Consensus.XPrv = "a8e281b615809046698fb0b0f2804a36d824d48fa443350f10f1b80649d39e5f1e85cf9855548915e36137345910606cbc8e7dd8497c831dce899ee6ac112445"
-       for _, v := range config.CommonConfig.Consensus.SelfVoteSigners {
-               address, err := common.DecodeAddress(v, &consensus.SoloNetParams)
-               if err != nil {
-                       t.Fatal(err)
-               }
-               config.CommonConfig.Consensus.Signers = append(config.CommonConfig.Consensus.Signers, address)
+func TestEncodeDecodeGlobalTxIndex(t *testing.T) {
+       want := &struct {
+               BlockHash bc.Hash
+               Position  uint64
+       }{
+               BlockHash: bc.NewHash([32]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20}),
+               Position:  1,
+       }
+
+       globalTxIdx := calcGlobalTxIndex(&want.BlockHash, want.Position)
+       blockHashGot, positionGot := parseGlobalTxIdx(globalTxIdx)
+       if *blockHashGot != want.BlockHash {
+               t.Errorf("blockHash mismatch. Get: %v. Expect: %v", *blockHashGot, want.BlockHash)
+       }
+
+       if positionGot != want.Position {
+               t.Errorf("position mismatch. Get: %v. Expect: %v", positionGot, want.Position)
+       }
+}
+
+func TestWalletVersion(t *testing.T) {
+       // prepare wallet
+       dirPath, err := ioutil.TempDir(".", "")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(dirPath)
+
+       testDB := dbm.NewDB("testdb", "leveldb", "temp")
+       defer func() {
+               testDB.Close()
+               os.RemoveAll("temp")
+       }()
+
+       dispatcher := event.NewDispatcher()
+       w := mockWallet(testDB, nil, nil, nil, dispatcher, false)
+
+       // legacy status test case
+       type legacyStatusInfo struct {
+               WorkHeight uint64
+               WorkHash   bc.Hash
+               BestHeight uint64
+               BestHash   bc.Hash
        }
-       config.CommonConfig.Consensus.Coinbase = "vsm1qkm743xmgnvh84pmjchq2s4tnfpgu9ae2f9slep"
-       address, err := common.DecodeAddress(config.CommonConfig.Consensus.Coinbase, &consensus.SoloNetParams)
+       rawWallet, err := json.Marshal(legacyStatusInfo{})
        if err != nil {
+               t.Fatal("Marshal legacyStatusInfo")
+       }
+
+       w.DB.Set(walletKey, rawWallet)
+       rawWallet = w.DB.Get(walletKey)
+       if rawWallet == nil {
+               t.Fatal("fail to load wallet StatusInfo")
+       }
+
+       if err := json.Unmarshal(rawWallet, &w.status); err != nil {
+               t.Fatal(err)
+       }
+
+       if err := w.checkWalletInfo(); err != errWalletVersionMismatch {
+               t.Fatal("fail to detect legacy wallet version")
+       }
+
+       // lower wallet version test case
+       lowerVersion := StatusInfo{Version: currentVersion - 1}
+       rawWallet, err = json.Marshal(lowerVersion)
+       if err != nil {
+               t.Fatal("save wallet info")
+       }
+
+       w.DB.Set(walletKey, rawWallet)
+       rawWallet = w.DB.Get(walletKey)
+       if rawWallet == nil {
+               t.Fatal("fail to load wallet StatusInfo")
+       }
+
+       if err := json.Unmarshal(rawWallet, &w.status); err != nil {
                t.Fatal(err)
        }
 
+       if err := w.checkWalletInfo(); err != errWalletVersionMismatch {
+               t.Fatal("fail to detect expired wallet version")
+       }
+}
+
+func TestWalletUpdate(t *testing.T) {
        dirPath, err := ioutil.TempDir(".", "")
        if err != nil {
                t.Fatal(err)
        }
        defer os.RemoveAll(dirPath)
 
+       config.CommonConfig = config.DefaultConfig()
        testDB := dbm.NewDB("testdb", "leveldb", "temp")
-       defer os.RemoveAll("temp")
+       defer func() {
+               testDB.Close()
+               os.RemoveAll("temp")
+       }()
 
-       store := leveldb.NewStore(testDB)
-       txPool := protocol.NewTxPool(store)
+       store := database.NewStore(testDB)
+       dispatcher := event.NewDispatcher()
+       txPool := protocol.NewTxPool(store, dispatcher)
 
-       chain, err := protocol.NewChain(store, txPool)
+       chain, err := protocol.NewChain(store, txPool, dispatcher)
        if err != nil {
                t.Fatal(err)
        }
@@ -81,15 +156,12 @@ func TestWalletUpdate(t *testing.T) {
        controlProg.KeyIndex = 1
 
        reg := asset.NewRegistry(testDB, chain)
-       asset, err := reg.Define([]chainkd.XPub{xpub1.XPub}, 1, nil, "TESTASSET", nil)
-       if err != nil {
-               t.Fatal(err)
-       }
+       asset := bc.AssetID{V0: 5}
 
        utxos := []*account.UTXO{}
        btmUtxo := mockUTXO(controlProg, consensus.BTMAssetID)
        utxos = append(utxos, btmUtxo)
-       OtherUtxo := mockUTXO(controlProg, &asset.AssetID)
+       OtherUtxo := mockUTXO(controlProg, &asset)
        utxos = append(utxos, OtherUtxo)
 
        _, txData, err := mockTxData(utxos, testAccount)
@@ -101,8 +173,10 @@ func TestWalletUpdate(t *testing.T) {
        block := mockSingleBlock(tx)
        txStatus := bc.NewTransactionStatus()
        txStatus.SetStatus(0, false)
+       txStatus.SetStatus(1, false)
        store.SaveBlock(block, txStatus)
-       w := mockWallet(testDB, accountManager, reg, chain, address)
+
+       w := mockWallet(testDB, accountManager, reg, chain, dispatcher, true)
        err = w.AttachBlock(block)
        if err != nil {
                t.Fatal(err)
@@ -116,6 +190,156 @@ func TestWalletUpdate(t *testing.T) {
        if len(wants) != 1 {
                t.Fatal(err)
        }
+
+       if wants[0].ID != tx.ID {
+               t.Fatal("account txID mismatch")
+       }
+
+       for position, tx := range block.Transactions {
+               get := w.DB.Get(calcGlobalTxIndexKey(tx.ID.String()))
+               bh := block.BlockHeader.Hash()
+               expect := calcGlobalTxIndex(&bh, uint64(position))
+               if !reflect.DeepEqual(get, expect) {
+                       t.Fatalf("position#%d: compare retrieved globalTxIdx err", position)
+               }
+       }
+}
+
+func TestRescanWallet(t *testing.T) {
+       // prepare wallet & db
+       dirPath, err := ioutil.TempDir(".", "")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(dirPath)
+
+       config.CommonConfig = config.DefaultConfig()
+       testDB := dbm.NewDB("testdb", "leveldb", "temp")
+       defer func() {
+               testDB.Close()
+               os.RemoveAll("temp")
+       }()
+
+       store := database.NewStore(testDB)
+       dispatcher := event.NewDispatcher()
+       txPool := protocol.NewTxPool(store, dispatcher)
+       chain, err := protocol.NewChain(store, txPool, dispatcher)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       statusInfo := StatusInfo{
+               Version:  currentVersion,
+               WorkHash: bc.Hash{V0: 0xff},
+       }
+       rawWallet, err := json.Marshal(statusInfo)
+       if err != nil {
+               t.Fatal("save wallet info")
+       }
+
+       w := mockWallet(testDB, nil, nil, chain, dispatcher, false)
+       w.DB.Set(walletKey, rawWallet)
+       rawWallet = w.DB.Get(walletKey)
+       if rawWallet == nil {
+               t.Fatal("fail to load wallet StatusInfo")
+       }
+
+       if err := json.Unmarshal(rawWallet, &w.status); err != nil {
+               t.Fatal(err)
+       }
+
+       // rescan wallet
+       if err := w.loadWalletInfo(); err != nil {
+               t.Fatal(err)
+       }
+
+       block := config.GenesisBlock()
+       if w.status.WorkHash != block.Hash() {
+               t.Fatal("reattach from genesis block")
+       }
+}
+
+func TestMemPoolTxQueryLoop(t *testing.T) {
+       dirPath, err := ioutil.TempDir(".", "")
+       if err != nil {
+               t.Fatal(err)
+       }
+       config.CommonConfig = config.DefaultConfig()
+       testDB := dbm.NewDB("testdb", "leveldb", dirPath)
+       defer func() {
+               testDB.Close()
+               os.RemoveAll(dirPath)
+       }()
+
+       store := database.NewStore(testDB)
+       dispatcher := event.NewDispatcher()
+       txPool := protocol.NewTxPool(store, dispatcher)
+
+       chain, err := protocol.NewChain(store, txPool, dispatcher)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       accountManager := account.NewManager(testDB, chain)
+       hsm, err := pseudohsm.New(dirPath)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       xpub1, _, err := hsm.XCreate("test_pub1", "password", "en")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       testAccount, err := accountManager.Create([]chainkd.XPub{xpub1.XPub}, 1, "testAccount", signers.BIP0044)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       controlProg, err := accountManager.CreateAddress(testAccount.ID, false)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       controlProg.KeyIndex = 1
+
+       reg := asset.NewRegistry(testDB, chain)
+       asset := bc.AssetID{V0: 5}
+
+       utxos := []*account.UTXO{}
+       btmUtxo := mockUTXO(controlProg, consensus.BTMAssetID)
+       utxos = append(utxos, btmUtxo)
+       OtherUtxo := mockUTXO(controlProg, &asset)
+       utxos = append(utxos, OtherUtxo)
+
+       _, txData, err := mockTxData(utxos, testAccount)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       tx := types.NewTx(*txData)
+       //block := mockSingleBlock(tx)
+       txStatus := bc.NewTransactionStatus()
+       txStatus.SetStatus(0, false)
+       w, err := NewWallet(testDB, accountManager, reg, hsm, chain, dispatcher, false)
+       go w.memPoolTxQueryLoop()
+       w.eventDispatcher.Post(protocol.TxMsgEvent{TxMsg: &protocol.TxPoolMsg{TxDesc: &protocol.TxDesc{Tx: tx}, MsgType: protocol.MsgNewTx}})
+       time.Sleep(time.Millisecond * 10)
+       if _, err = w.GetUnconfirmedTxByTxID(tx.ID.String()); err != nil {
+               t.Fatal("disaptch new tx msg error:", err)
+       }
+       w.eventDispatcher.Post(protocol.TxMsgEvent{TxMsg: &protocol.TxPoolMsg{TxDesc: &protocol.TxDesc{Tx: tx}, MsgType: protocol.MsgRemoveTx}})
+       time.Sleep(time.Millisecond * 10)
+       txs, err := w.GetUnconfirmedTxs(testAccount.ID)
+       if err != nil {
+               t.Fatal("get unconfirmed tx error:", err)
+       }
+
+       if len(txs) != 0 {
+               t.Fatal("disaptch remove tx msg error")
+       }
+
+       w.eventDispatcher.Post(protocol.TxMsgEvent{TxMsg: &protocol.TxPoolMsg{TxDesc: &protocol.TxDesc{Tx: tx}, MsgType: 2}})
 }
 
 func mockUTXO(controlProg *account.CtrlProgram, assetID *bc.AssetID) *account.UTXO {
@@ -144,9 +368,9 @@ func mockTxData(utxos []*account.UTXO, testAccount *account.Account) (*txbuilder
 
                out := &types.TxOutput{}
                if utxo.AssetID == *consensus.BTMAssetID {
-                       out = types.NewTxOutput(utxo.AssetID, 100, utxo.ControlProgram)
+                       out = types.NewIntraChainOutput(utxo.AssetID, 100, utxo.ControlProgram)
                } else {
-                       out = types.NewTxOutput(utxo.AssetID, utxo.Amount, utxo.ControlProgram)
+                       out = types.NewIntraChainOutput(utxo.AssetID, utxo.Amount, utxo.ControlProgram)
                }
                tplBuilder.AddOutput(out)
        }
@@ -154,15 +378,17 @@ func mockTxData(utxos []*account.UTXO, testAccount *account.Account) (*txbuilder
        return tplBuilder.Build()
 }
 
-func mockWallet(walletDB dbm.DB, account *account.Manager, asset *asset.Registry, chain *protocol.Chain, address common.Address) *Wallet {
+func mockWallet(walletDB dbm.DB, account *account.Manager, asset *asset.Registry, chain *protocol.Chain, dispatcher *event.Dispatcher, txIndexFlag bool) *Wallet {
        wallet := &Wallet{
-               DB:          walletDB,
-               AccountMgr:  account,
-               AssetReg:    asset,
-               chain:       chain,
-               RecoveryMgr: newRecoveryManager(walletDB, account),
-               dposAddress: address,
+               DB:              walletDB,
+               AccountMgr:      account,
+               AssetReg:        asset,
+               chain:           chain,
+               RecoveryMgr:     newRecoveryManager(walletDB, account),
+               eventDispatcher: dispatcher,
+               TxIndexFlag:     txIndexFlag,
        }
+       wallet.txMsgSub, _ = wallet.eventDispatcher.Subscribe(protocol.TxMsgEvent{})
        return wallet
 }
 
@@ -172,6 +398,6 @@ func mockSingleBlock(tx *types.Tx) *types.Block {
                        Version: 1,
                        Height:  1,
                },
-               Transactions: []*types.Tx{tx},
+               Transactions: []*types.Tx{config.GenesisTx(), tx},
        }
 }