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"
22 utxoPrefix byte = iota //UTXOPrefix is StandardUTXOKey prefix
23 sutxoPrefix //SUTXOPrefix is ContractUTXOKey prefix
26 accountPrefix // AccountPrefix is account ID prefix
29 txPrefix //TxPrefix is wallet database transactions prefix
30 txIndexPrefix //TxIndexPrefix is wallet database tx index prefix
31 unconfirmedTxPrefix //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
32 globalTxIndexPrefix //GlobalTxIndexPrefix is wallet database global tx index prefix
41 UTXOPrefix = []byte{utxoPrefix, colon}
42 SUTXOPrefix = []byte{sutxoPrefix, colon}
43 // ContractPrefix = []byte{contractPrefix, contractPrefix, colon}
44 ContractPrefix = "Contract:"
45 ContractIndexPrefix = []byte{contractIndexPrefix, colon}
46 AccountPrefix = []byte{accountPrefix, colon} // AccountPrefix is account ID prefix
47 AccountAliasPrefix = []byte{accountAliasPrefix, colon}
48 AccountIndexPrefix = []byte{accountIndexPrefix, colon}
49 TxPrefix = []byte{txPrefix, colon} //TxPrefix is wallet database transactions prefix
50 TxIndexPrefix = []byte{txIndexPrefix, colon} //TxIndexPrefix is wallet database tx index prefix
51 UnconfirmedTxPrefix = []byte{unconfirmedTxPrefix, colon} //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
52 GlobalTxIndexPrefix = []byte{globalTxIndexPrefix, colon} //GlobalTxIndexPrefix is wallet database global tx index prefix
53 WalletKey = []byte{walletKey}
54 MiningAddressKey = []byte{miningAddressKey}
55 CoinbaseAbKey = []byte{coinbaseAbKey}
56 RecoveryKey = []byte{recoveryKey}
61 // ErrFindAccount = errors.New("Failed to find account")
62 errAccntTxIDNotFound = errors.New("account TXID not found")
63 errGetAsset = errors.New("Failed to find asset definition")
66 func accountIndexKey(xpubs []chainkd.XPub) []byte {
69 cpy := append([]chainkd.XPub{}, xpubs[:]...)
70 sort.Sort(signers.SortKeys(cpy))
71 for _, xpub := range cpy {
72 xPubs = append(xPubs, xpub[:]...)
74 sha3pool.Sum256(hash[:], xPubs)
75 return append([]byte(AccountIndexPrefix), hash[:]...)
78 func Bip44ContractIndexKey(accountID string, change bool) []byte {
79 key := append([]byte(ContractIndexPrefix), accountID...)
81 return append(key, []byte{1}...)
83 return append(key, []byte{0}...)
86 // ContractKey account control promgram store prefix
87 func ContractKey(hash bc.Hash) []byte {
89 // return append([]byte(ContractPrefix), []byte(h)...)
90 return append([]byte(ContractPrefix), hash.Bytes()...)
93 // AccountIDKey account id store prefix
94 func AccountIDKey(accountID string) []byte {
95 return append([]byte(AccountPrefix), []byte(accountID)...)
98 // StandardUTXOKey makes an account unspent outputs key to store
99 func StandardUTXOKey(id bc.Hash) []byte {
101 return append(UTXOPrefix, []byte(name)...)
104 // ContractUTXOKey makes a smart contract unspent outputs key to store
105 func ContractUTXOKey(id bc.Hash) []byte {
107 return append(SUTXOPrefix, []byte(name)...)
110 func calcDeleteKey(blockHeight uint64) []byte {
111 return []byte(fmt.Sprintf("%s%016x", TxPrefix, blockHeight))
114 func calcTxIndexKey(txID string) []byte {
115 return append(TxIndexPrefix, []byte(txID)...)
118 func calcAnnotatedKey(formatKey string) []byte {
119 return append(TxPrefix, []byte(formatKey)...)
122 func calcUnconfirmedTxKey(formatKey string) []byte {
123 return append(UnconfirmedTxPrefix, []byte(formatKey)...)
126 func calcGlobalTxIndexKey(txID string) []byte {
127 return append(GlobalTxIndexPrefix, []byte(txID)...)
130 func CalcGlobalTxIndex(blockHash *bc.Hash, position uint64) []byte {
131 txIdx := make([]byte, 40)
132 copy(txIdx[:32], blockHash.Bytes())
133 binary.BigEndian.PutUint64(txIdx[32:], position)
137 func formatKey(blockHeight uint64, position uint32) string {
138 return fmt.Sprintf("%016x%08x", blockHeight, position)
141 func contractIndexKey(accountID string) []byte {
142 return append([]byte(ContractIndexPrefix), []byte(accountID)...)
145 func accountAliasKey(name string) []byte {
146 return append([]byte(AccountAliasPrefix), []byte(name)...)
149 // MockWalletStore store wallet using leveldb
150 type MockWalletStore struct {
155 // NewMockWalletStore create new MockWalletStore struct
156 func NewMockWalletStore(db dbm.DB) *MockWalletStore {
157 return &MockWalletStore{
163 // InitBatch initial batch
164 func (store *MockWalletStore) InitBatch() error {
165 if store.batch != nil {
166 return errors.New("MockWalletStore initail fail, store batch is not nil.")
168 store.batch = store.walletDB.NewBatch()
172 // CommitBatch commit batch
173 func (store *MockWalletStore) CommitBatch() error {
174 if store.batch == nil {
175 return errors.New("MockWalletStore commit fail, store batch is nil.")
182 // DeleteContractUTXO delete contract utxo by outputID
183 func (store *MockWalletStore) DeleteContractUTXO(outputID bc.Hash) {
184 if store.batch == nil {
185 store.walletDB.Delete(ContractUTXOKey(outputID))
187 store.batch.Delete(ContractUTXOKey(outputID))
191 // DeleteRecoveryStatus delete recovery status
192 func (store *MockWalletStore) DeleteRecoveryStatus() {
193 if store.batch == nil {
194 store.walletDB.Delete(RecoveryKey)
196 store.batch.Delete(RecoveryKey)
200 // DeleteTransactions delete transactions when orphan block rollback
201 func (store *MockWalletStore) DeleteTransactions(height uint64) {
202 batch := store.walletDB.NewBatch()
203 if store.batch != nil {
206 txIter := store.walletDB.IteratorPrefix(calcDeleteKey(height))
207 defer txIter.Release()
209 tmpTx := query.AnnotatedTx{}
211 if err := json.Unmarshal(txIter.Value(), &tmpTx); err == nil {
212 batch.Delete(calcTxIndexKey(tmpTx.ID.String()))
214 batch.Delete(txIter.Key())
216 if store.batch == nil {
221 // DeleteUnconfirmedTransaction delete unconfirmed tx by txID
222 func (store *MockWalletStore) DeleteUnconfirmedTransaction(txID string) {
223 if store.batch == nil {
224 store.walletDB.Delete(calcUnconfirmedTxKey(txID))
226 store.batch.Delete(calcUnconfirmedTxKey(txID))
230 // DeleteWalletTransactions delete all txs in wallet
231 func (store *MockWalletStore) DeleteWalletTransactions() {
232 batch := store.walletDB.NewBatch()
233 if store.batch != nil {
236 txIter := store.walletDB.IteratorPrefix([]byte(TxPrefix))
237 defer txIter.Release()
240 batch.Delete(txIter.Key())
243 txIndexIter := store.walletDB.IteratorPrefix([]byte(TxIndexPrefix))
244 defer txIndexIter.Release()
246 for txIndexIter.Next() {
247 batch.Delete(txIndexIter.Key())
249 if store.batch == nil {
254 // DeleteWalletUTXOs delete all txs in wallet
255 func (store *MockWalletStore) DeleteWalletUTXOs() {
256 batch := store.walletDB.NewBatch()
257 if store.batch != nil {
260 ruIter := store.walletDB.IteratorPrefix([]byte(UTXOPrefix))
261 defer ruIter.Release()
263 batch.Delete(ruIter.Key())
266 suIter := store.walletDB.IteratorPrefix([]byte(SUTXOPrefix))
267 defer suIter.Release()
269 batch.Delete(suIter.Key())
271 if store.batch == nil {
276 // GetAsset get asset by assetID
277 func (store *MockWalletStore) GetAsset(assetID *bc.AssetID) (*asset.Asset, error) {
278 definitionByte := store.walletDB.Get(asset.ExtAssetKey(assetID))
279 if definitionByte == nil {
280 return nil, errGetAsset
282 definitionMap := make(map[string]interface{})
283 if err := json.Unmarshal(definitionByte, &definitionMap); err != nil {
286 alias := assetID.String()
287 externalAsset := &asset.Asset{
290 DefinitionMap: definitionMap,
291 RawDefinitionByte: definitionByte,
293 return externalAsset, nil
296 // GetControlProgram get raw program by hash
297 func (store *MockWalletStore) GetControlProgram(hash bc.Hash) (*acc.CtrlProgram, error) {
298 rawProgram := store.walletDB.Get(ContractKey(hash))
299 if rawProgram == nil {
300 return nil, acc.ErrFindCtrlProgram
302 accountCP := new(acc.CtrlProgram)
303 if err := json.Unmarshal(rawProgram, &accountCP); err != nil {
306 return accountCP, nil
309 // GetGlobalTransactionIndex get global tx by txID
310 func (store *MockWalletStore) GetGlobalTransactionIndex(txID string) []byte {
311 return store.walletDB.Get(calcGlobalTxIndexKey(txID))
314 // GetStandardUTXO get standard utxo by id
315 func (store *MockWalletStore) GetStandardUTXO(outid bc.Hash) (*acc.UTXO, error) {
316 rawUTXO := store.walletDB.Get(StandardUTXOKey(outid))
318 return nil, fmt.Errorf("failed get standard UTXO, outputID: %s ", outid.String())
320 UTXO := new(acc.UTXO)
321 if err := json.Unmarshal(rawUTXO, UTXO); err != nil {
327 // GetTransaction get tx by txid
328 func (store *MockWalletStore) GetTransaction(txID string) (*query.AnnotatedTx, error) {
329 formatKey := store.walletDB.Get(calcTxIndexKey(txID))
330 if formatKey == nil {
331 return nil, errAccntTxIDNotFound
333 rawTx := store.walletDB.Get(calcAnnotatedKey(string(formatKey)))
334 tx := new(query.AnnotatedTx)
335 if err := json.Unmarshal(rawTx, tx); err != nil {
341 // GetUnconfirmedTransaction get unconfirmed tx by txID
342 func (store *MockWalletStore) GetUnconfirmedTransaction(txID string) (*query.AnnotatedTx, error) {
343 rawUnconfirmedTx := store.walletDB.Get(calcUnconfirmedTxKey(txID))
344 if rawUnconfirmedTx == nil {
345 return nil, fmt.Errorf("failed get unconfirmed tx, txID: %s ", txID)
347 tx := new(query.AnnotatedTx)
348 if err := json.Unmarshal(rawUnconfirmedTx, tx); err != nil {
354 // GetRecoveryStatus delete recovery status
355 func (store *MockWalletStore) GetRecoveryStatus(recoveryKey []byte) []byte {
356 return store.walletDB.Get(recoveryKey)
359 // GetWalletInfo get wallet information
360 func (store *MockWalletStore) GetWalletInfo() []byte {
361 return store.walletDB.Get([]byte(WalletKey))
364 // ListAccountUTXOs get all account unspent outputs
365 func (store *MockWalletStore) ListAccountUTXOs(key string) ([]*acc.UTXO, error) {
366 accountUtxoIter := store.walletDB.IteratorPrefix([]byte(key))
367 defer accountUtxoIter.Release()
369 confirmedUTXOs := []*acc.UTXO{}
370 for accountUtxoIter.Next() {
371 utxo := new(acc.UTXO)
372 if err := json.Unmarshal(accountUtxoIter.Value(), utxo); err != nil {
375 confirmedUTXOs = append(confirmedUTXOs, utxo)
377 return confirmedUTXOs, nil
380 func (store *MockWalletStore) ListTransactions(accountID string, StartTxID string, count uint, unconfirmed bool) ([]*query.AnnotatedTx, error) {
381 annotatedTxs := []*query.AnnotatedTx{}
387 startKey = calcUnconfirmedTxKey(StartTxID)
389 formatKey := store.walletDB.Get(calcTxIndexKey(StartTxID))
390 if formatKey == nil {
391 return nil, errAccntTxIDNotFound
393 startKey = calcAnnotatedKey(string(formatKey))
398 preFix = UnconfirmedTxPrefix
401 itr := store.walletDB.IteratorPrefixWithStart([]byte(preFix), startKey, true)
404 for txNum := count; itr.Next() && txNum > 0; txNum-- {
405 annotatedTx := new(query.AnnotatedTx)
406 if err := json.Unmarshal(itr.Value(), &annotatedTx); err != nil {
409 annotatedTxs = append(annotatedTxs, annotatedTx)
412 return annotatedTxs, nil
415 // ListUnconfirmedTransactions get all unconfirmed txs
416 func (store *MockWalletStore) ListUnconfirmedTransactions() ([]*query.AnnotatedTx, error) {
417 annotatedTxs := []*query.AnnotatedTx{}
418 txIter := store.walletDB.IteratorPrefix([]byte(UnconfirmedTxPrefix))
419 defer txIter.Release()
422 annotatedTx := &query.AnnotatedTx{}
423 if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
426 annotatedTxs = append(annotatedTxs, annotatedTx)
428 return annotatedTxs, nil
431 // SetAssetDefinition set assetID and definition
432 func (store *MockWalletStore) SetAssetDefinition(assetID *bc.AssetID, definition []byte) {
433 if store.batch == nil {
434 store.walletDB.Set(asset.ExtAssetKey(assetID), definition)
436 store.batch.Set(asset.ExtAssetKey(assetID), definition)
440 // SetContractUTXO set standard utxo
441 func (store *MockWalletStore) SetContractUTXO(outputID bc.Hash, utxo *acc.UTXO) error {
442 data, err := json.Marshal(utxo)
446 if store.batch == nil {
447 store.walletDB.Set(ContractUTXOKey(outputID), data)
449 store.batch.Set(ContractUTXOKey(outputID), data)
454 // SetGlobalTransactionIndex set global tx index by blockhash and position
455 func (store *MockWalletStore) SetGlobalTransactionIndex(globalTxID string, blockHash *bc.Hash, position uint64) {
456 if store.batch == nil {
457 store.walletDB.Set(calcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
459 store.batch.Set(calcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
463 // SetRecoveryStatus set recovery status
464 func (store *MockWalletStore) SetRecoveryStatus(recoveryKey, rawStatus []byte) {
465 if store.batch == nil {
466 store.walletDB.Set(recoveryKey, rawStatus)
468 store.batch.Set(recoveryKey, rawStatus)
472 // SetTransaction set raw transaction by block height and tx position
473 func (store *MockWalletStore) SetTransaction(height uint64, tx *query.AnnotatedTx) error {
474 batch := store.walletDB.NewBatch()
475 if store.batch != nil {
479 rawTx, err := json.Marshal(tx)
483 batch.Set(calcAnnotatedKey(formatKey(height, tx.Position)), rawTx)
484 batch.Set(calcTxIndexKey(tx.ID.String()), []byte(formatKey(height, tx.Position)))
486 if store.batch == nil {
492 // SetUnconfirmedTransaction set unconfirmed tx by txID
493 func (store *MockWalletStore) SetUnconfirmedTransaction(txID string, tx *query.AnnotatedTx) error {
494 rawTx, err := json.Marshal(tx)
498 if store.batch == nil {
499 store.walletDB.Set(calcUnconfirmedTxKey(txID), rawTx)
501 store.batch.Set(calcUnconfirmedTxKey(txID), rawTx)
506 // SetWalletInfo get wallet information
507 func (store *MockWalletStore) SetWalletInfo(rawWallet []byte) {
508 if store.batch == nil {
509 store.walletDB.Set([]byte(WalletKey), rawWallet)
511 store.batch.Set([]byte(WalletKey), rawWallet)