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.
35 walletStore = []byte("WS:")
36 SUTXOPrefix = append(walletStore, sutxoPrefix, colon)
37 AccountAliasPrefix = append(walletStore, accountAliasPrefix, colon)
38 TxPrefix = append(walletStore, txPrefix, colon) //TxPrefix is wallet database transactions prefix
39 TxIndexPrefix = append(walletStore, txIndexPrefix, colon) //TxIndexPrefix is wallet database tx index prefix
40 UnconfirmedTxPrefix = append(walletStore, unconfirmedTxPrefix, colon) //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
41 GlobalTxIndexPrefix = append(walletStore, globalTxIndexPrefix, colon) //GlobalTxIndexPrefix is wallet database global tx index prefix
42 WalletKey = append(walletStore, walletKey)
43 MiningAddressKey = append(walletStore, miningAddressKey)
44 CoinbaseAbKey = append(walletStore, coinbaseAbKey)
45 RecoveryKey = append(walletStore, recoveryKey)
48 // ContractUTXOKey makes a smart contract unspent outputs key to store
49 func ContractUTXOKey(id bc.Hash) []byte {
50 return append(SUTXOPrefix, id.Bytes()...)
53 func calcDeleteKey(blockHeight uint64) []byte {
54 return []byte(fmt.Sprintf("%s%016x", TxPrefix, blockHeight))
57 func calcTxIndexKey(txID string) []byte {
58 return append(TxIndexPrefix, []byte(txID)...)
61 func calcAnnotatedKey(formatKey string) []byte {
62 return append(TxPrefix, []byte(formatKey)...)
65 func calcUnconfirmedTxKey(formatKey string) []byte {
66 return append(UnconfirmedTxPrefix, []byte(formatKey)...)
69 func CalcGlobalTxIndexKey(txID string) []byte {
70 return append(GlobalTxIndexPrefix, []byte(txID)...)
73 func CalcGlobalTxIndex(blockHash *bc.Hash, position uint64) []byte {
74 txIdx := make([]byte, 40)
75 copy(txIdx[:32], blockHash.Bytes())
76 binary.BigEndian.PutUint64(txIdx[32:], position)
80 func formatKey(blockHeight uint64, position uint32) string {
81 return fmt.Sprintf("%016x%08x", blockHeight, position)
84 func contractIndexKey(accountID string) []byte {
85 return append(ContractIndexPrefix, []byte(accountID)...)
88 // WalletStore store wallet using leveldb
89 type WalletStore struct {
94 // NewWalletStore create new WalletStore struct
95 func NewWalletStore(db dbm.DB) *WalletStore {
102 // InitBatch initial new wallet store
103 func (store *WalletStore) InitBatch() wallet.WalletStore {
104 newStore := NewWalletStore(store.db)
105 newStore.batch = newStore.db.NewBatch()
109 // CommitBatch commit batch
110 func (store *WalletStore) CommitBatch() error {
111 if store.batch == nil {
112 return errors.New("WalletStore commit fail, store batch is nil.")
120 // DeleteContractUTXO delete contract utxo by outputID
121 func (store *WalletStore) DeleteContractUTXO(outputID bc.Hash) {
122 if store.batch == nil {
123 store.db.Delete(ContractUTXOKey(outputID))
125 store.batch.Delete(ContractUTXOKey(outputID))
129 // DeleteRecoveryStatus delete recovery status
130 func (store *WalletStore) DeleteRecoveryStatus() {
131 if store.batch == nil {
132 store.db.Delete(RecoveryKey)
134 store.batch.Delete(RecoveryKey)
138 // DeleteTransactions delete transactions when orphan block rollback
139 func (store *WalletStore) DeleteTransactions(height uint64) {
140 batch := store.db.NewBatch()
141 if store.batch != nil {
144 txIter := store.db.IteratorPrefix(calcDeleteKey(height))
145 defer txIter.Release()
148 tmpTx := new(query.AnnotatedTx)
149 if err := json.Unmarshal(txIter.Value(), tmpTx); err == nil {
150 batch.Delete(calcTxIndexKey(tmpTx.ID.String()))
152 log.WithFields(log.Fields{"module": logModule, "err": err}).Warning("fail on DeleteTransactions.")
154 batch.Delete(txIter.Key())
156 if store.batch == nil {
161 // DeleteUnconfirmedTransaction delete unconfirmed tx by txID
162 func (store *WalletStore) DeleteUnconfirmedTransaction(txID string) {
163 if store.batch == nil {
164 store.db.Delete(calcUnconfirmedTxKey(txID))
166 store.batch.Delete(calcUnconfirmedTxKey(txID))
170 // DeleteWalletTransactions delete all txs in wallet
171 func (store *WalletStore) DeleteWalletTransactions() {
172 batch := store.db.NewBatch()
173 if store.batch != nil {
176 txIter := store.db.IteratorPrefix(TxPrefix)
177 defer txIter.Release()
180 batch.Delete(txIter.Key())
183 txIndexIter := store.db.IteratorPrefix(TxIndexPrefix)
184 defer txIndexIter.Release()
186 for txIndexIter.Next() {
187 batch.Delete(txIndexIter.Key())
189 if store.batch == nil {
194 // DeleteWalletUTXOs delete all utxos in wallet
195 func (store *WalletStore) DeleteWalletUTXOs() {
196 batch := store.db.NewBatch()
197 if store.batch != nil {
201 ruIter := store.db.IteratorPrefix(UTXOPrefix)
202 defer ruIter.Release()
205 batch.Delete(ruIter.Key())
208 suIter := store.db.IteratorPrefix(SUTXOPrefix)
209 defer suIter.Release()
212 batch.Delete(suIter.Key())
214 if store.batch == nil {
219 // GetAsset get asset by assetID
220 func (store *WalletStore) GetAsset(assetID *bc.AssetID) (*asset.Asset, error) {
221 definitionByte := store.db.Get(asset.ExtAssetKey(assetID))
222 if definitionByte == nil {
223 return nil, wallet.ErrGetAsset
226 definitionMap := make(map[string]interface{})
227 if err := json.Unmarshal(definitionByte, &definitionMap); err != nil {
231 alias := strings.ToUpper(assetID.String())
232 externalAsset := &asset.Asset{
235 DefinitionMap: definitionMap,
236 RawDefinitionByte: definitionByte,
238 return externalAsset, nil
241 // GetGlobalTransactionIndex get global tx by txID
242 func (store *WalletStore) GetGlobalTransactionIndex(txID string) []byte {
243 return store.db.Get(CalcGlobalTxIndexKey(txID))
246 // GetStandardUTXO get standard utxo by id
247 func (store *WalletStore) GetStandardUTXO(outid bc.Hash) (*acc.UTXO, error) {
248 rawUTXO := store.db.Get(StandardUTXOKey(outid))
250 return nil, wallet.ErrGetStandardUTXO
253 UTXO := new(acc.UTXO)
254 if err := json.Unmarshal(rawUTXO, UTXO); err != nil {
261 // GetTransaction get tx by txid
262 func (store *WalletStore) GetTransaction(txID string) (*query.AnnotatedTx, error) {
263 formatKey := store.db.Get(calcTxIndexKey(txID))
264 if formatKey == nil {
265 return nil, wallet.ErrAccntTxIDNotFound
268 rawTx := store.db.Get(calcAnnotatedKey(string(formatKey)))
269 tx := new(query.AnnotatedTx)
270 if err := json.Unmarshal(rawTx, tx); err != nil {
277 // GetUnconfirmedTransaction get unconfirmed tx by txID
278 func (store *WalletStore) GetUnconfirmedTransaction(txID string) (*query.AnnotatedTx, error) {
279 rawUnconfirmedTx := store.db.Get(calcUnconfirmedTxKey(txID))
280 if rawUnconfirmedTx == nil {
281 return nil, fmt.Errorf("failed get unconfirmed tx, txID: %s ", txID)
284 tx := new(query.AnnotatedTx)
285 if err := json.Unmarshal(rawUnconfirmedTx, tx); err != nil {
292 // GetRecoveryStatus delete recovery status
293 func (store *WalletStore) GetRecoveryStatus() (*wallet.RecoveryState, error) {
294 rawStatus := store.db.Get(RecoveryKey)
295 if rawStatus == nil {
296 return nil, wallet.ErrGetRecoveryStatus
299 state := new(wallet.RecoveryState)
300 if err := json.Unmarshal(rawStatus, state); err != nil {
307 // GetWalletInfo get wallet information
308 func (store *WalletStore) GetWalletInfo() (*wallet.StatusInfo, error) {
309 rawStatus := store.db.Get(WalletKey)
310 if rawStatus == nil {
311 return nil, wallet.ErrGetWalletStatusInfo
314 status := new(wallet.StatusInfo)
315 if err := json.Unmarshal(rawStatus, status); err != nil {
322 // ListAccountUTXOs get all account unspent outputs
323 func (store *WalletStore) ListAccountUTXOs(id string, isSmartContract bool) ([]*acc.UTXO, error) {
329 idBytes, err := hex.DecodeString(id)
334 accountUtxoIter := store.db.IteratorPrefix(append(prefix, idBytes...))
335 defer accountUtxoIter.Release()
337 confirmedUTXOs := []*acc.UTXO{}
338 for accountUtxoIter.Next() {
339 utxo := new(acc.UTXO)
340 if err := json.Unmarshal(accountUtxoIter.Value(), utxo); err != nil {
344 confirmedUTXOs = append(confirmedUTXOs, utxo)
347 return confirmedUTXOs, nil
350 func (store *WalletStore) ListTransactions(accountID string, StartTxID string, count uint, unconfirmed bool) ([]*query.AnnotatedTx, error) {
351 annotatedTxs := []*query.AnnotatedTx{}
357 startKey = calcUnconfirmedTxKey(StartTxID)
359 formatKey := store.db.Get(calcTxIndexKey(StartTxID))
360 if formatKey == nil {
361 return nil, wallet.ErrAccntTxIDNotFound
364 startKey = calcAnnotatedKey(string(formatKey))
369 preFix = UnconfirmedTxPrefix
372 itr := store.db.IteratorPrefixWithStart(preFix, startKey, true)
375 for txNum := count; itr.Next() && txNum > 0; {
376 annotatedTx := new(query.AnnotatedTx)
377 if err := json.Unmarshal(itr.Value(), annotatedTx); err != nil {
381 if accountID == "" || wallet.FindTransactionsByAccount(annotatedTx, accountID) {
382 annotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, annotatedTxs...)
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.db.IteratorPrefix(UnconfirmedTxPrefix)
394 defer txIter.Release()
397 annotatedTx := new(query.AnnotatedTx)
398 if err := json.Unmarshal(txIter.Value(), annotatedTx); err != nil {
402 annotatedTxs = append(annotatedTxs, annotatedTx)
404 return annotatedTxs, nil
407 // SetAssetDefinition set assetID and definition
408 func (store *WalletStore) SetAssetDefinition(assetID *bc.AssetID, definition []byte) {
409 if store.batch == nil {
410 store.db.Set(asset.ExtAssetKey(assetID), definition)
412 store.batch.Set(asset.ExtAssetKey(assetID), definition)
416 // SetContractUTXO set standard utxo
417 func (store *WalletStore) SetContractUTXO(outputID bc.Hash, utxo *acc.UTXO) error {
418 data, err := json.Marshal(utxo)
423 if store.batch == nil {
424 store.db.Set(ContractUTXOKey(outputID), data)
426 store.batch.Set(ContractUTXOKey(outputID), data)
431 // SetGlobalTransactionIndex set global tx index by blockhash and position
432 func (store *WalletStore) SetGlobalTransactionIndex(globalTxID string, blockHash *bc.Hash, position uint64) {
433 if store.batch == nil {
434 store.db.Set(CalcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
436 store.batch.Set(CalcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
440 // SetRecoveryStatus set recovery status
441 func (store *WalletStore) SetRecoveryStatus(recoveryState *wallet.RecoveryState) error {
442 rawStatus, err := json.Marshal(recoveryState)
447 if store.batch == nil {
448 store.db.Set(RecoveryKey, rawStatus)
450 store.batch.Set(RecoveryKey, rawStatus)
455 // SetTransaction set raw transaction by block height and tx position
456 func (store *WalletStore) SetTransaction(height uint64, tx *query.AnnotatedTx) error {
457 batch := store.db.NewBatch()
458 if store.batch != nil {
462 rawTx, err := json.Marshal(tx)
467 batch.Set(calcAnnotatedKey(formatKey(height, tx.Position)), rawTx)
468 batch.Set(calcTxIndexKey(tx.ID.String()), []byte(formatKey(height, tx.Position)))
470 if store.batch == nil {
476 // SetUnconfirmedTransaction set unconfirmed tx by txID
477 func (store *WalletStore) SetUnconfirmedTransaction(txID string, tx *query.AnnotatedTx) error {
478 rawTx, err := json.Marshal(tx)
483 if store.batch == nil {
484 store.db.Set(calcUnconfirmedTxKey(txID), rawTx)
486 store.batch.Set(calcUnconfirmedTxKey(txID), rawTx)
491 // SetWalletInfo get wallet information
492 func (store *WalletStore) SetWalletInfo(status *wallet.StatusInfo) error {
493 rawWallet, err := json.Marshal(status)
498 if store.batch == nil {
499 store.db.Set(WalletKey, rawWallet)
501 store.batch.Set(WalletKey, rawWallet)