OSDN Git Service

e72269f01b8217d70cbb103b9d170a4f683074f7
[bytom/bytom.git] / wallet / wallet_test.go
1 package wallet
2
3 import (
4         "context"
5         "io/ioutil"
6         "os"
7         "testing"
8         "time"
9
10         "github.com/tendermint/go-wire/data/base58"
11         dbm "github.com/tendermint/tmlibs/db"
12
13         "github.com/bytom/account"
14         "github.com/bytom/asset"
15         "github.com/bytom/blockchain/pseudohsm"
16         "github.com/bytom/blockchain/txbuilder"
17         "github.com/bytom/consensus"
18         "github.com/bytom/crypto/ed25519/chainkd"
19         "github.com/bytom/crypto/sha3pool"
20         "github.com/bytom/database/leveldb"
21         "github.com/bytom/protocol"
22         "github.com/bytom/protocol/bc"
23         "github.com/bytom/protocol/bc/types"
24 )
25
26 func TestWalletUpdate(t *testing.T) {
27         dirPath, err := ioutil.TempDir(".", "")
28         if err != nil {
29                 t.Fatal(err)
30         }
31         defer os.RemoveAll(dirPath)
32
33         testDB := dbm.NewDB("testdb", "leveldb", "temp")
34         defer os.RemoveAll("temp")
35
36         store := leveldb.NewStore(testDB)
37         txPool := protocol.NewTxPool()
38
39         chain, err := protocol.NewChain(store, txPool)
40         if err != nil {
41                 t.Fatal(err)
42         }
43
44         accountManager := account.NewManager(testDB, chain)
45         hsm, err := pseudohsm.New(dirPath)
46         if err != nil {
47                 t.Fatal(err)
48         }
49
50         xpub1, err := hsm.XCreate("test_pub1", "password")
51         if err != nil {
52                 t.Fatal(err)
53         }
54
55         testAccount, err := accountManager.Create(nil, []chainkd.XPub{xpub1.XPub}, 1, "testAccount")
56         if err != nil {
57                 t.Fatal(err)
58         }
59
60         controlProg, err := accountManager.CreateAddress(nil, testAccount.ID, false)
61         if err != nil {
62                 t.Fatal(err)
63         }
64
65         controlProg.KeyIndex = 1
66
67         reg := asset.NewRegistry(testDB, chain)
68         asset, err := reg.Define([]chainkd.XPub{xpub1.XPub}, 1, nil, "TESTASSET")
69         if err != nil {
70                 t.Fatal(err)
71         }
72
73         utxos := []*account.UTXO{}
74         btmUtxo := mockUTXO(controlProg, consensus.BTMAssetID)
75         utxos = append(utxos, btmUtxo)
76         OtherUtxo := mockUTXO(controlProg, &asset.AssetID)
77         utxos = append(utxos, OtherUtxo)
78
79         _, txData, err := mockTxData(utxos, testAccount)
80         if err != nil {
81                 t.Fatal(err)
82         }
83
84         tx := types.NewTx(*txData)
85         block := mockSingleBlock(tx)
86         txStatus := bc.NewTransactionStatus()
87         store.SaveBlock(block, txStatus)
88
89         w := mockWallet(testDB, accountManager, reg, chain)
90         err = w.AttachBlock(block)
91         if err != nil {
92                 t.Fatal(err)
93         }
94
95         want, err := w.GetTransactionsByTxID(tx.ID.String())
96         if len(want) != 1 {
97                 t.Fatal(err)
98         }
99
100         wants, err := w.GetTransactionsByTxID("")
101         if len(wants) != 1 {
102                 t.Fatal(err)
103         }
104 }
105
106 func TestExportAndImportPrivKey(t *testing.T) {
107         dirPath, err := ioutil.TempDir(".", "")
108         if err != nil {
109                 t.Fatal(err)
110         }
111         defer os.RemoveAll(dirPath)
112
113         testDB := dbm.NewDB("testdb", "leveldb", "temp")
114         defer os.RemoveAll("temp")
115
116         store := leveldb.NewStore(testDB)
117         txPool := protocol.NewTxPool()
118
119         chain, err := protocol.NewChain(store, txPool)
120         if err != nil {
121                 t.Fatal(err)
122         }
123
124         acntManager := account.NewManager(testDB, chain)
125         reg := asset.NewRegistry(testDB, chain)
126
127         hsm, err := pseudohsm.New(dirPath)
128         if err != nil {
129                 t.Fatal(err)
130         }
131
132         pwd := "password"
133         xpub, err := hsm.XCreate("alias", pwd)
134         if err != nil {
135                 t.Fatal(err)
136         }
137
138         w, err := NewWallet(testDB, acntManager, reg, hsm, chain)
139         if err != nil {
140                 t.Fatal(err)
141         }
142
143         ctx := context.Background()
144         acnt1, err := w.AccountMgr.Create(ctx, []chainkd.XPub{xpub.XPub}, 1, "account-alias")
145         if err != nil {
146                 t.Fatal(err)
147         }
148
149         priv, err := w.ExportAccountPrivKey(xpub.XPub, pwd)
150
151         wantPriv, err := hsm.LoadChainKDKey(xpub.XPub, pwd)
152         if err != nil {
153                 t.Fatal(err)
154         }
155         var hashed [32]byte
156         sha3pool.Sum256(hashed[:], wantPriv[:])
157
158         tmp := append(wantPriv[:], hashed[:4]...)
159         res := base58.Encode(tmp)
160
161         if res != *priv {
162                 t.Fatalf("XPrivs should be identical.\nBefore: %v\n After: %v\n", *priv, res)
163         }
164
165         rawPriv, err := base58.Decode(*priv)
166         if err != nil {
167                 t.Fatal(err)
168         }
169
170         if len(rawPriv) != 68 {
171                 t.Fatal("invalid private key hash length")
172         }
173
174         var xprv [64]byte
175         copy(xprv[:], rawPriv[:64])
176
177         _, err = w.ImportAccountPrivKey(xprv, xpub.Alias, pwd, 0, acnt1.Alias)
178         if err != pseudohsm.ErrDuplicateKeyAlias {
179                 t.Fatal(err)
180         }
181
182         hsm.XDelete(xpub.XPub, pwd)
183
184         _, err = w.ImportAccountPrivKey(xprv, xpub.Alias, pwd, 0, acnt1.Alias)
185         if err != account.ErrDuplicateAlias {
186                 t.Fatal(err)
187         }
188
189         accountInfo := struct {
190                 AccountInfo string `json:"account_info"`
191         }{AccountInfo: acnt1.Alias}
192
193         w.AccountMgr.DeleteAccount(accountInfo)
194
195         acnt2, err := w.ImportAccountPrivKey(xprv, xpub.Alias, pwd, 0, acnt1.Alias)
196         if err != nil {
197                 t.Fatal(err)
198         }
199
200         if acnt2.XPub != acnt1.XPubs[0] {
201                 t.Fatalf("XPubs should be identical.\nBefore: %v\n After: %v\n", acnt1.XPubs[0], acnt2.XPub)
202         }
203 }
204
205 func mockUTXO(controlProg *account.CtrlProgram, assetID *bc.AssetID) *account.UTXO {
206         utxo := &account.UTXO{}
207         utxo.OutputID = bc.Hash{V0: 1}
208         utxo.SourceID = bc.Hash{V0: 2}
209         utxo.AssetID = *assetID
210         utxo.Amount = 1000000000
211         utxo.SourcePos = 0
212         utxo.ControlProgram = controlProg.ControlProgram
213         utxo.AccountID = controlProg.AccountID
214         utxo.Address = controlProg.Address
215         utxo.ControlProgramIndex = controlProg.KeyIndex
216         return utxo
217 }
218
219 func mockTxData(utxos []*account.UTXO, testAccount *account.Account) (*txbuilder.Template, *types.TxData, error) {
220         tplBuilder := txbuilder.NewBuilder(time.Now())
221
222         for _, utxo := range utxos {
223                 txInput, sigInst, err := account.UtxoToInputs(testAccount.Signer, utxo)
224                 if err != nil {
225                         return nil, nil, err
226                 }
227                 tplBuilder.AddInput(txInput, sigInst)
228
229                 out := &types.TxOutput{}
230                 if utxo.AssetID == *consensus.BTMAssetID {
231                         out = types.NewTxOutput(utxo.AssetID, 100, utxo.ControlProgram)
232                 } else {
233                         out = types.NewTxOutput(utxo.AssetID, utxo.Amount, utxo.ControlProgram)
234                 }
235                 tplBuilder.AddOutput(out)
236         }
237
238         return tplBuilder.Build()
239 }
240
241 func mockWallet(walletDB dbm.DB, account *account.Manager, asset *asset.Registry, chain *protocol.Chain) *Wallet {
242         return &Wallet{
243                 DB:             walletDB,
244                 AccountMgr:     account,
245                 AssetReg:       asset,
246                 chain:          chain,
247                 rescanProgress: make(chan struct{}, 1),
248         }
249 }
250
251 func mockSingleBlock(tx *types.Tx) *types.Block {
252         return &types.Block{
253                 BlockHeader: types.BlockHeader{
254                         Version: 1,
255                         Height:  1,
256                         Bits:    2305843009230471167,
257                 },
258                 Transactions: []*types.Tx{tx},
259         }
260 }