9 acc "github.com/vapor/account"
10 "github.com/vapor/asset"
11 "github.com/vapor/blockchain/query"
12 "github.com/vapor/blockchain/signers"
13 "github.com/vapor/common"
14 "github.com/vapor/crypto/ed25519/chainkd"
15 "github.com/vapor/crypto/sha3pool"
16 dbm "github.com/vapor/database/leveldb"
17 "github.com/vapor/errors"
18 "github.com/vapor/protocol/bc"
22 // UTXOPrefix = "ACU:" //UTXOPrefix is StandardUTXOKey prefix
23 // SUTXOPrefix = "SCU:" //SUTXOPrefix is ContractUTXOKey prefix
25 ContractPrefix = "Contract:"
27 // ContractIndexPrefix = "ContractIndex:"
28 // AccountPrefix = "Account:" // AccountPrefix is account ID prefix
29 // AccountAliasPrefix = "AccountAlias:"
30 // AccountIndexPrefix = "AccountIndex:"
31 // TxPrefix = "TXS:" //TxPrefix is wallet database transactions prefix
32 // TxIndexPrefix = "TID:" //TxIndexPrefix is wallet database tx index prefix
33 // UnconfirmedTxPrefix = "UTXS:" //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
34 // GlobalTxIndexPrefix = "GTID:" //GlobalTxIndexPrefix is wallet database global tx index prefix
35 // WalletKey = "WalletInfo"
36 // MiningAddressKey = "MiningAddress"
37 // CoinbaseAbKey = "CoinbaseArbitrary"
41 utxoPrefix byte = iota //UTXOPrefix is StandardUTXOKey prefix
42 sUTXOPrefix //SUTXOPrefix is ContractUTXOKey prefix
45 accountPrefix // AccountPrefix is account ID prefix
48 txPrefix //TxPrefix is wallet database transactions prefix
49 txIndexPrefix //TxIndexPrefix is wallet database tx index prefix
50 unconfirmedTxPrefix //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
51 globalTxIndexPrefix //GlobalTxIndexPrefix is wallet database global tx index prefix
59 UTXOPrefix = []byte{utxoPrefix, colon}
60 SUTXOPrefix = []byte{sUTXOPrefix, colon}
61 // ContractPrefix = []byte{contractPrefix, colon}
62 ContractIndexPrefix = []byte{contractIndexPrefix, colon}
63 AccountPrefix = []byte{accountPrefix, colon} // AccountPrefix is account ID prefix
64 AccountAliasPrefix = []byte{accountAliasPrefix, colon}
65 AccountIndexPrefix = []byte{accountIndexPrefix, colon}
66 TxPrefix = []byte{txPrefix, colon} //TxPrefix is wallet database transactions prefix
67 TxIndexPrefix = []byte{txIndexPrefix, colon} //TxIndexPrefix is wallet database tx index prefix
68 UnconfirmedTxPrefix = []byte{unconfirmedTxPrefix, colon} //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
69 GlobalTxIndexPrefix = []byte{globalTxIndexPrefix, colon} //GlobalTxIndexPrefix is wallet database global tx index prefix
70 WalletKey = []byte{walletKey}
71 MiningAddressKey = []byte{miningAddressKey}
72 CoinbaseAbKey = []byte{coinbaseAbKey}
77 // ErrFindAccount = errors.New("Failed to find account")
78 errAccntTxIDNotFound = errors.New("account TXID not found")
79 errGetAssetDefinition = errors.New("Failed to find asset definition")
82 func accountIndexKey(xpubs []chainkd.XPub) []byte {
85 cpy := append([]chainkd.XPub{}, xpubs[:]...)
86 sort.Sort(signers.SortKeys(cpy))
87 for _, xpub := range cpy {
88 xPubs = append(xPubs, xpub[:]...)
90 sha3pool.Sum256(hash[:], xPubs)
91 return append([]byte(AccountIndexPrefix), hash[:]...)
94 func Bip44ContractIndexKey(accountID string, change bool) []byte {
95 key := append([]byte(ContractIndexPrefix), accountID...)
97 return append(key, []byte{1}...)
99 return append(key, []byte{0}...)
102 // ContractKey account control promgram store prefix
103 func ContractKey(hash common.Hash) []byte {
105 // return append([]byte(ContractPrefix), []byte(h)...)
106 return append([]byte(ContractPrefix), hash.Bytes()...)
109 // AccountIDKey account id store prefix
110 func AccountIDKey(accountID string) []byte {
111 return append([]byte(AccountPrefix), []byte(accountID)...)
114 // StandardUTXOKey makes an account unspent outputs key to store
115 func StandardUTXOKey(id bc.Hash) []byte {
117 return append(UTXOPrefix, []byte(name)...)
120 // ContractUTXOKey makes a smart contract unspent outputs key to store
121 func ContractUTXOKey(id bc.Hash) []byte {
123 return append(SUTXOPrefix, []byte(name)...)
126 func calcDeleteKey(blockHeight uint64) []byte {
127 return []byte(fmt.Sprintf("%s%016x", TxPrefix, blockHeight))
130 func calcTxIndexKey(txID string) []byte {
131 return append(TxIndexPrefix, []byte(txID)...)
134 func calcAnnotatedKey(formatKey string) []byte {
135 return append(TxPrefix, []byte(formatKey)...)
138 func calcUnconfirmedTxKey(formatKey string) []byte {
139 return append(UnconfirmedTxPrefix, []byte(formatKey)...)
142 func calcGlobalTxIndexKey(txID string) []byte {
143 return append(GlobalTxIndexPrefix, []byte(txID)...)
146 func CalcGlobalTxIndex(blockHash *bc.Hash, position uint64) []byte {
147 txIdx := make([]byte, 40)
148 copy(txIdx[:32], blockHash.Bytes())
149 binary.BigEndian.PutUint64(txIdx[32:], position)
153 func formatKey(blockHeight uint64, position uint32) string {
154 return fmt.Sprintf("%016x%08x", blockHeight, position)
157 func contractIndexKey(accountID string) []byte {
158 return append([]byte(ContractIndexPrefix), []byte(accountID)...)
161 func accountAliasKey(name string) []byte {
162 return append([]byte(AccountAliasPrefix), []byte(name)...)
165 // WalletStore store wallet using leveldb
166 type WalletStore struct {
171 // NewWalletStore create new WalletStore struct
172 func NewWalletStore(db dbm.DB) *WalletStore {
179 // InitBatch initial batch
180 func (store *WalletStore) InitBatch() {
181 if store.batch == nil {
182 store.batch = store.walletDB.NewBatch()
186 // CommitBatch commit batch
187 func (store *WalletStore) CommitBatch() {
188 if store.batch != nil {
194 // GetAssetDefinition get asset definition by assetiD
195 func (store *WalletStore) GetAssetDefinition(assetID *bc.AssetID) (*asset.Asset, error) {
196 definitionByte := store.walletDB.Get(asset.ExtAssetKey(assetID))
197 if definitionByte == nil {
198 return nil, errGetAssetDefinition
200 definitionMap := make(map[string]interface{})
201 if err := json.Unmarshal(definitionByte, &definitionMap); err != nil {
204 alias := assetID.String()
205 externalAsset := &asset.Asset{
208 DefinitionMap: definitionMap,
209 RawDefinitionByte: definitionByte,
211 return externalAsset, nil
214 // SetAssetDefinition set assetID and definition
215 func (store *WalletStore) SetAssetDefinition(assetID *bc.AssetID, definition []byte) {
216 if store.batch == nil {
217 store.walletDB.Set(asset.ExtAssetKey(assetID), definition)
219 store.batch.Set(asset.ExtAssetKey(assetID), definition)
223 // GetControlProgram get raw program by hash
224 func (store *WalletStore) GetControlProgram(hash common.Hash) (*acc.CtrlProgram, error) {
225 rawProgram := store.walletDB.Get(ContractKey(hash))
226 if rawProgram == nil {
227 return nil, fmt.Errorf("failed get account control program:%x ", hash)
229 accountCP := new(acc.CtrlProgram)
230 if err := json.Unmarshal(rawProgram, &accountCP); err != nil {
233 return accountCP, nil
236 // GetAccountByAccountID get account value by account ID
237 func (store *WalletStore) GetAccountByAccountID(accountID string) (*acc.Account, error) {
238 rawAccount := store.walletDB.Get(AccountIDKey(accountID))
239 if rawAccount == nil {
240 return nil, fmt.Errorf("failed get account, accountID: %s ", accountID)
242 account := new(acc.Account)
243 if err := json.Unmarshal(rawAccount, account); err != nil {
249 // DeleteTransactions delete transactions when orphan block rollback
250 func (store *WalletStore) DeleteTransactions(height uint64) {
251 batch := store.walletDB.NewBatch()
252 if store.batch != nil {
255 txIter := store.walletDB.IteratorPrefix(calcDeleteKey(height))
256 defer txIter.Release()
258 tmpTx := query.AnnotatedTx{}
260 if err := json.Unmarshal(txIter.Value(), &tmpTx); err == nil {
261 batch.Delete(calcTxIndexKey(tmpTx.ID.String()))
263 batch.Delete(txIter.Key())
265 if store.batch == nil {
270 // SetTransaction set raw transaction by block height and tx position
271 func (store *WalletStore) SetTransaction(height uint64, tx *query.AnnotatedTx) error {
272 batch := store.walletDB.NewBatch()
273 if store.batch != nil {
277 rawTx, err := json.Marshal(tx)
281 batch.Set(calcAnnotatedKey(formatKey(height, tx.Position)), rawTx)
282 batch.Set(calcTxIndexKey(tx.ID.String()), []byte(formatKey(height, tx.Position)))
284 if store.batch == nil {
290 // DeleteUnconfirmedTransaction delete unconfirmed tx by txID
291 func (store *WalletStore) DeleteUnconfirmedTransaction(txID string) {
292 if store.batch == nil {
293 store.walletDB.Delete(calcUnconfirmedTxKey(txID))
295 store.batch.Delete(calcUnconfirmedTxKey(txID))
299 // SetGlobalTransactionIndex set global tx index by blockhash and position
300 func (store *WalletStore) SetGlobalTransactionIndex(globalTxID string, blockHash *bc.Hash, position uint64) {
301 if store.batch == nil {
302 store.walletDB.Set(calcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
304 store.batch.Set(calcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
308 // GetStandardUTXO get standard utxo by id
309 func (store *WalletStore) GetStandardUTXO(outid bc.Hash) (*acc.UTXO, error) {
310 rawUTXO := store.walletDB.Get(StandardUTXOKey(outid))
312 return nil, fmt.Errorf("failed get standard UTXO, outputID: %s ", outid.String())
314 UTXO := new(acc.UTXO)
315 if err := json.Unmarshal(rawUTXO, UTXO); err != nil {
321 // GetTransaction get tx by txid
322 func (store *WalletStore) GetTransaction(txID string) (*query.AnnotatedTx, error) {
323 formatKey := store.walletDB.Get(calcTxIndexKey(txID))
324 if formatKey == nil {
325 return nil, errAccntTxIDNotFound
327 rawTx := store.walletDB.Get(calcAnnotatedKey(string(formatKey)))
328 tx := new(query.AnnotatedTx)
329 if err := json.Unmarshal(rawTx, tx); err != nil {
335 // GetGlobalTransaction get global tx by txID
336 func (store *WalletStore) GetGlobalTransaction(txID string) []byte {
337 return store.walletDB.Get(calcGlobalTxIndexKey(txID))
340 // GetTransactions get all walletDB transactions
341 func (store *WalletStore) GetTransactions() ([]*query.AnnotatedTx, error) {
342 annotatedTxs := []*query.AnnotatedTx{}
344 txIter := store.walletDB.IteratorPrefix([]byte(TxPrefix))
345 defer txIter.Release()
347 annotatedTx := &query.AnnotatedTx{}
348 if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
351 annotatedTxs = append(annotatedTxs, annotatedTx)
354 return annotatedTxs, nil
357 // GetUnconfirmedTransactions get all unconfirmed txs
358 func (store *WalletStore) GetUnconfirmedTransactions() ([]*query.AnnotatedTx, error) {
359 annotatedTxs := []*query.AnnotatedTx{}
360 txIter := store.walletDB.IteratorPrefix([]byte(UnconfirmedTxPrefix))
361 defer txIter.Release()
364 annotatedTx := &query.AnnotatedTx{}
365 if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
368 annotatedTxs = append(annotatedTxs, annotatedTx)
370 return annotatedTxs, nil
373 // GetUnconfirmedTransaction get unconfirmed tx by txID
374 func (store *WalletStore) GetUnconfirmedTransaction(txID string) []byte {
375 return store.walletDB.Get(calcUnconfirmedTxKey(txID))
378 // SetUnconfirmedTransaction set unconfirmed tx by txID
379 func (store *WalletStore) SetUnconfirmedTransaction(txID string, rawTx []byte) {
380 if store.batch == nil {
381 store.walletDB.Set(calcUnconfirmedTxKey(txID), rawTx)
383 store.batch.Set(calcUnconfirmedTxKey(txID), rawTx)
387 // DeleteStardardUTXO delete stardard utxo by outputID
388 func (store *WalletStore) DeleteStardardUTXO(outputID bc.Hash) {
389 if store.batch == nil {
390 store.walletDB.Delete(StandardUTXOKey(outputID))
392 store.batch.Delete(StandardUTXOKey(outputID))
396 // DeleteContractUTXO delete contract utxo by outputID
397 func (store *WalletStore) DeleteContractUTXO(outputID bc.Hash) {
398 if store.batch == nil {
399 store.walletDB.Delete(ContractUTXOKey(outputID))
401 store.batch.Delete(ContractUTXOKey(outputID))
405 // SetStandardUTXO set standard utxo
406 func (store *WalletStore) SetStandardUTXO(outputID bc.Hash, data []byte) {
407 if store.batch == nil {
408 store.walletDB.Set(StandardUTXOKey(outputID), data)
410 store.batch.Set(StandardUTXOKey(outputID), data)
414 // SetContractUTXO set standard utxo
415 func (store *WalletStore) SetContractUTXO(outputID bc.Hash, data []byte) {
416 if store.batch == nil {
417 store.walletDB.Set(ContractUTXOKey(outputID), data)
419 store.batch.Set(ContractUTXOKey(outputID), data)
423 // GetWalletInfo get wallet information
424 func (store *WalletStore) GetWalletInfo() []byte {
425 return store.walletDB.Get([]byte(WalletKey))
428 // SetWalletInfo get wallet information
429 func (store *WalletStore) SetWalletInfo(rawWallet []byte) {
430 if store.batch == nil {
431 store.walletDB.Set([]byte(WalletKey), rawWallet)
433 store.batch.Set([]byte(WalletKey), rawWallet)
437 // DeleteWalletTransactions delete all txs in wallet
438 func (store *WalletStore) DeleteWalletTransactions() {
439 batch := store.walletDB.NewBatch()
440 if store.batch != nil {
443 txIter := store.walletDB.IteratorPrefix([]byte(TxPrefix))
444 defer txIter.Release()
447 batch.Delete(txIter.Key())
450 txIndexIter := store.walletDB.IteratorPrefix([]byte(TxIndexPrefix))
451 defer txIndexIter.Release()
453 for txIndexIter.Next() {
454 batch.Delete(txIndexIter.Key())
456 if store.batch == nil {
461 // DeleteWalletUTXOs delete all txs in wallet
462 func (store *WalletStore) DeleteWalletUTXOs() {
463 batch := store.walletDB.NewBatch()
464 if store.batch != nil {
467 ruIter := store.walletDB.IteratorPrefix([]byte(UTXOPrefix))
468 defer ruIter.Release()
470 batch.Delete(ruIter.Key())
473 suIter := store.walletDB.IteratorPrefix([]byte(SUTXOPrefix))
474 defer suIter.Release()
476 batch.Delete(suIter.Key())
478 if store.batch == nil {
483 // GetAccountUTXOs get all account unspent outputs
484 func (store *WalletStore) GetAccountUTXOs(key string) [][]byte {
485 accountUtxoIter := store.walletDB.IteratorPrefix([]byte(key))
486 defer accountUtxoIter.Release()
488 rawUTXOs := make([][]byte, 0)
489 for accountUtxoIter.Next() {
490 utxo := accountUtxoIter.Value()
491 rawUTXOs = append(rawUTXOs, utxo)
496 // SetRecoveryStatus set recovery status
497 func (store *WalletStore) SetRecoveryStatus(recoveryKey, rawStatus []byte) {
498 if store.batch == nil {
499 store.walletDB.Set(recoveryKey, rawStatus)
501 store.batch.Set(recoveryKey, rawStatus)
505 // DeleteRecoveryStatus delete recovery status
506 func (store *WalletStore) DeleteRecoveryStatus(recoveryKey []byte) {
507 if store.batch == nil {
508 store.walletDB.Delete(recoveryKey)
510 store.batch.Delete(recoveryKey)
514 // GetRecoveryStatus delete recovery status
515 func (store *WalletStore) GetRecoveryStatus(recoveryKey []byte) []byte {
516 return store.walletDB.Get(recoveryKey)