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/crypto/ed25519/chainkd"
14 "github.com/vapor/crypto/sha3pool"
15 dbm "github.com/vapor/database/leveldb"
16 "github.com/vapor/errors"
17 "github.com/vapor/protocol/bc"
21 // UTXOPrefix = "ACU:" //UTXOPrefix is StandardUTXOKey prefix
22 // SUTXOPrefix = "SCU:" //SUTXOPrefix is ContractUTXOKey prefix
24 ContractPrefix = "Contract:"
26 // ContractIndexPrefix = "ContractIndex:"
27 // AccountPrefix = "Account:" // AccountPrefix is account ID prefix
28 // AccountAliasPrefix = "AccountAlias:"
29 // AccountIndexPrefix = "AccountIndex:"
30 // TxPrefix = "TXS:" //TxPrefix is wallet database transactions prefix
31 // TxIndexPrefix = "TID:" //TxIndexPrefix is wallet database tx index prefix
32 // UnconfirmedTxPrefix = "UTXS:" //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
33 // GlobalTxIndexPrefix = "GTID:" //GlobalTxIndexPrefix is wallet database global tx index prefix
34 // WalletKey = "WalletInfo"
35 // MiningAddressKey = "MiningAddress"
36 // CoinbaseAbKey = "CoinbaseArbitrary"
40 utxoPrefix byte = iota //UTXOPrefix is StandardUTXOKey prefix
41 sUTXOPrefix //SUTXOPrefix is ContractUTXOKey prefix
44 accountPrefix // AccountPrefix is account ID prefix
47 txPrefix //TxPrefix is wallet database transactions prefix
48 txIndexPrefix //TxIndexPrefix is wallet database tx index prefix
49 unconfirmedTxPrefix //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
50 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}
73 RecoveryKey = []byte{recoveryKey}
78 // ErrFindAccount = errors.New("Failed to find account")
79 errAccntTxIDNotFound = errors.New("account TXID not found")
80 errGetAssetDefinition = errors.New("Failed to find asset definition")
83 func accountIndexKey(xpubs []chainkd.XPub) []byte {
86 cpy := append([]chainkd.XPub{}, xpubs[:]...)
87 sort.Sort(signers.SortKeys(cpy))
88 for _, xpub := range cpy {
89 xPubs = append(xPubs, xpub[:]...)
91 sha3pool.Sum256(hash[:], xPubs)
92 return append([]byte(AccountIndexPrefix), hash[:]...)
95 func Bip44ContractIndexKey(accountID string, change bool) []byte {
96 key := append([]byte(ContractIndexPrefix), accountID...)
98 return append(key, []byte{1}...)
100 return append(key, []byte{0}...)
103 // ContractKey account control promgram store prefix
104 func ContractKey(hash bc.Hash) []byte {
105 return append([]byte(ContractPrefix), hash.Bytes()...)
108 // AccountIDKey account id store prefix
109 func AccountIDKey(accountID string) []byte {
110 return append([]byte(AccountPrefix), []byte(accountID)...)
113 // StandardUTXOKey makes an account unspent outputs key to store
114 func StandardUTXOKey(id bc.Hash) []byte {
116 return append(UTXOPrefix, []byte(name)...)
119 // ContractUTXOKey makes a smart contract unspent outputs key to store
120 func ContractUTXOKey(id bc.Hash) []byte {
122 return append(SUTXOPrefix, []byte(name)...)
125 func calcDeleteKey(blockHeight uint64) []byte {
126 return []byte(fmt.Sprintf("%s%016x", TxPrefix, blockHeight))
129 func calcTxIndexKey(txID string) []byte {
130 return append(TxIndexPrefix, []byte(txID)...)
133 func calcAnnotatedKey(formatKey string) []byte {
134 return append(TxPrefix, []byte(formatKey)...)
137 func calcUnconfirmedTxKey(formatKey string) []byte {
138 return append(UnconfirmedTxPrefix, []byte(formatKey)...)
141 func calcGlobalTxIndexKey(txID string) []byte {
142 return append(GlobalTxIndexPrefix, []byte(txID)...)
145 func CalcGlobalTxIndex(blockHash *bc.Hash, position uint64) []byte {
146 txIdx := make([]byte, 40)
147 copy(txIdx[:32], blockHash.Bytes())
148 binary.BigEndian.PutUint64(txIdx[32:], position)
152 func formatKey(blockHeight uint64, position uint32) string {
153 return fmt.Sprintf("%016x%08x", blockHeight, position)
156 func contractIndexKey(accountID string) []byte {
157 return append([]byte(ContractIndexPrefix), []byte(accountID)...)
160 func accountAliasKey(name string) []byte {
161 return append([]byte(AccountAliasPrefix), []byte(name)...)
164 // WalletStore store wallet using leveldb
165 type WalletStore struct {
170 // NewWalletStore create new WalletStore struct
171 func NewWalletStore(db dbm.DB) *WalletStore {
178 // InitBatch initial batch
179 func (store *WalletStore) InitBatch() error {
180 if store.batch != nil {
181 return errors.New("WalletStore initail fail, store batch is not nil.")
183 store.batch = store.walletDB.NewBatch()
187 // CommitBatch commit batch
188 func (store *WalletStore) CommitBatch() error {
189 if store.batch == nil {
190 return errors.New("WalletStore commit fail, store batch is nil.")
197 // DeleteContractUTXO delete contract utxo by outputID
198 func (store *WalletStore) DeleteContractUTXO(outputID bc.Hash) {
199 if store.batch == nil {
200 store.walletDB.Delete(ContractUTXOKey(outputID))
202 store.batch.Delete(ContractUTXOKey(outputID))
206 // DeleteRecoveryStatus delete recovery status
207 func (store *WalletStore) DeleteRecoveryStatus() {
208 if store.batch == nil {
209 store.walletDB.Delete(RecoveryKey)
211 store.batch.Delete(RecoveryKey)
215 // DeleteStardardUTXO delete stardard utxo by outputID
216 func (store *WalletStore) DeleteStardardUTXO(outputID bc.Hash) {
217 if store.batch == nil {
218 store.walletDB.Delete(StandardUTXOKey(outputID))
220 store.batch.Delete(StandardUTXOKey(outputID))
224 // DeleteTransactions delete transactions when orphan block rollback
225 func (store *WalletStore) DeleteTransactions(height uint64) {
226 batch := store.walletDB.NewBatch()
227 if store.batch != nil {
230 txIter := store.walletDB.IteratorPrefix(calcDeleteKey(height))
231 defer txIter.Release()
233 tmpTx := query.AnnotatedTx{}
235 if err := json.Unmarshal(txIter.Value(), &tmpTx); err == nil {
236 batch.Delete(calcTxIndexKey(tmpTx.ID.String()))
238 batch.Delete(txIter.Key())
240 if store.batch == nil {
245 // DeleteUnconfirmedTransaction delete unconfirmed tx by txID
246 func (store *WalletStore) DeleteUnconfirmedTransaction(txID string) {
247 if store.batch == nil {
248 store.walletDB.Delete(calcUnconfirmedTxKey(txID))
250 store.batch.Delete(calcUnconfirmedTxKey(txID))
254 // DeleteWalletTransactions delete all txs in wallet
255 func (store *WalletStore) DeleteWalletTransactions() {
256 batch := store.walletDB.NewBatch()
257 if store.batch != nil {
260 txIter := store.walletDB.IteratorPrefix([]byte(TxPrefix))
261 defer txIter.Release()
264 batch.Delete(txIter.Key())
267 txIndexIter := store.walletDB.IteratorPrefix([]byte(TxIndexPrefix))
268 defer txIndexIter.Release()
270 for txIndexIter.Next() {
271 batch.Delete(txIndexIter.Key())
273 if store.batch == nil {
278 // DeleteWalletUTXOs delete all txs in wallet
279 func (store *WalletStore) DeleteWalletUTXOs() {
280 batch := store.walletDB.NewBatch()
281 if store.batch != nil {
284 ruIter := store.walletDB.IteratorPrefix([]byte(UTXOPrefix))
285 defer ruIter.Release()
287 batch.Delete(ruIter.Key())
290 suIter := store.walletDB.IteratorPrefix([]byte(SUTXOPrefix))
291 defer suIter.Release()
293 batch.Delete(suIter.Key())
295 if store.batch == nil {
300 // GetAccountByID get account value by account ID
301 func (store *WalletStore) GetAccountByID(accountID string) (*acc.Account, error) {
302 rawAccount := store.walletDB.Get(AccountIDKey(accountID))
303 if rawAccount == nil {
304 return nil, fmt.Errorf("failed get account, accountID: %s ", accountID)
306 account := new(acc.Account)
307 if err := json.Unmarshal(rawAccount, account); err != nil {
313 // GetAssetDefinition get asset definition by assetiD
314 func (store *WalletStore) GetAssetDefinition(assetID *bc.AssetID) (*asset.Asset, error) {
315 definitionByte := store.walletDB.Get(asset.ExtAssetKey(assetID))
316 if definitionByte == nil {
317 return nil, errGetAssetDefinition
319 definitionMap := make(map[string]interface{})
320 if err := json.Unmarshal(definitionByte, &definitionMap); err != nil {
323 alias := assetID.String()
324 externalAsset := &asset.Asset{
327 DefinitionMap: definitionMap,
328 RawDefinitionByte: definitionByte,
330 return externalAsset, nil
333 // GetControlProgram get raw program by hash
334 func (store *WalletStore) GetControlProgram(hash bc.Hash) (*acc.CtrlProgram, error) {
335 rawProgram := store.walletDB.Get(ContractKey(hash))
336 if rawProgram == nil {
337 return nil, fmt.Errorf("failed get account control program:%x ", hash)
339 accountCP := new(acc.CtrlProgram)
340 if err := json.Unmarshal(rawProgram, &accountCP); err != nil {
343 return accountCP, nil
346 // GetGlobalTransactionIndex get global tx by txID
347 func (store *WalletStore) GetGlobalTransactionIndex(txID string) []byte {
348 return store.walletDB.Get(calcGlobalTxIndexKey(txID))
351 // GetStandardUTXO get standard utxo by id
352 func (store *WalletStore) GetStandardUTXO(outid bc.Hash) (*acc.UTXO, error) {
353 rawUTXO := store.walletDB.Get(StandardUTXOKey(outid))
355 return nil, fmt.Errorf("failed get standard UTXO, outputID: %s ", outid.String())
357 UTXO := new(acc.UTXO)
358 if err := json.Unmarshal(rawUTXO, UTXO); err != nil {
364 // GetTransaction get tx by txid
365 func (store *WalletStore) GetTransaction(txID string) (*query.AnnotatedTx, error) {
366 formatKey := store.walletDB.Get(calcTxIndexKey(txID))
367 if formatKey == nil {
368 return nil, errAccntTxIDNotFound
370 rawTx := store.walletDB.Get(calcAnnotatedKey(string(formatKey)))
371 tx := new(query.AnnotatedTx)
372 if err := json.Unmarshal(rawTx, tx); err != nil {
378 // GetUnconfirmedTransaction get unconfirmed tx by txID
379 func (store *WalletStore) GetUnconfirmedTransaction(txID string) (*query.AnnotatedTx, error) {
380 rawUnconfirmedTx := store.walletDB.Get(calcUnconfirmedTxKey(txID))
381 if rawUnconfirmedTx == nil {
382 return nil, fmt.Errorf("failed get unconfirmed tx, txID: %s ", txID)
384 tx := new(query.AnnotatedTx)
385 if err := json.Unmarshal(rawUnconfirmedTx, tx); err != nil {
391 // GetRecoveryStatus delete recovery status
392 func (store *WalletStore) GetRecoveryStatus(recoveryKey []byte) []byte {
393 return store.walletDB.Get(recoveryKey)
396 // GetWalletInfo get wallet information
397 func (store *WalletStore) GetWalletInfo() []byte {
398 return store.walletDB.Get([]byte(WalletKey))
401 // ListAccountUTXOs get all account unspent outputs
402 func (store *WalletStore) ListAccountUTXOs(key string) ([]*acc.UTXO, error) {
403 accountUtxoIter := store.walletDB.IteratorPrefix([]byte(key))
404 defer accountUtxoIter.Release()
406 confirmedUTXOs := []*acc.UTXO{}
407 for accountUtxoIter.Next() {
408 utxo := new(acc.UTXO)
409 if err := json.Unmarshal(accountUtxoIter.Value(), utxo); err != nil {
412 confirmedUTXOs = append(confirmedUTXOs, utxo)
414 return confirmedUTXOs, nil
417 // ListTransactions get all walletDB transactions
418 func (store *WalletStore) ListTransactions() ([]*query.AnnotatedTx, error) {
419 annotatedTxs := []*query.AnnotatedTx{}
421 txIter := store.walletDB.IteratorPrefix([]byte(TxPrefix))
422 defer txIter.Release()
424 annotatedTx := &query.AnnotatedTx{}
425 if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
428 annotatedTxs = append(annotatedTxs, annotatedTx)
431 return annotatedTxs, nil
434 // ListUnconfirmedTransactions get all unconfirmed txs
435 func (store *WalletStore) ListUnconfirmedTransactions() ([]*query.AnnotatedTx, error) {
436 annotatedTxs := []*query.AnnotatedTx{}
437 txIter := store.walletDB.IteratorPrefix([]byte(UnconfirmedTxPrefix))
438 defer txIter.Release()
441 annotatedTx := &query.AnnotatedTx{}
442 if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
445 annotatedTxs = append(annotatedTxs, annotatedTx)
447 return annotatedTxs, nil
450 // SetAssetDefinition set assetID and definition
451 func (store *WalletStore) SetAssetDefinition(assetID *bc.AssetID, definition []byte) {
452 if store.batch == nil {
453 store.walletDB.Set(asset.ExtAssetKey(assetID), definition)
455 store.batch.Set(asset.ExtAssetKey(assetID), definition)
459 // SetContractUTXO set standard utxo
460 func (store *WalletStore) SetContractUTXO(outputID bc.Hash, utxo *acc.UTXO) error {
461 data, err := json.Marshal(utxo)
465 if store.batch == nil {
466 store.walletDB.Set(ContractUTXOKey(outputID), data)
468 store.batch.Set(ContractUTXOKey(outputID), data)
473 // SetGlobalTransactionIndex set global tx index by blockhash and position
474 func (store *WalletStore) SetGlobalTransactionIndex(globalTxID string, blockHash *bc.Hash, position uint64) {
475 if store.batch == nil {
476 store.walletDB.Set(calcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
478 store.batch.Set(calcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
482 // SetRecoveryStatus set recovery status
483 func (store *WalletStore) SetRecoveryStatus(recoveryKey, rawStatus []byte) {
484 if store.batch == nil {
485 store.walletDB.Set(recoveryKey, rawStatus)
487 store.batch.Set(recoveryKey, rawStatus)
491 // SetStandardUTXO set standard utxo
492 func (store *WalletStore) SetStandardUTXO(outputID bc.Hash, utxo *acc.UTXO) error {
493 data, err := json.Marshal(utxo)
497 if store.batch == nil {
498 store.walletDB.Set(StandardUTXOKey(outputID), data)
500 store.batch.Set(StandardUTXOKey(outputID), data)
505 // SetTransaction set raw transaction by block height and tx position
506 func (store *WalletStore) SetTransaction(height uint64, tx *query.AnnotatedTx) error {
507 batch := store.walletDB.NewBatch()
508 if store.batch != nil {
512 rawTx, err := json.Marshal(tx)
516 batch.Set(calcAnnotatedKey(formatKey(height, tx.Position)), rawTx)
517 batch.Set(calcTxIndexKey(tx.ID.String()), []byte(formatKey(height, tx.Position)))
519 if store.batch == nil {
525 // SetUnconfirmedTransaction set unconfirmed tx by txID
526 func (store *WalletStore) SetUnconfirmedTransaction(txID string, tx *query.AnnotatedTx) error {
527 rawTx, err := json.Marshal(tx)
531 if store.batch == nil {
532 store.walletDB.Set(calcUnconfirmedTxKey(txID), rawTx)
534 store.batch.Set(calcUnconfirmedTxKey(txID), rawTx)
539 // SetWalletInfo get wallet information
540 func (store *WalletStore) SetWalletInfo(rawWallet []byte) {
541 if store.batch == nil {
542 store.walletDB.Set([]byte(WalletKey), rawWallet)
544 store.batch.Set([]byte(WalletKey), rawWallet)