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)
}
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)
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)
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 {
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)
}
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
}
Version: 1,
Height: 1,
},
- Transactions: []*types.Tx{tx},
+ Transactions: []*types.Tx{config.GenesisTx(), tx},
}
}