10 log "github.com/sirupsen/logrus"
12 acc "github.com/bytom/vapor/account"
13 "github.com/bytom/vapor/asset"
14 "github.com/bytom/vapor/blockchain/query"
15 dbm "github.com/bytom/vapor/database/leveldb"
16 "github.com/bytom/vapor/errors"
17 "github.com/bytom/vapor/protocol/bc"
18 "github.com/bytom/vapor/wallet"
22 sutxoPrefix byte = iota //SUTXOPrefix is ContractUTXOKey prefix
24 txPrefix //TxPrefix is wallet database transactions prefix
25 txIndexPrefix //TxIndexPrefix is wallet database tx index prefix
26 unconfirmedTxPrefix //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
27 globalTxIndexPrefix //GlobalTxIndexPrefix is wallet database global tx index prefix
31 recoveryKey //recoveryKey key for db store recovery info.
34 // pre-define variables
36 walletStore = []byte("WS:")
37 SUTXOPrefix = append(walletStore, sutxoPrefix, colon)
38 AccountAliasPrefix = append(walletStore, accountAliasPrefix, colon)
39 TxPrefix = append(walletStore, txPrefix, colon) //TxPrefix is wallet database transactions prefix
40 TxIndexPrefix = append(walletStore, txIndexPrefix, colon) //TxIndexPrefix is wallet database tx index prefix
41 UnconfirmedTxPrefix = append(walletStore, unconfirmedTxPrefix, colon) //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
42 GlobalTxIndexPrefix = append(walletStore, globalTxIndexPrefix, colon) //GlobalTxIndexPrefix is wallet database global tx index prefix
43 WalletKey = append(walletStore, walletKey)
44 MiningAddressKey = append(walletStore, miningAddressKey)
45 CoinbaseAbKey = append(walletStore, coinbaseAbKey)
46 RecoveryKey = append(walletStore, recoveryKey)
49 // ContractUTXOKey makes a smart contract unspent outputs key to store
50 func ContractUTXOKey(id bc.Hash) []byte {
51 return append(SUTXOPrefix, id.Bytes()...)
54 func calcDeleteKey(blockHeight uint64) []byte {
55 return []byte(fmt.Sprintf("%s%016x", TxPrefix, blockHeight))
58 func calcTxIndexKey(txID string) []byte {
59 return append(TxIndexPrefix, []byte(txID)...)
62 func calcAnnotatedKey(formatKey string) []byte {
63 return append(TxPrefix, []byte(formatKey)...)
66 func calcUnconfirmedTxKey(formatKey string) []byte {
67 return append(UnconfirmedTxPrefix, []byte(formatKey)...)
70 // CalcGlobalTxIndexKey calculate tx hash index key
71 func CalcGlobalTxIndexKey(txID string) []byte {
72 return append(GlobalTxIndexPrefix, []byte(txID)...)
75 // CalcGlobalTxIndex calcuate the block index + position index key
76 func CalcGlobalTxIndex(blockHash *bc.Hash, position uint64) []byte {
77 txIdx := make([]byte, 40)
78 copy(txIdx[:32], blockHash.Bytes())
79 binary.BigEndian.PutUint64(txIdx[32:], position)
83 func formatKey(blockHeight uint64, position uint32) string {
84 return fmt.Sprintf("%016x%08x", blockHeight, position)
87 func contractIndexKey(accountID string) []byte {
88 return append(ContractIndexPrefix, []byte(accountID)...)
91 // WalletStore store wallet using leveldb
92 type WalletStore struct {
97 // NewWalletStore create new WalletStore struct
98 func NewWalletStore(db dbm.DB) *WalletStore {
105 // InitBatch initial new wallet store
106 func (store *WalletStore) InitBatch() wallet.WalletStore {
107 newStore := NewWalletStore(store.db)
108 newStore.batch = newStore.db.NewBatch()
112 // CommitBatch commit batch
113 func (store *WalletStore) CommitBatch() error {
114 if store.batch == nil {
115 return errors.New("walletStore commit fail, store batch is nil")
123 // DeleteContractUTXO delete contract utxo by outputID
124 func (store *WalletStore) DeleteContractUTXO(outputID bc.Hash) {
125 if store.batch == nil {
126 store.db.Delete(ContractUTXOKey(outputID))
128 store.batch.Delete(ContractUTXOKey(outputID))
132 // DeleteRecoveryStatus delete recovery status
133 func (store *WalletStore) DeleteRecoveryStatus() {
134 if store.batch == nil {
135 store.db.Delete(RecoveryKey)
137 store.batch.Delete(RecoveryKey)
141 // DeleteTransactions delete transactions when orphan block rollback
142 func (store *WalletStore) DeleteTransactions(height uint64) {
143 batch := store.db.NewBatch()
144 if store.batch != nil {
147 txIter := store.db.IteratorPrefix(calcDeleteKey(height))
148 defer txIter.Release()
151 tmpTx := new(query.AnnotatedTx)
152 if err := json.Unmarshal(txIter.Value(), tmpTx); err == nil {
153 batch.Delete(calcTxIndexKey(tmpTx.ID.String()))
155 log.WithFields(log.Fields{"module": logModule, "err": err}).Warning("fail on DeleteTransactions.")
157 batch.Delete(txIter.Key())
159 if store.batch == nil {
164 // DeleteUnconfirmedTransaction delete unconfirmed tx by txID
165 func (store *WalletStore) DeleteUnconfirmedTransaction(txID string) {
166 if store.batch == nil {
167 store.db.Delete(calcUnconfirmedTxKey(txID))
169 store.batch.Delete(calcUnconfirmedTxKey(txID))
173 // DeleteWalletTransactions delete all txs in wallet
174 func (store *WalletStore) DeleteWalletTransactions() {
175 batch := store.db.NewBatch()
176 if store.batch != nil {
179 txIter := store.db.IteratorPrefix(TxPrefix)
180 defer txIter.Release()
183 batch.Delete(txIter.Key())
186 txIndexIter := store.db.IteratorPrefix(TxIndexPrefix)
187 defer txIndexIter.Release()
189 for txIndexIter.Next() {
190 batch.Delete(txIndexIter.Key())
192 if store.batch == nil {
197 // DeleteWalletUTXOs delete all utxos in wallet
198 func (store *WalletStore) DeleteWalletUTXOs() {
199 batch := store.db.NewBatch()
200 if store.batch != nil {
204 ruIter := store.db.IteratorPrefix(UTXOPrefix)
205 defer ruIter.Release()
208 batch.Delete(ruIter.Key())
211 suIter := store.db.IteratorPrefix(SUTXOPrefix)
212 defer suIter.Release()
215 batch.Delete(suIter.Key())
217 if store.batch == nil {
222 // GetAsset get asset by assetID
223 func (store *WalletStore) GetAsset(assetID *bc.AssetID) (*asset.Asset, error) {
224 definitionByte := store.db.Get(asset.ExtAssetKey(assetID))
225 if definitionByte == nil {
226 return nil, wallet.ErrGetAsset
229 definitionMap := make(map[string]interface{})
230 if err := json.Unmarshal(definitionByte, &definitionMap); err != nil {
234 alias := strings.ToUpper(assetID.String())
235 externalAsset := &asset.Asset{
238 DefinitionMap: definitionMap,
239 RawDefinitionByte: definitionByte,
241 return externalAsset, nil
244 // GetGlobalTransactionIndex get global tx by txID
245 func (store *WalletStore) GetGlobalTransactionIndex(txID string) []byte {
246 return store.db.Get(CalcGlobalTxIndexKey(txID))
249 // GetStandardUTXO get standard utxo by id
250 func (store *WalletStore) GetStandardUTXO(outid bc.Hash) (*acc.UTXO, error) {
251 rawUTXO := store.db.Get(StandardUTXOKey(outid))
253 return nil, wallet.ErrGetStandardUTXO
256 UTXO := new(acc.UTXO)
257 if err := json.Unmarshal(rawUTXO, UTXO); err != nil {
264 // GetTransaction get tx by txid
265 func (store *WalletStore) GetTransaction(txID string) (*query.AnnotatedTx, error) {
266 formatKey := store.db.Get(calcTxIndexKey(txID))
267 if formatKey == nil {
268 return nil, wallet.ErrAccntTxIDNotFound
271 rawTx := store.db.Get(calcAnnotatedKey(string(formatKey)))
272 tx := new(query.AnnotatedTx)
273 if err := json.Unmarshal(rawTx, tx); err != nil {
280 // GetUnconfirmedTransaction get unconfirmed tx by txID
281 func (store *WalletStore) GetUnconfirmedTransaction(txID string) (*query.AnnotatedTx, error) {
282 rawUnconfirmedTx := store.db.Get(calcUnconfirmedTxKey(txID))
283 if rawUnconfirmedTx == nil {
284 return nil, fmt.Errorf("failed get unconfirmed tx, txID: %s ", txID)
287 tx := new(query.AnnotatedTx)
288 if err := json.Unmarshal(rawUnconfirmedTx, tx); err != nil {
295 // GetRecoveryStatus delete recovery status
296 func (store *WalletStore) GetRecoveryStatus() (*wallet.RecoveryState, error) {
297 rawStatus := store.db.Get(RecoveryKey)
298 if rawStatus == nil {
299 return nil, wallet.ErrGetRecoveryStatus
302 state := new(wallet.RecoveryState)
303 if err := json.Unmarshal(rawStatus, state); err != nil {
310 // GetWalletInfo get wallet information
311 func (store *WalletStore) GetWalletInfo() (*wallet.StatusInfo, error) {
312 rawStatus := store.db.Get(WalletKey)
313 if rawStatus == nil {
314 return nil, wallet.ErrGetWalletStatusInfo
317 status := new(wallet.StatusInfo)
318 if err := json.Unmarshal(rawStatus, status); err != nil {
325 // ListAccountUTXOs get all account unspent outputs
326 func (store *WalletStore) ListAccountUTXOs(id string, isSmartContract bool) ([]*acc.UTXO, error) {
332 idBytes, err := hex.DecodeString(id)
337 accountUtxoIter := store.db.IteratorPrefix(append(prefix, idBytes...))
338 defer accountUtxoIter.Release()
340 confirmedUTXOs := []*acc.UTXO{}
341 for accountUtxoIter.Next() {
342 utxo := new(acc.UTXO)
343 if err := json.Unmarshal(accountUtxoIter.Value(), utxo); err != nil {
347 confirmedUTXOs = append(confirmedUTXOs, utxo)
350 return confirmedUTXOs, nil
353 // ListTransactions list tx by filter args
354 func (store *WalletStore) ListTransactions(accountID string, StartTxID string, count uint, unconfirmed bool) ([]*query.AnnotatedTx, error) {
355 annotatedTxs := []*query.AnnotatedTx{}
361 startKey = calcUnconfirmedTxKey(StartTxID)
363 formatKey := store.db.Get(calcTxIndexKey(StartTxID))
364 if formatKey == nil {
365 return nil, wallet.ErrAccntTxIDNotFound
368 startKey = calcAnnotatedKey(string(formatKey))
373 preFix = UnconfirmedTxPrefix
376 itr := store.db.IteratorPrefixWithStart(preFix, startKey, true)
379 for txNum := count; itr.Next() && txNum > 0; {
380 annotatedTx := new(query.AnnotatedTx)
381 if err := json.Unmarshal(itr.Value(), annotatedTx); err != nil {
385 if accountID == "" || wallet.FindTransactionsByAccount(annotatedTx, accountID) {
386 annotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, annotatedTxs...)
391 return annotatedTxs, nil
394 // ListUnconfirmedTransactions get all unconfirmed txs
395 func (store *WalletStore) ListUnconfirmedTransactions() ([]*query.AnnotatedTx, error) {
396 annotatedTxs := []*query.AnnotatedTx{}
397 txIter := store.db.IteratorPrefix(UnconfirmedTxPrefix)
398 defer txIter.Release()
401 annotatedTx := new(query.AnnotatedTx)
402 if err := json.Unmarshal(txIter.Value(), annotatedTx); err != nil {
406 annotatedTxs = append(annotatedTxs, annotatedTx)
408 return annotatedTxs, nil
411 // SetAssetDefinition set assetID and definition
412 func (store *WalletStore) SetAssetDefinition(assetID *bc.AssetID, definition []byte) {
413 if store.batch == nil {
414 store.db.Set(asset.ExtAssetKey(assetID), definition)
416 store.batch.Set(asset.ExtAssetKey(assetID), definition)
420 // SetContractUTXO set standard utxo
421 func (store *WalletStore) SetContractUTXO(outputID bc.Hash, utxo *acc.UTXO) error {
422 data, err := json.Marshal(utxo)
427 if store.batch == nil {
428 store.db.Set(ContractUTXOKey(outputID), data)
430 store.batch.Set(ContractUTXOKey(outputID), data)
435 // SetGlobalTransactionIndex set global tx index by blockhash and position
436 func (store *WalletStore) SetGlobalTransactionIndex(globalTxID string, blockHash *bc.Hash, position uint64) {
437 if store.batch == nil {
438 store.db.Set(CalcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
440 store.batch.Set(CalcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
444 // SetRecoveryStatus set recovery status
445 func (store *WalletStore) SetRecoveryStatus(recoveryState *wallet.RecoveryState) error {
446 rawStatus, err := json.Marshal(recoveryState)
451 if store.batch == nil {
452 store.db.Set(RecoveryKey, rawStatus)
454 store.batch.Set(RecoveryKey, rawStatus)
459 // SetTransaction set raw transaction by block height and tx position
460 func (store *WalletStore) SetTransaction(height uint64, tx *query.AnnotatedTx) error {
461 batch := store.db.NewBatch()
462 if store.batch != nil {
466 rawTx, err := json.Marshal(tx)
471 batch.Set(calcAnnotatedKey(formatKey(height, tx.Position)), rawTx)
472 batch.Set(calcTxIndexKey(tx.ID.String()), []byte(formatKey(height, tx.Position)))
474 if store.batch == nil {
480 // SetUnconfirmedTransaction set unconfirmed tx by txID
481 func (store *WalletStore) SetUnconfirmedTransaction(txID string, tx *query.AnnotatedTx) error {
482 rawTx, err := json.Marshal(tx)
487 if store.batch == nil {
488 store.db.Set(calcUnconfirmedTxKey(txID), rawTx)
490 store.batch.Set(calcUnconfirmedTxKey(txID), rawTx)
495 // SetWalletInfo get wallet information
496 func (store *WalletStore) SetWalletInfo(status *wallet.StatusInfo) error {
497 rawWallet, err := json.Marshal(status)
502 if store.batch == nil {
503 store.db.Set(WalletKey, rawWallet)
505 store.batch.Set(WalletKey, rawWallet)