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 byte = iota //UTXOPrefix is StandardUTXOKey prefix
22 sUTXOPrefix //SUTXOPrefix is ContractUTXOKey prefix
25 accountPrefix // AccountPrefix is account ID prefix
28 txPrefix //TxPrefix is wallet database transactions prefix
29 txIndexPrefix //TxIndexPrefix is wallet database tx index prefix
30 unconfirmedTxPrefix //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
31 globalTxIndexPrefix //GlobalTxIndexPrefix is wallet database global tx index prefix
40 UTXOPrefix = []byte{utxoPrefix, colon}
41 SUTXOPrefix = []byte{sUTXOPrefix, colon}
42 // ContractPrefix = []byte{contractPrefix, contractPrefix, colon}
43 ContractPrefix = "Contract:"
44 ContractIndexPrefix = []byte{contractIndexPrefix, colon}
45 AccountPrefix = []byte{accountPrefix, colon} // AccountPrefix is account ID prefix
46 AccountAliasPrefix = []byte{accountAliasPrefix, colon}
47 AccountIndexPrefix = []byte{accountIndexPrefix, colon}
48 TxPrefix = []byte{txPrefix, colon} //TxPrefix is wallet database transactions prefix
49 TxIndexPrefix = []byte{txIndexPrefix, colon} //TxIndexPrefix is wallet database tx index prefix
50 UnconfirmedTxPrefix = []byte{unconfirmedTxPrefix, colon} //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
51 GlobalTxIndexPrefix = []byte{globalTxIndexPrefix, colon} //GlobalTxIndexPrefix is wallet database global tx index prefix
52 WalletKey = []byte{walletKey}
53 MiningAddressKey = []byte{miningAddressKey}
54 CoinbaseAbKey = []byte{coinbaseAbKey}
55 RecoveryKey = []byte{recoveryKey}
60 // ErrFindAccount = errors.New("Failed to find account")
61 errAccntTxIDNotFound = errors.New("account TXID not found")
62 errGetAsset = errors.New("Failed to find asset definition")
65 func accountIndexKey(xpubs []chainkd.XPub) []byte {
68 cpy := append([]chainkd.XPub{}, xpubs[:]...)
69 sort.Sort(signers.SortKeys(cpy))
70 for _, xpub := range cpy {
71 xPubs = append(xPubs, xpub[:]...)
73 sha3pool.Sum256(hash[:], xPubs)
74 return append([]byte(AccountIndexPrefix), hash[:]...)
77 func Bip44ContractIndexKey(accountID string, change bool) []byte {
78 key := append([]byte(ContractIndexPrefix), accountID...)
80 return append(key, []byte{1}...)
82 return append(key, []byte{0}...)
85 // ContractKey account control promgram store prefix
86 func ContractKey(hash bc.Hash) []byte {
88 // return append([]byte(ContractPrefix), []byte(h)...)
89 return append([]byte(ContractPrefix), hash.Bytes()...)
92 // AccountIDKey account id store prefix
93 func AccountIDKey(accountID string) []byte {
94 return append([]byte(AccountPrefix), []byte(accountID)...)
97 // StandardUTXOKey makes an account unspent outputs key to store
98 func StandardUTXOKey(id bc.Hash) []byte {
100 return append(UTXOPrefix, []byte(name)...)
103 // ContractUTXOKey makes a smart contract unspent outputs key to store
104 func ContractUTXOKey(id bc.Hash) []byte {
106 return append(SUTXOPrefix, []byte(name)...)
109 func calcDeleteKey(blockHeight uint64) []byte {
110 return []byte(fmt.Sprintf("%s%016x", TxPrefix, blockHeight))
113 func calcTxIndexKey(txID string) []byte {
114 return append(TxIndexPrefix, []byte(txID)...)
117 func calcAnnotatedKey(formatKey string) []byte {
118 return append(TxPrefix, []byte(formatKey)...)
121 func calcUnconfirmedTxKey(formatKey string) []byte {
122 return append(UnconfirmedTxPrefix, []byte(formatKey)...)
125 func calcGlobalTxIndexKey(txID string) []byte {
126 return append(GlobalTxIndexPrefix, []byte(txID)...)
129 func CalcGlobalTxIndex(blockHash *bc.Hash, position uint64) []byte {
130 txIdx := make([]byte, 40)
131 copy(txIdx[:32], blockHash.Bytes())
132 binary.BigEndian.PutUint64(txIdx[32:], position)
136 func formatKey(blockHeight uint64, position uint32) string {
137 return fmt.Sprintf("%016x%08x", blockHeight, position)
140 func contractIndexKey(accountID string) []byte {
141 return append([]byte(ContractIndexPrefix), []byte(accountID)...)
144 func accountAliasKey(name string) []byte {
145 return append([]byte(AccountAliasPrefix), []byte(name)...)
148 // WalletStore store wallet using leveldb
149 type WalletStore struct {
154 // NewWalletStore create new WalletStore struct
155 func NewWalletStore(db dbm.DB) *WalletStore {
162 // InitBatch initial batch
163 func (store *WalletStore) InitBatch() error {
164 if store.batch != nil {
165 return errors.New("WalletStore initail fail, store batch is not nil.")
167 store.batch = store.walletDB.NewBatch()
171 // CommitBatch commit batch
172 func (store *WalletStore) CommitBatch() error {
173 if store.batch == nil {
174 return errors.New("WalletStore commit fail, store batch is nil.")
181 // DeleteContractUTXO delete contract utxo by outputID
182 func (store *WalletStore) DeleteContractUTXO(outputID bc.Hash) {
183 if store.batch == nil {
184 store.walletDB.Delete(ContractUTXOKey(outputID))
186 store.batch.Delete(ContractUTXOKey(outputID))
190 // DeleteRecoveryStatus delete recovery status
191 func (store *WalletStore) DeleteRecoveryStatus() {
192 if store.batch == nil {
193 store.walletDB.Delete(RecoveryKey)
195 store.batch.Delete(RecoveryKey)
199 // DeleteStardardUTXO delete stardard utxo by outputID
200 func (store *WalletStore) DeleteStardardUTXO(outputID bc.Hash) {
201 if store.batch == nil {
202 store.walletDB.Delete(StandardUTXOKey(outputID))
204 store.batch.Delete(StandardUTXOKey(outputID))
208 // DeleteTransactions delete transactions when orphan block rollback
209 func (store *WalletStore) DeleteTransactions(height uint64) {
210 batch := store.walletDB.NewBatch()
211 if store.batch != nil {
214 txIter := store.walletDB.IteratorPrefix(calcDeleteKey(height))
215 defer txIter.Release()
217 tmpTx := query.AnnotatedTx{}
219 if err := json.Unmarshal(txIter.Value(), &tmpTx); err == nil {
220 batch.Delete(calcTxIndexKey(tmpTx.ID.String()))
222 batch.Delete(txIter.Key())
224 if store.batch == nil {
229 // DeleteUnconfirmedTransaction delete unconfirmed tx by txID
230 func (store *WalletStore) DeleteUnconfirmedTransaction(txID string) {
231 if store.batch == nil {
232 store.walletDB.Delete(calcUnconfirmedTxKey(txID))
234 store.batch.Delete(calcUnconfirmedTxKey(txID))
238 // DeleteWalletTransactions delete all txs in wallet
239 func (store *WalletStore) DeleteWalletTransactions() {
240 batch := store.walletDB.NewBatch()
241 if store.batch != nil {
244 txIter := store.walletDB.IteratorPrefix([]byte(TxPrefix))
245 defer txIter.Release()
248 batch.Delete(txIter.Key())
251 txIndexIter := store.walletDB.IteratorPrefix([]byte(TxIndexPrefix))
252 defer txIndexIter.Release()
254 for txIndexIter.Next() {
255 batch.Delete(txIndexIter.Key())
257 if store.batch == nil {
262 // DeleteWalletUTXOs delete all txs in wallet
263 func (store *WalletStore) DeleteWalletUTXOs() {
264 batch := store.walletDB.NewBatch()
265 if store.batch != nil {
268 ruIter := store.walletDB.IteratorPrefix([]byte(UTXOPrefix))
269 defer ruIter.Release()
271 batch.Delete(ruIter.Key())
274 suIter := store.walletDB.IteratorPrefix([]byte(SUTXOPrefix))
275 defer suIter.Release()
277 batch.Delete(suIter.Key())
279 if store.batch == nil {
284 // GetAccount get account value by account ID
285 func (store *WalletStore) GetAccount(accountID string) (*acc.Account, error) {
286 rawAccount := store.walletDB.Get(AccountIDKey(accountID))
287 if rawAccount == nil {
288 return nil, fmt.Errorf("failed get account, accountID: %s ", accountID)
290 account := new(acc.Account)
291 if err := json.Unmarshal(rawAccount, account); err != nil {
297 // GetAsset get asset by assetID
298 func (store *WalletStore) GetAsset(assetID *bc.AssetID) (*asset.Asset, error) {
299 definitionByte := store.walletDB.Get(asset.ExtAssetKey(assetID))
300 if definitionByte == nil {
301 return nil, errGetAsset
303 definitionMap := make(map[string]interface{})
304 if err := json.Unmarshal(definitionByte, &definitionMap); err != nil {
307 alias := assetID.String()
308 externalAsset := &asset.Asset{
311 DefinitionMap: definitionMap,
312 RawDefinitionByte: definitionByte,
314 return externalAsset, nil
317 // GetControlProgram get raw program by hash
318 func (store *WalletStore) GetControlProgram(hash bc.Hash) (*acc.CtrlProgram, error) {
319 rawProgram := store.walletDB.Get(ContractKey(hash))
320 if rawProgram == nil {
321 return nil, fmt.Errorf("failed get account control program:%x ", hash)
323 accountCP := new(acc.CtrlProgram)
324 if err := json.Unmarshal(rawProgram, &accountCP); err != nil {
327 return accountCP, nil
330 // GetGlobalTransactionIndex get global tx by txID
331 func (store *WalletStore) GetGlobalTransactionIndex(txID string) []byte {
332 return store.walletDB.Get(calcGlobalTxIndexKey(txID))
335 // GetStandardUTXO get standard utxo by id
336 func (store *WalletStore) GetStandardUTXO(outid bc.Hash) (*acc.UTXO, error) {
337 rawUTXO := store.walletDB.Get(StandardUTXOKey(outid))
339 return nil, fmt.Errorf("failed get standard UTXO, outputID: %s ", outid.String())
341 UTXO := new(acc.UTXO)
342 if err := json.Unmarshal(rawUTXO, UTXO); err != nil {
348 // GetTransaction get tx by txid
349 func (store *WalletStore) GetTransaction(txID string) (*query.AnnotatedTx, error) {
350 formatKey := store.walletDB.Get(calcTxIndexKey(txID))
351 if formatKey == nil {
352 return nil, errAccntTxIDNotFound
354 rawTx := store.walletDB.Get(calcAnnotatedKey(string(formatKey)))
355 tx := new(query.AnnotatedTx)
356 if err := json.Unmarshal(rawTx, tx); err != nil {
362 // GetUnconfirmedTransaction get unconfirmed tx by txID
363 func (store *WalletStore) GetUnconfirmedTransaction(txID string) (*query.AnnotatedTx, error) {
364 rawUnconfirmedTx := store.walletDB.Get(calcUnconfirmedTxKey(txID))
365 if rawUnconfirmedTx == nil {
366 return nil, fmt.Errorf("failed get unconfirmed tx, txID: %s ", txID)
368 tx := new(query.AnnotatedTx)
369 if err := json.Unmarshal(rawUnconfirmedTx, tx); err != nil {
375 // GetRecoveryStatus delete recovery status
376 func (store *WalletStore) GetRecoveryStatus(recoveryKey []byte) []byte {
377 return store.walletDB.Get(recoveryKey)
380 // GetWalletInfo get wallet information
381 func (store *WalletStore) GetWalletInfo() []byte {
382 return store.walletDB.Get([]byte(WalletKey))
385 // ListAccountUTXOs get all account unspent outputs
386 func (store *WalletStore) ListAccountUTXOs(key string) ([]*acc.UTXO, error) {
387 accountUtxoIter := store.walletDB.IteratorPrefix([]byte(key))
388 defer accountUtxoIter.Release()
390 confirmedUTXOs := []*acc.UTXO{}
391 for accountUtxoIter.Next() {
392 utxo := new(acc.UTXO)
393 if err := json.Unmarshal(accountUtxoIter.Value(), utxo); err != nil {
396 confirmedUTXOs = append(confirmedUTXOs, utxo)
398 return confirmedUTXOs, nil
401 func (store *WalletStore) ListTransactions(accountID string, StartTxID string, count uint, unconfirmed bool) ([]*query.AnnotatedTx, error) {
402 annotatedTxs := []*query.AnnotatedTx{}
408 startKey = calcUnconfirmedTxKey(StartTxID)
410 formatKey := store.walletDB.Get(calcTxIndexKey(StartTxID))
411 if formatKey == nil {
412 return nil, errAccntTxIDNotFound
414 startKey = calcAnnotatedKey(string(formatKey))
419 preFix = UnconfirmedTxPrefix
422 itr := store.walletDB.IteratorPrefixWithStart([]byte(preFix), startKey)
425 for txNum := count; itr.Next() && txNum > 0; txNum-- {
426 annotatedTx := new(query.AnnotatedTx)
427 if err := json.Unmarshal(itr.Value(), &annotatedTx); err != nil {
430 annotatedTxs = append(annotatedTxs, annotatedTx)
433 return annotatedTxs, nil
436 // ListUnconfirmedTransactions get all unconfirmed txs
437 func (store *WalletStore) ListUnconfirmedTransactions() ([]*query.AnnotatedTx, error) {
438 annotatedTxs := []*query.AnnotatedTx{}
439 txIter := store.walletDB.IteratorPrefix([]byte(UnconfirmedTxPrefix))
440 defer txIter.Release()
443 annotatedTx := &query.AnnotatedTx{}
444 if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
447 annotatedTxs = append(annotatedTxs, annotatedTx)
449 return annotatedTxs, nil
452 // SetAssetDefinition set assetID and definition
453 func (store *WalletStore) SetAssetDefinition(assetID *bc.AssetID, definition []byte) {
454 if store.batch == nil {
455 store.walletDB.Set(asset.ExtAssetKey(assetID), definition)
457 store.batch.Set(asset.ExtAssetKey(assetID), definition)
461 // SetContractUTXO set standard utxo
462 func (store *WalletStore) SetContractUTXO(outputID bc.Hash, utxo *acc.UTXO) error {
463 data, err := json.Marshal(utxo)
467 if store.batch == nil {
468 store.walletDB.Set(ContractUTXOKey(outputID), data)
470 store.batch.Set(ContractUTXOKey(outputID), data)
475 // SetGlobalTransactionIndex set global tx index by blockhash and position
476 func (store *WalletStore) SetGlobalTransactionIndex(globalTxID string, blockHash *bc.Hash, position uint64) {
477 if store.batch == nil {
478 store.walletDB.Set(calcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
480 store.batch.Set(calcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
484 // SetRecoveryStatus set recovery status
485 func (store *WalletStore) SetRecoveryStatus(recoveryKey, rawStatus []byte) {
486 if store.batch == nil {
487 store.walletDB.Set(recoveryKey, rawStatus)
489 store.batch.Set(recoveryKey, rawStatus)
493 // SetStandardUTXO set standard utxo
494 func (store *WalletStore) SetStandardUTXO(outputID bc.Hash, utxo *acc.UTXO) error {
495 data, err := json.Marshal(utxo)
499 if store.batch == nil {
500 store.walletDB.Set(StandardUTXOKey(outputID), data)
502 store.batch.Set(StandardUTXOKey(outputID), data)
507 // SetTransaction set raw transaction by block height and tx position
508 func (store *WalletStore) SetTransaction(height uint64, tx *query.AnnotatedTx) error {
509 batch := store.walletDB.NewBatch()
510 if store.batch != nil {
514 rawTx, err := json.Marshal(tx)
518 batch.Set(calcAnnotatedKey(formatKey(height, tx.Position)), rawTx)
519 batch.Set(calcTxIndexKey(tx.ID.String()), []byte(formatKey(height, tx.Position)))
521 if store.batch == nil {
527 // SetUnconfirmedTransaction set unconfirmed tx by txID
528 func (store *WalletStore) SetUnconfirmedTransaction(txID string, tx *query.AnnotatedTx) error {
529 rawTx, err := json.Marshal(tx)
533 if store.batch == nil {
534 store.walletDB.Set(calcUnconfirmedTxKey(txID), rawTx)
536 store.batch.Set(calcUnconfirmedTxKey(txID), rawTx)
541 // SetWalletInfo get wallet information
542 func (store *WalletStore) SetWalletInfo(rawWallet []byte) {
543 if store.batch == nil {
544 store.walletDB.Set([]byte(WalletKey), rawWallet)
546 store.batch.Set([]byte(WalletKey), rawWallet)