9 "github.com/vapor/asset"
10 "github.com/vapor/blockchain/query"
11 "github.com/vapor/blockchain/signers"
12 "github.com/vapor/common"
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
58 UTXOPrefix = []byte{utxoPrefix, colon}
59 SUTXOPrefix = []byte{sUTXOPrefix, colon}
60 // ContractPrefix = []byte{contractPrefix, colon}
61 ContractIndexPrefix = []byte{contractIndexPrefix, colon}
62 AccountPrefix = []byte{accountPrefix, colon} // AccountPrefix is account ID prefix
63 AccountAliasPrefix = []byte{accountAliasPrefix, colon}
64 AccountIndexPrefix = []byte{accountIndexPrefix, colon}
65 TxPrefix = []byte{txPrefix, colon} //TxPrefix is wallet database transactions prefix
66 TxIndexPrefix = []byte{txIndexPrefix, colon} //TxIndexPrefix is wallet database tx index prefix
67 UnconfirmedTxPrefix = []byte{unconfirmedTxPrefix, colon} //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
68 GlobalTxIndexPrefix = []byte{globalTxIndexPrefix, colon} //GlobalTxIndexPrefix is wallet database global tx index prefix
69 WalletKey = []byte{walletKey}
70 MiningAddressKey = []byte{miningAddressKey}
71 CoinbaseAbKey = []byte{coinbaseAbKey}
76 // ErrFindAccount = errors.New("Failed to find account")
77 errAccntTxIDNotFound = errors.New("account TXID not found")
78 errGetAssetDefinition = errors.New("Failed to find asset definition")
81 func accountIndexKey(xpubs []chainkd.XPub) []byte {
84 cpy := append([]chainkd.XPub{}, xpubs[:]...)
85 sort.Sort(signers.SortKeys(cpy))
86 for _, xpub := range cpy {
87 xPubs = append(xPubs, xpub[:]...)
89 sha3pool.Sum256(hash[:], xPubs)
90 return append([]byte(AccountIndexPrefix), hash[:]...)
93 func Bip44ContractIndexKey(accountID string, change bool) []byte {
94 key := append([]byte(ContractIndexPrefix), accountID...)
96 return append(key, []byte{1}...)
98 return append(key, []byte{0}...)
101 // ContractKey account control promgram store prefix
102 func ContractKey(hash common.Hash) []byte {
104 // return append([]byte(ContractPrefix), []byte(h)...)
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() {
180 if store.batch == nil {
181 store.batch = store.walletDB.NewBatch()
185 // CommitBatch commit batch
186 func (store *WalletStore) CommitBatch() {
187 if store.batch != nil {
193 // GetAssetDefinition get asset definition by assetiD
194 func (store *WalletStore) GetAssetDefinition(assetID *bc.AssetID) (*asset.Asset, error) {
195 definitionByte := store.walletDB.Get(asset.ExtAssetKey(assetID))
196 if definitionByte == nil {
197 return nil, errGetAssetDefinition
199 definitionMap := make(map[string]interface{})
200 if err := json.Unmarshal(definitionByte, &definitionMap); err != nil {
203 alias := assetID.String()
204 externalAsset := &asset.Asset{
207 DefinitionMap: definitionMap,
208 RawDefinitionByte: definitionByte,
210 return externalAsset, nil
213 // SetAssetDefinition set assetID and definition
214 func (store *WalletStore) SetAssetDefinition(assetID *bc.AssetID, definition []byte) {
215 if store.batch == nil {
216 store.walletDB.Set(asset.ExtAssetKey(assetID), definition)
218 store.batch.Set(asset.ExtAssetKey(assetID), definition)
222 // // GetRawProgram get raw program by hash
223 // func (store *WalletStore) GetRawProgram(hash common.Hash) ([]byte, error) {
224 // rawProgram := store.walletDB.Get(ContractKey(hash))
225 // if rawProgram == nil {
226 // return nil, fmt.Errorf("failed get account control program:%x ", hash)
229 // accountCP := new(account.CtrlProgram)
230 // if err := json.Unmarshal(rawProgram, &accountCP); err != nil {
233 // return accountCP, nil
236 // GetRawProgram get raw program by hash
237 func (store *WalletStore) GetRawProgram(hash common.Hash) []byte {
238 return store.walletDB.Get(ContractKey(hash))
241 // GetAccountByAccountID get account value by account ID
242 func (store *WalletStore) GetAccountByAccountID(accountID string) []byte {
243 return store.walletDB.Get(AccountIDKey(accountID))
246 // DeleteTransactions delete transactions when orphan block rollback
247 func (store *WalletStore) DeleteTransactions(height uint64) {
248 batch := store.walletDB.NewBatch()
249 if store.batch != nil {
252 txIter := store.walletDB.IteratorPrefix(calcDeleteKey(height))
253 defer txIter.Release()
255 tmpTx := query.AnnotatedTx{}
257 if err := json.Unmarshal(txIter.Value(), &tmpTx); err == nil {
258 batch.Delete(calcTxIndexKey(tmpTx.ID.String()))
260 batch.Delete(txIter.Key())
262 if store.batch == nil {
267 // SetTransaction set raw transaction by block height and tx position
268 func (store *WalletStore) SetTransaction(height uint64, position uint32, txID string, rawTx []byte) {
269 batch := store.walletDB.NewBatch()
270 if store.batch != nil {
274 batch.Set(calcAnnotatedKey(formatKey(height, position)), rawTx)
275 batch.Set(calcTxIndexKey(txID), []byte(formatKey(height, position)))
277 if store.batch == nil {
282 // DeleteUnconfirmedTransaction delete unconfirmed tx by txID
283 func (store *WalletStore) DeleteUnconfirmedTransaction(txID string) {
284 if store.batch == nil {
285 store.walletDB.Delete(calcUnconfirmedTxKey(txID))
287 store.batch.Delete(calcUnconfirmedTxKey(txID))
291 // SetGlobalTransactionIndex set global tx index by blockhash and position
292 func (store *WalletStore) SetGlobalTransactionIndex(globalTxID string, blockHash *bc.Hash, position uint64) {
293 if store.batch == nil {
294 store.walletDB.Set(calcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
296 store.batch.Set(calcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
300 // GetStandardUTXO get standard utxo by id
301 func (store *WalletStore) GetStandardUTXO(outid bc.Hash) []byte {
302 return store.walletDB.Get(StandardUTXOKey(outid))
305 // GetTransaction get tx by tx index
306 func (store *WalletStore) GetTransaction(txID string) ([]byte, error) {
307 formatKey := store.walletDB.Get(calcTxIndexKey(txID))
308 if formatKey == nil {
309 return nil, errAccntTxIDNotFound
311 txInfo := store.walletDB.Get(calcAnnotatedKey(string(formatKey)))
315 // GetGlobalTransaction get global tx by txID
316 func (store *WalletStore) GetGlobalTransaction(txID string) []byte {
317 return store.walletDB.Get(calcGlobalTxIndexKey(txID))
320 // GetTransactions get all walletDB transactions
321 func (store *WalletStore) GetTransactions() ([]*query.AnnotatedTx, error) {
322 annotatedTxs := []*query.AnnotatedTx{}
324 txIter := store.walletDB.IteratorPrefix([]byte(TxPrefix))
325 defer txIter.Release()
327 annotatedTx := &query.AnnotatedTx{}
328 if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
331 annotatedTxs = append(annotatedTxs, annotatedTx)
334 return annotatedTxs, nil
337 // GetUnconfirmedTransactions get all unconfirmed txs
338 func (store *WalletStore) GetUnconfirmedTransactions() ([]*query.AnnotatedTx, error) {
339 annotatedTxs := []*query.AnnotatedTx{}
340 txIter := store.walletDB.IteratorPrefix([]byte(UnconfirmedTxPrefix))
341 defer txIter.Release()
344 annotatedTx := &query.AnnotatedTx{}
345 if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
348 annotatedTxs = append(annotatedTxs, annotatedTx)
350 return annotatedTxs, nil
353 // GetUnconfirmedTransaction get unconfirmed tx by txID
354 func (store *WalletStore) GetUnconfirmedTransaction(txID string) []byte {
355 return store.walletDB.Get(calcUnconfirmedTxKey(txID))
358 // SetUnconfirmedTransaction set unconfirmed tx by txID
359 func (store *WalletStore) SetUnconfirmedTransaction(txID string, rawTx []byte) {
360 if store.batch == nil {
361 store.walletDB.Set(calcUnconfirmedTxKey(txID), rawTx)
363 store.batch.Set(calcUnconfirmedTxKey(txID), rawTx)
367 // DeleteStardardUTXO delete stardard utxo by outputID
368 func (store *WalletStore) DeleteStardardUTXO(outputID bc.Hash) {
369 if store.batch == nil {
370 store.walletDB.Delete(StandardUTXOKey(outputID))
372 store.batch.Delete(StandardUTXOKey(outputID))
376 // DeleteContractUTXO delete contract utxo by outputID
377 func (store *WalletStore) DeleteContractUTXO(outputID bc.Hash) {
378 if store.batch == nil {
379 store.walletDB.Delete(ContractUTXOKey(outputID))
381 store.batch.Delete(ContractUTXOKey(outputID))
385 // SetStandardUTXO set standard utxo
386 func (store *WalletStore) SetStandardUTXO(outputID bc.Hash, data []byte) {
387 if store.batch == nil {
388 store.walletDB.Set(StandardUTXOKey(outputID), data)
390 store.batch.Set(StandardUTXOKey(outputID), data)
394 // SetContractUTXO set standard utxo
395 func (store *WalletStore) SetContractUTXO(outputID bc.Hash, data []byte) {
396 if store.batch == nil {
397 store.walletDB.Set(ContractUTXOKey(outputID), data)
399 store.batch.Set(ContractUTXOKey(outputID), data)
403 // GetWalletInfo get wallet information
404 func (store *WalletStore) GetWalletInfo() []byte {
405 return store.walletDB.Get([]byte(WalletKey))
408 // SetWalletInfo get wallet information
409 func (store *WalletStore) SetWalletInfo(rawWallet []byte) {
410 if store.batch == nil {
411 store.walletDB.Set([]byte(WalletKey), rawWallet)
413 store.batch.Set([]byte(WalletKey), rawWallet)
417 // DeleteWalletTransactions delete all txs in wallet
418 func (store *WalletStore) DeleteWalletTransactions() {
419 batch := store.walletDB.NewBatch()
420 if store.batch != nil {
423 txIter := store.walletDB.IteratorPrefix([]byte(TxPrefix))
424 defer txIter.Release()
427 batch.Delete(txIter.Key())
430 txIndexIter := store.walletDB.IteratorPrefix([]byte(TxIndexPrefix))
431 defer txIndexIter.Release()
433 for txIndexIter.Next() {
434 batch.Delete(txIndexIter.Key())
436 if store.batch == nil {
441 // DeleteWalletUTXOs delete all txs in wallet
442 func (store *WalletStore) DeleteWalletUTXOs() {
443 batch := store.walletDB.NewBatch()
444 if store.batch != nil {
447 ruIter := store.walletDB.IteratorPrefix([]byte(UTXOPrefix))
448 defer ruIter.Release()
450 batch.Delete(ruIter.Key())
453 suIter := store.walletDB.IteratorPrefix([]byte(SUTXOPrefix))
454 defer suIter.Release()
456 batch.Delete(suIter.Key())
458 if store.batch == nil {
463 // GetAccountUTXOs get all account unspent outputs
464 func (store *WalletStore) GetAccountUTXOs(key string) [][]byte {
465 accountUtxoIter := store.walletDB.IteratorPrefix([]byte(key))
466 defer accountUtxoIter.Release()
468 rawUTXOs := make([][]byte, 0)
469 for accountUtxoIter.Next() {
470 utxo := accountUtxoIter.Value()
471 rawUTXOs = append(rawUTXOs, utxo)
476 // SetRecoveryStatus set recovery status
477 func (store *WalletStore) SetRecoveryStatus(recoveryKey, rawStatus []byte) {
478 if store.batch == nil {
479 store.walletDB.Set(recoveryKey, rawStatus)
481 store.batch.Set(recoveryKey, rawStatus)
485 // DeleteRecoveryStatus delete recovery status
486 func (store *WalletStore) DeleteRecoveryStatus(recoveryKey []byte) {
487 if store.batch == nil {
488 store.walletDB.Delete(recoveryKey)
490 store.batch.Delete(recoveryKey)
494 // GetRecoveryStatus delete recovery status
495 func (store *WalletStore) GetRecoveryStatus(recoveryKey []byte) []byte {
496 return store.walletDB.Get(recoveryKey)