OSDN Git Service

5d239b0202ce1029032789e0b2f47c01c90cd168
[bytom/bytom.git] / wallet / wallet_test.go
1 package wallet
2
3 import (
4         "encoding/json"
5         "io/ioutil"
6         "os"
7         "reflect"
8         "testing"
9         "time"
10
11         "github.com/bytom/account"
12         "github.com/bytom/asset"
13         "github.com/bytom/blockchain/pseudohsm"
14         "github.com/bytom/blockchain/signers"
15         "github.com/bytom/blockchain/txbuilder"
16         "github.com/bytom/config"
17         "github.com/bytom/consensus"
18         "github.com/bytom/crypto/ed25519/chainkd"
19         "github.com/bytom/database"
20         dbm "github.com/bytom/database/leveldb"
21         "github.com/bytom/event"
22         "github.com/bytom/protocol"
23         "github.com/bytom/protocol/bc"
24         "github.com/bytom/protocol/bc/types"
25 )
26
27 func TestWalletVersion(t *testing.T) {
28         // prepare wallet
29         dirPath, err := ioutil.TempDir(".", "")
30         if err != nil {
31                 t.Fatal(err)
32         }
33         defer os.RemoveAll(dirPath)
34
35         testDB := dbm.NewDB("testdb", "leveldb", "temp")
36         defer os.RemoveAll("temp")
37
38         dispatcher := event.NewDispatcher()
39         w := mockWallet(testDB, nil, nil, nil, dispatcher)
40
41         // legacy status test case
42         type legacyStatusInfo struct {
43                 WorkHeight uint64
44                 WorkHash   bc.Hash
45                 BestHeight uint64
46                 BestHash   bc.Hash
47         }
48         rawWallet, err := json.Marshal(legacyStatusInfo{})
49         if err != nil {
50                 t.Fatal("Marshal legacyStatusInfo")
51         }
52
53         w.DB.Set(walletKey, rawWallet)
54         rawWallet = w.DB.Get(walletKey)
55         if rawWallet == nil {
56                 t.Fatal("fail to load wallet StatusInfo")
57         }
58
59         if err := json.Unmarshal(rawWallet, &w.status); err != nil {
60                 t.Fatal(err)
61         }
62
63         if err := w.checkWalletInfo(); err != errWalletVersionMismatch {
64                 t.Fatal("fail to detect legacy wallet version")
65         }
66
67         // lower wallet version test case
68         lowerVersion := StatusInfo{Version: currentVersion - 1}
69         rawWallet, err = json.Marshal(lowerVersion)
70         if err != nil {
71                 t.Fatal("save wallet info")
72         }
73
74         w.DB.Set(walletKey, rawWallet)
75         rawWallet = w.DB.Get(walletKey)
76         if rawWallet == nil {
77                 t.Fatal("fail to load wallet StatusInfo")
78         }
79
80         if err := json.Unmarshal(rawWallet, &w.status); err != nil {
81                 t.Fatal(err)
82         }
83
84         if err := w.checkWalletInfo(); err != errWalletVersionMismatch {
85                 t.Fatal("fail to detect expired wallet version")
86         }
87 }
88
89 func TestWalletUpdate(t *testing.T) {
90         dirPath, err := ioutil.TempDir(".", "")
91         if err != nil {
92                 t.Fatal(err)
93         }
94         defer os.RemoveAll(dirPath)
95
96         testDB := dbm.NewDB("testdb", "leveldb", "temp")
97         defer os.RemoveAll("temp")
98
99         store := database.NewStore(testDB)
100         dispatcher := event.NewDispatcher()
101         txPool := protocol.NewTxPool(store, dispatcher)
102
103         chain, err := protocol.NewChain(store, txPool)
104         if err != nil {
105                 t.Fatal(err)
106         }
107
108         accountManager := account.NewManager(testDB, chain)
109         hsm, err := pseudohsm.New(dirPath)
110         if err != nil {
111                 t.Fatal(err)
112         }
113
114         xpub1, _, err := hsm.XCreate("test_pub1", "password", "en")
115         if err != nil {
116                 t.Fatal(err)
117         }
118
119         testAccount, err := accountManager.Create([]chainkd.XPub{xpub1.XPub}, 1, "testAccount", signers.BIP0044)
120         if err != nil {
121                 t.Fatal(err)
122         }
123
124         controlProg, err := accountManager.CreateAddress(testAccount.ID, false)
125         if err != nil {
126                 t.Fatal(err)
127         }
128
129         controlProg.KeyIndex = 1
130
131         reg := asset.NewRegistry(testDB, chain)
132         asset, err := reg.Define([]chainkd.XPub{xpub1.XPub}, 1, nil, 0, "TESTASSET", nil)
133         if err != nil {
134                 t.Fatal(err)
135         }
136
137         utxos := []*account.UTXO{}
138         btmUtxo := mockUTXO(controlProg, consensus.BTMAssetID)
139         utxos = append(utxos, btmUtxo)
140         OtherUtxo := mockUTXO(controlProg, &asset.AssetID)
141         utxos = append(utxos, OtherUtxo)
142
143         _, txData, err := mockTxData(utxos, testAccount)
144         if err != nil {
145                 t.Fatal(err)
146         }
147
148         tx := types.NewTx(*txData)
149         block := mockSingleBlock(tx)
150         txStatus := bc.NewTransactionStatus()
151         txStatus.SetStatus(0, false)
152         txStatus.SetStatus(1, false)
153         store.SaveBlock(block, txStatus)
154
155         w := mockWallet(testDB, accountManager, reg, chain, dispatcher)
156         err = w.AttachBlock(block)
157         if err != nil {
158                 t.Fatal(err)
159         }
160
161         if _, err := w.GetTransactionByTxID(tx.ID.String()); err != nil {
162                 t.Fatal(err)
163         }
164
165         wants, err := w.GetTransactions("")
166         if len(wants) != 1 {
167                 t.Fatal(err)
168         }
169
170         if wants[0].ID != tx.ID {
171                 t.Fatal("account txID mismatch")
172         }
173
174         for position, tx := range block.Transactions {
175                 get := w.DB.Get(calcGlobalTxIndexKey(tx.ID.String()))
176                 bh := block.BlockHeader.Hash()
177                 expect := calcGlobalTxIndex(&bh, position)
178                 if !reflect.DeepEqual(get, expect) {
179                         t.Fatalf("position#%d: compare retrieved globalTxIdx err", position)
180                 }
181         }
182 }
183
184 func TestMemPoolTxQueryLoop(t *testing.T) {
185         dirPath, err := ioutil.TempDir(".", "")
186         if err != nil {
187                 t.Fatal(err)
188         }
189         defer os.RemoveAll(dirPath)
190
191         testDB := dbm.NewDB("testdb", "leveldb", dirPath)
192
193         store := database.NewStore(testDB)
194         dispatcher := event.NewDispatcher()
195         txPool := protocol.NewTxPool(store, dispatcher)
196
197         chain, err := protocol.NewChain(store, txPool)
198         if err != nil {
199                 t.Fatal(err)
200         }
201
202         accountManager := account.NewManager(testDB, chain)
203         hsm, err := pseudohsm.New(dirPath)
204         if err != nil {
205                 t.Fatal(err)
206         }
207
208         xpub1, _, err := hsm.XCreate("test_pub1", "password", "en")
209         if err != nil {
210                 t.Fatal(err)
211         }
212
213         testAccount, err := accountManager.Create([]chainkd.XPub{xpub1.XPub}, 1, "testAccount", signers.BIP0044)
214         if err != nil {
215                 t.Fatal(err)
216         }
217
218         controlProg, err := accountManager.CreateAddress(testAccount.ID, false)
219         if err != nil {
220                 t.Fatal(err)
221         }
222
223         controlProg.KeyIndex = 1
224
225         reg := asset.NewRegistry(testDB, chain)
226         asset, err := reg.Define([]chainkd.XPub{xpub1.XPub}, 1, nil, 0, "TESTASSET", nil)
227         if err != nil {
228                 t.Fatal(err)
229         }
230
231         utxos := []*account.UTXO{}
232         btmUtxo := mockUTXO(controlProg, consensus.BTMAssetID)
233         utxos = append(utxos, btmUtxo)
234         OtherUtxo := mockUTXO(controlProg, &asset.AssetID)
235         utxos = append(utxos, OtherUtxo)
236
237         _, txData, err := mockTxData(utxos, testAccount)
238         if err != nil {
239                 t.Fatal(err)
240         }
241
242         tx := types.NewTx(*txData)
243         //block := mockSingleBlock(tx)
244         txStatus := bc.NewTransactionStatus()
245         txStatus.SetStatus(0, false)
246         w, err := NewWallet(testDB, accountManager, reg, hsm, chain, dispatcher)
247         go w.memPoolTxQueryLoop()
248         w.eventDispatcher.Post(protocol.TxMsgEvent{TxMsg: &protocol.TxPoolMsg{TxDesc: &protocol.TxDesc{Tx: tx}, MsgType: protocol.MsgNewTx}})
249         time.Sleep(time.Millisecond * 10)
250         if _, err = w.GetUnconfirmedTxByTxID(tx.ID.String()); err != nil {
251                 t.Fatal("disaptch new tx msg error:", err)
252         }
253         w.eventDispatcher.Post(protocol.TxMsgEvent{TxMsg: &protocol.TxPoolMsg{TxDesc: &protocol.TxDesc{Tx: tx}, MsgType: protocol.MsgRemoveTx}})
254         time.Sleep(time.Millisecond * 10)
255         txs, err := w.GetUnconfirmedTxs(testAccount.ID)
256         if err != nil {
257                 t.Fatal("get unconfirmed tx error:", err)
258         }
259
260         if len(txs) != 0 {
261                 t.Fatal("disaptch remove tx msg error")
262         }
263
264         w.eventDispatcher.Post(protocol.TxMsgEvent{TxMsg: &protocol.TxPoolMsg{TxDesc: &protocol.TxDesc{Tx: tx}, MsgType: 2}})
265 }
266
267 func mockUTXO(controlProg *account.CtrlProgram, assetID *bc.AssetID) *account.UTXO {
268         utxo := &account.UTXO{}
269         utxo.OutputID = bc.Hash{V0: 1}
270         utxo.SourceID = bc.Hash{V0: 2}
271         utxo.AssetID = *assetID
272         utxo.Amount = 1000000000
273         utxo.SourcePos = 0
274         utxo.ControlProgram = controlProg.ControlProgram
275         utxo.AccountID = controlProg.AccountID
276         utxo.Address = controlProg.Address
277         utxo.ControlProgramIndex = controlProg.KeyIndex
278         return utxo
279 }
280
281 func mockTxData(utxos []*account.UTXO, testAccount *account.Account) (*txbuilder.Template, *types.TxData, error) {
282         tplBuilder := txbuilder.NewBuilder(time.Now())
283
284         for _, utxo := range utxos {
285                 txInput, sigInst, err := account.UtxoToInputs(testAccount.Signer, utxo)
286                 if err != nil {
287                         return nil, nil, err
288                 }
289                 tplBuilder.AddInput(txInput, sigInst)
290
291                 out := &types.TxOutput{}
292                 if utxo.AssetID == *consensus.BTMAssetID {
293                         out = types.NewTxOutput(utxo.AssetID, 100, utxo.ControlProgram)
294                 } else {
295                         out = types.NewTxOutput(utxo.AssetID, utxo.Amount, utxo.ControlProgram)
296                 }
297                 tplBuilder.AddOutput(out)
298         }
299
300         return tplBuilder.Build()
301 }
302
303 func mockWallet(walletDB dbm.DB, account *account.Manager, asset *asset.Registry, chain *protocol.Chain, dispatcher *event.Dispatcher) *Wallet {
304         wallet := &Wallet{
305                 DB:              walletDB,
306                 AccountMgr:      account,
307                 AssetReg:        asset,
308                 chain:           chain,
309                 RecoveryMgr:     newRecoveryManager(walletDB, account),
310                 eventDispatcher: dispatcher,
311         }
312         wallet.txMsgSub, _ = wallet.eventDispatcher.Subscribe(protocol.TxMsgEvent{})
313         return wallet
314 }
315
316 func mockSingleBlock(tx *types.Tx) *types.Block {
317         return &types.Block{
318                 BlockHeader: types.BlockHeader{
319                         Version: 1,
320                         Height:  1,
321                         Bits:    2305843009230471167,
322                 },
323                 Transactions: []*types.Tx{config.GenesisTx(), tx},
324         }
325 }