github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/wallet/wallet_test.go (about) 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/bytom/account" 12 "github.com/bytom/bytom/asset" 13 "github.com/bytom/bytom/blockchain/pseudohsm" 14 "github.com/bytom/bytom/blockchain/signers" 15 "github.com/bytom/bytom/blockchain/txbuilder" 16 "github.com/bytom/bytom/config" 17 "github.com/bytom/bytom/consensus" 18 "github.com/bytom/bytom/contract" 19 "github.com/bytom/bytom/crypto/ed25519/chainkd" 20 "github.com/bytom/bytom/database" 21 dbm "github.com/bytom/bytom/database/leveldb" 22 "github.com/bytom/bytom/event" 23 "github.com/bytom/bytom/protocol" 24 "github.com/bytom/bytom/protocol/bc" 25 "github.com/bytom/bytom/protocol/bc/types" 26 ) 27 28 func TestEncodeDecodeGlobalTxIndex(t *testing.T) { 29 want := &struct { 30 BlockHash bc.Hash 31 Position uint64 32 }{ 33 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}), 34 Position: 1, 35 } 36 37 globalTxIdx := calcGlobalTxIndex(&want.BlockHash, want.Position) 38 blockHashGot, positionGot := parseGlobalTxIdx(globalTxIdx) 39 if *blockHashGot != want.BlockHash { 40 t.Errorf("blockHash mismatch. Get: %v. Expect: %v", *blockHashGot, want.BlockHash) 41 } 42 43 if positionGot != want.Position { 44 t.Errorf("position mismatch. Get: %v. Expect: %v", positionGot, want.Position) 45 } 46 } 47 48 func TestWalletVersion(t *testing.T) { 49 // prepare wallet 50 dirPath, err := ioutil.TempDir(".", "") 51 if err != nil { 52 t.Fatal(err) 53 } 54 defer os.RemoveAll(dirPath) 55 56 testDB := dbm.NewDB("testdb", "leveldb", "temp") 57 defer os.RemoveAll("temp") 58 59 dispatcher := event.NewDispatcher() 60 w := mockWallet(testDB, nil, nil, nil, dispatcher, false) 61 62 // legacy status test case 63 type legacyStatusInfo struct { 64 WorkHeight uint64 65 WorkHash bc.Hash 66 BestHeight uint64 67 BestHash bc.Hash 68 } 69 rawWallet, err := json.Marshal(legacyStatusInfo{}) 70 if err != nil { 71 t.Fatal("Marshal legacyStatusInfo") 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 legacy wallet version") 86 } 87 88 // lower wallet version test case 89 lowerVersion := StatusInfo{Version: currentVersion - 1} 90 rawWallet, err = json.Marshal(lowerVersion) 91 if err != nil { 92 t.Fatal("save wallet info") 93 } 94 95 w.DB.Set(walletKey, rawWallet) 96 rawWallet = w.DB.Get(walletKey) 97 if rawWallet == nil { 98 t.Fatal("fail to load wallet StatusInfo") 99 } 100 101 if err := json.Unmarshal(rawWallet, &w.status); err != nil { 102 t.Fatal(err) 103 } 104 105 if err := w.checkWalletInfo(); err != errWalletVersionMismatch { 106 t.Fatal("fail to detect expired wallet version") 107 } 108 } 109 110 func TestWalletUpdate(t *testing.T) { 111 dirPath, err := ioutil.TempDir(".", "") 112 if err != nil { 113 t.Fatal(err) 114 } 115 defer os.RemoveAll(dirPath) 116 117 testDB := dbm.NewDB("testdb", "leveldb", "temp") 118 defer os.RemoveAll("temp") 119 120 store := database.NewStore(testDB) 121 dispatcher := event.NewDispatcher() 122 txPool := protocol.NewTxPool(store, dispatcher) 123 124 chain, err := protocol.NewChain(store, txPool, dispatcher) 125 if err != nil { 126 t.Fatal(err) 127 } 128 129 accountManager := account.NewManager(testDB, chain) 130 hsm, err := pseudohsm.New(dirPath) 131 if err != nil { 132 t.Fatal(err) 133 } 134 135 xpub1, _, err := hsm.XCreate("test_pub1", "password", "en") 136 if err != nil { 137 t.Fatal(err) 138 } 139 140 testAccount, err := accountManager.Create([]chainkd.XPub{xpub1.XPub}, 1, "testAccount", signers.BIP0044) 141 if err != nil { 142 t.Fatal(err) 143 } 144 145 controlProg, err := accountManager.CreateAddress(testAccount.ID, false) 146 if err != nil { 147 t.Fatal(err) 148 } 149 150 controlProg.KeyIndex = 1 151 152 reg := asset.NewRegistry(testDB, chain) 153 asset, err := reg.Define([]chainkd.XPub{xpub1.XPub}, 1, nil, 0, "TESTASSET", nil) 154 if err != nil { 155 t.Fatal(err) 156 } 157 158 utxos := []*account.UTXO{} 159 btmUtxo := mockUTXO(controlProg, consensus.BTMAssetID) 160 utxos = append(utxos, btmUtxo) 161 OtherUtxo := mockUTXO(controlProg, &asset.AssetID) 162 utxos = append(utxos, OtherUtxo) 163 164 _, txData, err := mockTxData(utxos, testAccount) 165 if err != nil { 166 t.Fatal(err) 167 } 168 169 tx := types.NewTx(*txData) 170 block := mockSingleBlock(tx) 171 store.SaveBlock(block) 172 173 w := mockWallet(testDB, accountManager, reg, chain, dispatcher, true) 174 err = w.AttachBlock(block) 175 if err != nil { 176 t.Fatal(err) 177 } 178 179 if _, err := w.GetTransactionByTxID(tx.ID.String()); err != nil { 180 t.Fatal(err) 181 } 182 183 wants, err := w.GetTransactions("") 184 if len(wants) != 1 { 185 t.Fatal(err) 186 } 187 188 if wants[0].ID != tx.ID { 189 t.Fatal("account txID mismatch") 190 } 191 192 for position, tx := range block.Transactions { 193 get := w.DB.Get(calcGlobalTxIndexKey(tx.ID.String())) 194 bh := block.BlockHeader.Hash() 195 expect := calcGlobalTxIndex(&bh, uint64(position)) 196 if !reflect.DeepEqual(get, expect) { 197 t.Fatalf("position#%d: compare retrieved globalTxIdx err", position) 198 } 199 } 200 } 201 202 func TestRescanWallet(t *testing.T) { 203 // prepare wallet & db 204 dirPath, err := ioutil.TempDir(".", "") 205 if err != nil { 206 t.Fatal(err) 207 } 208 defer os.RemoveAll(dirPath) 209 210 testDB := dbm.NewDB("testdb", "leveldb", "temp") 211 defer os.RemoveAll("temp") 212 213 store := database.NewStore(testDB) 214 dispatcher := event.NewDispatcher() 215 txPool := protocol.NewTxPool(store, dispatcher) 216 chain, err := protocol.NewChain(store, txPool, dispatcher) 217 if err != nil { 218 t.Fatal(err) 219 } 220 221 statusInfo := StatusInfo{ 222 Version: currentVersion, 223 WorkHash: bc.Hash{V0: 0xff}, 224 } 225 rawWallet, err := json.Marshal(statusInfo) 226 if err != nil { 227 t.Fatal("save wallet info") 228 } 229 230 w := mockWallet(testDB, nil, nil, chain, dispatcher, false) 231 w.DB.Set(walletKey, rawWallet) 232 rawWallet = w.DB.Get(walletKey) 233 if rawWallet == nil { 234 t.Fatal("fail to load wallet StatusInfo") 235 } 236 237 if err := json.Unmarshal(rawWallet, &w.status); err != nil { 238 t.Fatal(err) 239 } 240 241 // rescan wallet 242 if err := w.loadWalletInfo(); err != nil { 243 t.Fatal(err) 244 } 245 246 block := config.GenesisBlock() 247 if w.status.WorkHash != block.Hash() { 248 t.Fatal("reattach from genesis block") 249 } 250 } 251 252 func TestMemPoolTxQueryLoop(t *testing.T) { 253 dirPath, err := ioutil.TempDir(".", "") 254 if err != nil { 255 t.Fatal(err) 256 } 257 defer os.RemoveAll(dirPath) 258 259 testDB := dbm.NewDB("testdb", "leveldb", dirPath) 260 261 store := database.NewStore(testDB) 262 dispatcher := event.NewDispatcher() 263 txPool := protocol.NewTxPool(store, dispatcher) 264 265 chain, err := protocol.NewChain(store, txPool, dispatcher) 266 if err != nil { 267 t.Fatal(err) 268 } 269 270 accountManager := account.NewManager(testDB, chain) 271 hsm, err := pseudohsm.New(dirPath) 272 if err != nil { 273 t.Fatal(err) 274 } 275 276 xpub1, _, err := hsm.XCreate("test_pub1", "password", "en") 277 if err != nil { 278 t.Fatal(err) 279 } 280 281 testAccount, err := accountManager.Create([]chainkd.XPub{xpub1.XPub}, 1, "testAccount", signers.BIP0044) 282 if err != nil { 283 t.Fatal(err) 284 } 285 286 controlProg, err := accountManager.CreateAddress(testAccount.ID, false) 287 if err != nil { 288 t.Fatal(err) 289 } 290 291 controlProg.KeyIndex = 1 292 293 reg := asset.NewRegistry(testDB, chain) 294 contractReg := contract.NewRegistry(testDB) 295 asset, err := reg.Define([]chainkd.XPub{xpub1.XPub}, 1, nil, 0, "TESTASSET", nil) 296 if err != nil { 297 t.Fatal(err) 298 } 299 300 utxos := []*account.UTXO{} 301 btmUtxo := mockUTXO(controlProg, consensus.BTMAssetID) 302 utxos = append(utxos, btmUtxo) 303 OtherUtxo := mockUTXO(controlProg, &asset.AssetID) 304 utxos = append(utxos, OtherUtxo) 305 306 _, txData, err := mockTxData(utxos, testAccount) 307 if err != nil { 308 t.Fatal(err) 309 } 310 311 tx := types.NewTx(*txData) 312 //block := mockSingleBlock(tx) 313 w, err := NewWallet(testDB, accountManager, reg, contractReg, hsm, chain, dispatcher, false) 314 go w.memPoolTxQueryLoop() 315 w.eventDispatcher.Post(protocol.TxMsgEvent{TxMsg: &protocol.TxPoolMsg{TxDesc: &protocol.TxDesc{Tx: tx}, MsgType: protocol.MsgNewTx}}) 316 time.Sleep(time.Millisecond * 10) 317 if _, err = w.GetUnconfirmedTxByTxID(tx.ID.String()); err != nil { 318 t.Fatal("disaptch new tx msg error:", err) 319 } 320 w.eventDispatcher.Post(protocol.TxMsgEvent{TxMsg: &protocol.TxPoolMsg{TxDesc: &protocol.TxDesc{Tx: tx}, MsgType: protocol.MsgRemoveTx}}) 321 time.Sleep(time.Millisecond * 10) 322 txs, err := w.GetUnconfirmedTxs(testAccount.ID) 323 if err != nil { 324 t.Fatal("get unconfirmed tx error:", err) 325 } 326 327 if len(txs) != 0 { 328 t.Fatal("disaptch remove tx msg error") 329 } 330 331 w.eventDispatcher.Post(protocol.TxMsgEvent{TxMsg: &protocol.TxPoolMsg{TxDesc: &protocol.TxDesc{Tx: tx}, MsgType: 2}}) 332 } 333 334 func mockUTXO(controlProg *account.CtrlProgram, assetID *bc.AssetID) *account.UTXO { 335 utxo := &account.UTXO{} 336 utxo.OutputID = bc.Hash{V0: 1} 337 utxo.SourceID = bc.Hash{V0: 2} 338 utxo.AssetID = *assetID 339 utxo.Amount = 1000000000 340 utxo.SourcePos = 0 341 utxo.ControlProgram = controlProg.ControlProgram 342 utxo.AccountID = controlProg.AccountID 343 utxo.Address = controlProg.Address 344 utxo.ControlProgramIndex = controlProg.KeyIndex 345 return utxo 346 } 347 348 func mockTxData(utxos []*account.UTXO, testAccount *account.Account) (*txbuilder.Template, *types.TxData, error) { 349 tplBuilder := txbuilder.NewBuilder(time.Now()) 350 351 for _, utxo := range utxos { 352 txInput, sigInst, err := account.UtxoToInputs(testAccount.Signer, utxo) 353 if err != nil { 354 return nil, nil, err 355 } 356 tplBuilder.AddInput(txInput, sigInst) 357 358 out := &types.TxOutput{} 359 if utxo.AssetID == *consensus.BTMAssetID { 360 out = types.NewOriginalTxOutput(utxo.AssetID, 100, utxo.ControlProgram, nil) 361 } else { 362 out = types.NewOriginalTxOutput(utxo.AssetID, utxo.Amount, utxo.ControlProgram, nil) 363 } 364 tplBuilder.AddOutput(out) 365 } 366 367 return tplBuilder.Build() 368 } 369 370 func mockWallet(walletDB dbm.DB, account *account.Manager, asset *asset.Registry, chain *protocol.Chain, dispatcher *event.Dispatcher, txIndexFlag bool) *Wallet { 371 wallet := &Wallet{ 372 DB: walletDB, 373 AccountMgr: account, 374 AssetReg: asset, 375 chain: chain, 376 RecoveryMgr: newRecoveryManager(walletDB, account), 377 eventDispatcher: dispatcher, 378 TxIndexFlag: txIndexFlag, 379 } 380 wallet.txMsgSub, _ = wallet.eventDispatcher.Subscribe(protocol.TxMsgEvent{}) 381 return wallet 382 } 383 384 func mockSingleBlock(tx *types.Tx) *types.Block { 385 return &types.Block{ 386 BlockHeader: types.BlockHeader{ 387 Version: 1, 388 Height: 1, 389 }, 390 Transactions: append(config.GenesisTxs(), tx), 391 } 392 }