9 "github.com/vapor/wallet"
11 acc "github.com/vapor/account"
12 "github.com/vapor/asset"
13 "github.com/vapor/blockchain/query"
14 "github.com/vapor/blockchain/signers"
15 "github.com/vapor/crypto/ed25519/chainkd"
16 "github.com/vapor/crypto/sha3pool"
17 dbm "github.com/vapor/database/leveldb"
18 "github.com/vapor/errors"
19 "github.com/vapor/protocol/bc"
24 // ErrFindAccount = errors.New("Failed to find account")
25 errAccntTxIDNotFound = errors.New("account TXID not found")
26 errGetAsset = errors.New("Failed to find asset definition")
29 func accountIndexKey(xpubs []chainkd.XPub) []byte {
32 cpy := append([]chainkd.XPub{}, xpubs[:]...)
33 sort.Sort(signers.SortKeys(cpy))
34 for _, xpub := range cpy {
35 xPubs = append(xPubs, xpub[:]...)
37 sha3pool.Sum256(hash[:], xPubs)
38 return append([]byte(dbm.AccountIndexPrefix), hash[:]...)
41 func Bip44ContractIndexKey(accountID string, change bool) []byte {
42 key := append([]byte(dbm.ContractIndexPrefix), accountID...)
44 return append(key, []byte{1}...)
46 return append(key, []byte{0}...)
49 // ContractKey account control promgram store prefix
50 func ContractKey(hash bc.Hash) []byte {
51 return append([]byte(dbm.ContractPrefix), hash.Bytes()...)
54 // AccountIDKey account id store prefix
55 func AccountIDKey(accountID string) []byte {
56 return append([]byte(dbm.AccountPrefix), []byte(accountID)...)
59 // StandardUTXOKey makes an account unspent outputs key to store
60 func StandardUTXOKey(id bc.Hash) []byte {
61 return append(dbm.UTXOPrefix, id.Bytes()...)
64 // ContractUTXOKey makes a smart contract unspent outputs key to store
65 func ContractUTXOKey(id bc.Hash) []byte {
66 return append(dbm.SUTXOPrefix, id.Bytes()...)
69 func calcDeleteKey(blockHeight uint64) []byte {
70 return []byte(fmt.Sprintf("%s%016x", dbm.TxPrefix, blockHeight))
73 func calcTxIndexKey(txID string) []byte {
74 return append(dbm.TxIndexPrefix, []byte(txID)...)
77 func calcAnnotatedKey(formatKey string) []byte {
78 return append(dbm.TxPrefix, []byte(formatKey)...)
81 func calcUnconfirmedTxKey(formatKey string) []byte {
82 return append(dbm.UnconfirmedTxPrefix, []byte(formatKey)...)
85 func CalcGlobalTxIndexKey(txID string) []byte {
86 return append(dbm.GlobalTxIndexPrefix, []byte(txID)...)
89 func CalcGlobalTxIndex(blockHash *bc.Hash, position uint64) []byte {
90 txIdx := make([]byte, 40)
91 copy(txIdx[:32], blockHash.Bytes())
92 binary.BigEndian.PutUint64(txIdx[32:], position)
96 func formatKey(blockHeight uint64, position uint32) string {
97 return fmt.Sprintf("%016x%08x", blockHeight, position)
100 func contractIndexKey(accountID string) []byte {
101 return append([]byte(dbm.ContractIndexPrefix), []byte(accountID)...)
104 func accountAliasKey(name string) []byte {
105 return append([]byte(dbm.AccountAliasPrefix), []byte(name)...)
108 // WalletStore store wallet using leveldb
109 type WalletStore struct {
114 // NewWalletStore create new WalletStore struct
115 func NewWalletStore(db dbm.DB) *WalletStore {
122 // InitBatch initial batch
123 func (store *WalletStore) InitBatch() error {
124 if store.batch != nil {
125 return errors.New("WalletStore initail fail, store batch is not nil.")
127 store.batch = store.walletDB.NewBatch()
131 // CommitBatch commit batch
132 func (store *WalletStore) CommitBatch() error {
133 if store.batch == nil {
134 return errors.New("WalletStore commit fail, store batch is nil.")
141 // DeleteContractUTXO delete contract utxo by outputID
142 func (store *WalletStore) DeleteContractUTXO(outputID bc.Hash) {
143 if store.batch == nil {
144 store.walletDB.Delete(ContractUTXOKey(outputID))
146 store.batch.Delete(ContractUTXOKey(outputID))
150 // DeleteRecoveryStatus delete recovery status
151 func (store *WalletStore) DeleteRecoveryStatus() {
152 if store.batch == nil {
153 store.walletDB.Delete(dbm.RecoveryKey)
155 store.batch.Delete(dbm.RecoveryKey)
159 // DeleteTransactions delete transactions when orphan block rollback
160 func (store *WalletStore) DeleteTransactions(height uint64) {
161 batch := store.walletDB.NewBatch()
162 if store.batch != nil {
165 txIter := store.walletDB.IteratorPrefix(calcDeleteKey(height))
166 defer txIter.Release()
168 tmpTx := query.AnnotatedTx{}
170 if err := json.Unmarshal(txIter.Value(), &tmpTx); err == nil {
171 batch.Delete(calcTxIndexKey(tmpTx.ID.String()))
173 batch.Delete(txIter.Key())
175 if store.batch == nil {
180 // DeleteUnconfirmedTransaction delete unconfirmed tx by txID
181 func (store *WalletStore) DeleteUnconfirmedTransaction(txID string) {
182 if store.batch == nil {
183 store.walletDB.Delete(calcUnconfirmedTxKey(txID))
185 store.batch.Delete(calcUnconfirmedTxKey(txID))
189 // DeleteWalletTransactions delete all txs in wallet
190 func (store *WalletStore) DeleteWalletTransactions() {
191 batch := store.walletDB.NewBatch()
192 if store.batch != nil {
195 txIter := store.walletDB.IteratorPrefix([]byte(dbm.TxPrefix))
196 defer txIter.Release()
199 batch.Delete(txIter.Key())
202 txIndexIter := store.walletDB.IteratorPrefix([]byte(dbm.TxIndexPrefix))
203 defer txIndexIter.Release()
205 for txIndexIter.Next() {
206 batch.Delete(txIndexIter.Key())
208 if store.batch == nil {
213 // DeleteWalletUTXOs delete all txs in wallet
214 func (store *WalletStore) DeleteWalletUTXOs() {
215 batch := store.walletDB.NewBatch()
216 if store.batch != nil {
219 ruIter := store.walletDB.IteratorPrefix([]byte(dbm.UTXOPrefix))
220 defer ruIter.Release()
222 batch.Delete(ruIter.Key())
225 suIter := store.walletDB.IteratorPrefix([]byte(dbm.SUTXOPrefix))
226 defer suIter.Release()
228 batch.Delete(suIter.Key())
230 if store.batch == nil {
235 // GetAsset get asset by assetID
236 func (store *WalletStore) GetAsset(assetID *bc.AssetID) (*asset.Asset, error) {
237 definitionByte := store.walletDB.Get(asset.ExtAssetKey(assetID))
238 if definitionByte == nil {
239 return nil, errGetAsset
241 definitionMap := make(map[string]interface{})
242 if err := json.Unmarshal(definitionByte, &definitionMap); err != nil {
245 alias := assetID.String()
246 externalAsset := &asset.Asset{
249 DefinitionMap: definitionMap,
250 RawDefinitionByte: definitionByte,
252 return externalAsset, nil
255 // GetControlProgram get raw program by hash
256 func (store *WalletStore) GetControlProgram(hash bc.Hash) (*acc.CtrlProgram, error) {
257 rawProgram := store.walletDB.Get(ContractKey(hash))
258 if rawProgram == nil {
259 return nil, acc.ErrFindCtrlProgram
261 accountCP := new(acc.CtrlProgram)
262 if err := json.Unmarshal(rawProgram, &accountCP); err != nil {
265 return accountCP, nil
268 // GetGlobalTransactionIndex get global tx by txID
269 func (store *WalletStore) GetGlobalTransactionIndex(txID string) []byte {
270 return store.walletDB.Get(CalcGlobalTxIndexKey(txID))
273 // GetStandardUTXO get standard utxo by id
274 func (store *WalletStore) GetStandardUTXO(outid bc.Hash) (*acc.UTXO, error) {
275 rawUTXO := store.walletDB.Get(StandardUTXOKey(outid))
277 return nil, fmt.Errorf("failed get standard UTXO, outputID: %s ", outid.String())
279 UTXO := new(acc.UTXO)
280 if err := json.Unmarshal(rawUTXO, UTXO); err != nil {
286 // GetTransaction get tx by txid
287 func (store *WalletStore) GetTransaction(txID string) (*query.AnnotatedTx, error) {
288 formatKey := store.walletDB.Get(calcTxIndexKey(txID))
289 if formatKey == nil {
290 return nil, errAccntTxIDNotFound
292 rawTx := store.walletDB.Get(calcAnnotatedKey(string(formatKey)))
293 tx := new(query.AnnotatedTx)
294 if err := json.Unmarshal(rawTx, tx); err != nil {
300 // GetUnconfirmedTransaction get unconfirmed tx by txID
301 func (store *WalletStore) GetUnconfirmedTransaction(txID string) (*query.AnnotatedTx, error) {
302 rawUnconfirmedTx := store.walletDB.Get(calcUnconfirmedTxKey(txID))
303 if rawUnconfirmedTx == nil {
304 return nil, fmt.Errorf("failed get unconfirmed tx, txID: %s ", txID)
306 tx := new(query.AnnotatedTx)
307 if err := json.Unmarshal(rawUnconfirmedTx, tx); err != nil {
313 // GetRecoveryStatus delete recovery status
314 func (store *WalletStore) GetRecoveryStatus() (*wallet.RecoveryState, error) {
315 rawStatus := store.walletDB.Get(dbm.RecoveryKey)
316 if rawStatus == nil {
317 return nil, wallet.ErrGetRecoveryStatus
319 state := new(wallet.RecoveryState)
320 if err := json.Unmarshal(rawStatus, state); err != nil {
326 // GetWalletInfo get wallet information
327 func (store *WalletStore) GetWalletInfo() (*wallet.StatusInfo, error) {
328 rawStatus := store.walletDB.Get([]byte(dbm.WalletKey))
329 if rawStatus == nil {
330 return nil, wallet.ErrGetWalletStatusInfo
332 status := new(wallet.StatusInfo)
333 if err := json.Unmarshal(rawStatus, status); err != nil {
339 // ListAccountUTXOs get all account unspent outputs
340 func (store *WalletStore) ListAccountUTXOs(key string) ([]*acc.UTXO, error) {
341 accountUtxoIter := store.walletDB.IteratorPrefix([]byte(key))
342 defer accountUtxoIter.Release()
344 confirmedUTXOs := []*acc.UTXO{}
345 for accountUtxoIter.Next() {
346 utxo := new(acc.UTXO)
347 if err := json.Unmarshal(accountUtxoIter.Value(), utxo); err != nil {
350 confirmedUTXOs = append(confirmedUTXOs, utxo)
352 return confirmedUTXOs, nil
355 func (store *WalletStore) ListTransactions(accountID string, StartTxID string, count uint, unconfirmed bool) ([]*query.AnnotatedTx, error) {
356 annotatedTxs := []*query.AnnotatedTx{}
358 preFix := dbm.TxPrefix
362 startKey = calcUnconfirmedTxKey(StartTxID)
364 formatKey := store.walletDB.Get(calcTxIndexKey(StartTxID))
365 if formatKey == nil {
366 return nil, errAccntTxIDNotFound
368 startKey = calcAnnotatedKey(string(formatKey))
373 preFix = dbm.UnconfirmedTxPrefix
376 itr := store.walletDB.IteratorPrefixWithStart([]byte(preFix), startKey, true)
379 for txNum := count; itr.Next() && txNum > 0; txNum-- {
380 annotatedTx := new(query.AnnotatedTx)
381 if err := json.Unmarshal(itr.Value(), &annotatedTx); err != nil {
384 annotatedTxs = append(annotatedTxs, annotatedTx)
387 return annotatedTxs, nil
390 // ListUnconfirmedTransactions get all unconfirmed txs
391 func (store *WalletStore) ListUnconfirmedTransactions() ([]*query.AnnotatedTx, error) {
392 annotatedTxs := []*query.AnnotatedTx{}
393 txIter := store.walletDB.IteratorPrefix([]byte(dbm.UnconfirmedTxPrefix))
394 defer txIter.Release()
397 annotatedTx := &query.AnnotatedTx{}
398 if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
401 annotatedTxs = append(annotatedTxs, annotatedTx)
403 return annotatedTxs, nil
406 // SetAssetDefinition set assetID and definition
407 func (store *WalletStore) SetAssetDefinition(assetID *bc.AssetID, definition []byte) {
408 if store.batch == nil {
409 store.walletDB.Set(asset.ExtAssetKey(assetID), definition)
411 store.batch.Set(asset.ExtAssetKey(assetID), definition)
415 // SetContractUTXO set standard utxo
416 func (store *WalletStore) SetContractUTXO(outputID bc.Hash, utxo *acc.UTXO) error {
417 data, err := json.Marshal(utxo)
421 if store.batch == nil {
422 store.walletDB.Set(ContractUTXOKey(outputID), data)
424 store.batch.Set(ContractUTXOKey(outputID), data)
429 // SetGlobalTransactionIndex set global tx index by blockhash and position
430 func (store *WalletStore) SetGlobalTransactionIndex(globalTxID string, blockHash *bc.Hash, position uint64) {
431 if store.batch == nil {
432 store.walletDB.Set(CalcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
434 store.batch.Set(CalcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
438 // SetRecoveryStatus set recovery status
439 func (store *WalletStore) SetRecoveryStatus(recoveryState *wallet.RecoveryState) error {
440 rawStatus, err := json.Marshal(recoveryState)
444 if store.batch == nil {
445 store.walletDB.Set(dbm.RecoveryKey, rawStatus)
447 store.batch.Set(dbm.RecoveryKey, rawStatus)
452 // SetTransaction set raw transaction by block height and tx position
453 func (store *WalletStore) SetTransaction(height uint64, tx *query.AnnotatedTx) error {
454 batch := store.walletDB.NewBatch()
455 if store.batch != nil {
459 rawTx, err := json.Marshal(tx)
463 batch.Set(calcAnnotatedKey(formatKey(height, tx.Position)), rawTx)
464 batch.Set(calcTxIndexKey(tx.ID.String()), []byte(formatKey(height, tx.Position)))
466 if store.batch == nil {
472 // SetUnconfirmedTransaction set unconfirmed tx by txID
473 func (store *WalletStore) SetUnconfirmedTransaction(txID string, tx *query.AnnotatedTx) error {
474 rawTx, err := json.Marshal(tx)
478 if store.batch == nil {
479 store.walletDB.Set(calcUnconfirmedTxKey(txID), rawTx)
481 store.batch.Set(calcUnconfirmedTxKey(txID), rawTx)
486 // SetWalletInfo get wallet information
487 func (store *WalletStore) SetWalletInfo(status *wallet.StatusInfo) error {
488 rawWallet, err := json.Marshal(status)
493 if store.batch == nil {
494 store.walletDB.Set([]byte(dbm.WalletKey), rawWallet)
496 store.batch.Set([]byte(dbm.WalletKey), rawWallet)