import (
"encoding/binary"
+ "encoding/hex"
"encoding/json"
"fmt"
- "sort"
acc "github.com/vapor/account"
"github.com/vapor/asset"
"github.com/vapor/blockchain/query"
- "github.com/vapor/blockchain/signers"
- "github.com/vapor/crypto/ed25519/chainkd"
- "github.com/vapor/crypto/sha3pool"
dbm "github.com/vapor/database/leveldb"
"github.com/vapor/errors"
"github.com/vapor/protocol/bc"
+ "github.com/vapor/wallet"
)
-// const (
-// utxoPrefix byte = iota //UTXOPrefix is StandardUTXOKey prefix
-// sutxoPrefix //SUTXOPrefix is ContractUTXOKey prefix
-// contractPrefix
-// contractIndexPrefix
-// accountPrefix // AccountPrefix is account ID prefix
-// accountAliasPrefix
-// accountIndexPrefix
-// txPrefix //TxPrefix is wallet database transactions prefix
-// txIndexPrefix //TxIndexPrefix is wallet database tx index prefix
-// unconfirmedTxPrefix //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
-// globalTxIndexPrefix //GlobalTxIndexPrefix is wallet database global tx index prefix
-// walletKey
-// miningAddressKey
-// coinbaseAbKey
-// recoveryKey
-// )
-
-// // leveldb key prefix
-// var (
-// UTXOPrefix = []byte{utxoPrefix, colon}
-// SUTXOPrefix = []byte{sutxoPrefix, colon}
-// ContractPrefix = []byte{contractPrefix, contractPrefix, colon}
-// ContractIndexPrefix = []byte{contractIndexPrefix, colon}
-// AccountPrefix = []byte{accountPrefix, colon} // AccountPrefix is account ID prefix
-// AccountAliasPrefix = []byte{accountAliasPrefix, colon}
-// AccountIndexPrefix = []byte{accountIndexPrefix, colon}
-// TxPrefix = []byte{txPrefix, colon} //TxPrefix is wallet database transactions prefix
-// TxIndexPrefix = []byte{txIndexPrefix, colon} //TxIndexPrefix is wallet database tx index prefix
-// UnconfirmedTxPrefix = []byte{unconfirmedTxPrefix, colon} //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
-// GlobalTxIndexPrefix = []byte{globalTxIndexPrefix, colon} //GlobalTxIndexPrefix is wallet database global tx index prefix
-// WalletKey = []byte{walletKey}
-// MiningAddressKey = []byte{miningAddressKey}
-// CoinbaseAbKey = []byte{coinbaseAbKey}
-// RecoveryKey = []byte{recoveryKey}
-// )
+const (
+ utxoPrefix byte = iota //UTXOPrefix is StandardUTXOKey prefix
+ sutxoPrefix //SUTXOPrefix is ContractUTXOKey prefix
+ contractPrefix
+ contractIndexPrefix
+ accountPrefix // AccountPrefix is account ID prefix
+ accountAliasPrefix
+ accountIndexPrefix
+ txPrefix //TxPrefix is wallet database transactions prefix
+ txIndexPrefix //TxIndexPrefix is wallet database tx index prefix
+ unconfirmedTxPrefix //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
+ globalTxIndexPrefix //GlobalTxIndexPrefix is wallet database global tx index prefix
+ walletKey
+ miningAddressKey
+ coinbaseAbKey
+ recoveryKey //recoveryKey key for db store recovery info.
+)
+
+// leveldb key prefix
+var (
+ // colon byte = 0x3a
+ UTXOPrefix = []byte{utxoPrefix, colon}
+ SUTXOPrefix = []byte{sutxoPrefix, colon}
+ ContractPrefix = []byte{contractPrefix, contractPrefix, colon}
+ ContractIndexPrefix = []byte{contractIndexPrefix, colon}
+ AccountPrefix = []byte{accountPrefix, colon} // AccountPrefix is account ID prefix
+ AccountAliasPrefix = []byte{accountAliasPrefix, colon}
+ AccountIndexPrefix = []byte{accountIndexPrefix, colon}
+ TxPrefix = []byte{txPrefix, colon} //TxPrefix is wallet database transactions prefix
+ TxIndexPrefix = []byte{txIndexPrefix, colon} //TxIndexPrefix is wallet database tx index prefix
+ UnconfirmedTxPrefix = []byte{unconfirmedTxPrefix, colon} //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
+ GlobalTxIndexPrefix = []byte{globalTxIndexPrefix, colon} //GlobalTxIndexPrefix is wallet database global tx index prefix
+ WalletKey = []byte{walletKey}
+ MiningAddressKey = []byte{miningAddressKey}
+ CoinbaseAbKey = []byte{coinbaseAbKey}
+ RecoveryKey = []byte{recoveryKey}
+)
// errors
var (
- // ErrFindAccount = errors.New("Failed to find account")
errAccntTxIDNotFound = errors.New("account TXID not found")
errGetAsset = errors.New("Failed to find asset definition")
)
-func accountIndexKey(xpubs []chainkd.XPub) []byte {
- var hash [32]byte
- var xPubs []byte
- cpy := append([]chainkd.XPub{}, xpubs[:]...)
- sort.Sort(signers.SortKeys(cpy))
- for _, xpub := range cpy {
- xPubs = append(xPubs, xpub[:]...)
- }
- sha3pool.Sum256(hash[:], xPubs)
- return append([]byte(dbm.AccountIndexPrefix), hash[:]...)
-}
-
-func Bip44ContractIndexKey(accountID string, change bool) []byte {
- key := append([]byte(dbm.ContractIndexPrefix), accountID...)
- if change {
- return append(key, []byte{1}...)
- }
- return append(key, []byte{0}...)
-}
-
-// ContractKey account control promgram store prefix
-func ContractKey(hash bc.Hash) []byte {
- return append([]byte(dbm.ContractPrefix), hash.Bytes()...)
-}
-
-// AccountIDKey account id store prefix
-func AccountIDKey(accountID string) []byte {
- return append([]byte(dbm.AccountPrefix), []byte(accountID)...)
-}
-
-// StandardUTXOKey makes an account unspent outputs key to store
-func StandardUTXOKey(id bc.Hash) []byte {
- return append(dbm.UTXOPrefix, id.Bytes()...)
-}
-
// ContractUTXOKey makes a smart contract unspent outputs key to store
func ContractUTXOKey(id bc.Hash) []byte {
- return append(dbm.SUTXOPrefix, id.Bytes()...)
+ return append(SUTXOPrefix, id.Bytes()...)
}
func calcDeleteKey(blockHeight uint64) []byte {
- return []byte(fmt.Sprintf("%s%016x", dbm.TxPrefix, blockHeight))
+ return []byte(fmt.Sprintf("%s%016x", TxPrefix, blockHeight))
}
func calcTxIndexKey(txID string) []byte {
- return append(dbm.TxIndexPrefix, []byte(txID)...)
+ return append(TxIndexPrefix, []byte(txID)...)
}
func calcAnnotatedKey(formatKey string) []byte {
- return append(dbm.TxPrefix, []byte(formatKey)...)
+ return append(TxPrefix, []byte(formatKey)...)
}
func calcUnconfirmedTxKey(formatKey string) []byte {
- return append(dbm.UnconfirmedTxPrefix, []byte(formatKey)...)
+ return append(UnconfirmedTxPrefix, []byte(formatKey)...)
}
-func calcGlobalTxIndexKey(txID string) []byte {
- return append(dbm.GlobalTxIndexPrefix, []byte(txID)...)
+func CalcGlobalTxIndexKey(txID string) []byte {
+ return append(GlobalTxIndexPrefix, []byte(txID)...)
}
func CalcGlobalTxIndex(blockHash *bc.Hash, position uint64) []byte {
}
func contractIndexKey(accountID string) []byte {
- return append([]byte(dbm.ContractIndexPrefix), []byte(accountID)...)
+ return append(ContractIndexPrefix, []byte(accountID)...)
}
func accountAliasKey(name string) []byte {
- return append([]byte(dbm.AccountAliasPrefix), []byte(name)...)
+ return append(AccountAliasPrefix, []byte(name)...)
}
// WalletStore store wallet using leveldb
type WalletStore struct {
- walletDB dbm.DB
- batch dbm.Batch
+ db dbm.DB
+ batch dbm.Batch
}
// NewWalletStore create new WalletStore struct
func NewWalletStore(db dbm.DB) *WalletStore {
return &WalletStore{
- walletDB: db,
- batch: nil,
+ db: db,
+ batch: nil,
}
}
if store.batch != nil {
return errors.New("WalletStore initail fail, store batch is not nil.")
}
- store.batch = store.walletDB.NewBatch()
+
+ store.batch = store.db.NewBatch()
return nil
}
if store.batch == nil {
return errors.New("WalletStore commit fail, store batch is nil.")
}
+
store.batch.Write()
store.batch = nil
return nil
// DeleteContractUTXO delete contract utxo by outputID
func (store *WalletStore) DeleteContractUTXO(outputID bc.Hash) {
if store.batch == nil {
- store.walletDB.Delete(ContractUTXOKey(outputID))
+ store.db.Delete(ContractUTXOKey(outputID))
} else {
store.batch.Delete(ContractUTXOKey(outputID))
}
// DeleteRecoveryStatus delete recovery status
func (store *WalletStore) DeleteRecoveryStatus() {
if store.batch == nil {
- store.walletDB.Delete(dbm.RecoveryKey)
+ store.db.Delete(RecoveryKey)
} else {
- store.batch.Delete(dbm.RecoveryKey)
+ store.batch.Delete(RecoveryKey)
}
}
// DeleteTransactions delete transactions when orphan block rollback
func (store *WalletStore) DeleteTransactions(height uint64) {
- batch := store.walletDB.NewBatch()
+ batch := store.db.NewBatch()
if store.batch != nil {
batch = store.batch
}
- txIter := store.walletDB.IteratorPrefix(calcDeleteKey(height))
+ txIter := store.db.IteratorPrefix(calcDeleteKey(height))
defer txIter.Release()
tmpTx := query.AnnotatedTx{}
// DeleteUnconfirmedTransaction delete unconfirmed tx by txID
func (store *WalletStore) DeleteUnconfirmedTransaction(txID string) {
if store.batch == nil {
- store.walletDB.Delete(calcUnconfirmedTxKey(txID))
+ store.db.Delete(calcUnconfirmedTxKey(txID))
} else {
store.batch.Delete(calcUnconfirmedTxKey(txID))
}
// DeleteWalletTransactions delete all txs in wallet
func (store *WalletStore) DeleteWalletTransactions() {
- batch := store.walletDB.NewBatch()
+ batch := store.db.NewBatch()
if store.batch != nil {
batch = store.batch
}
- txIter := store.walletDB.IteratorPrefix([]byte(dbm.TxPrefix))
+ txIter := store.db.IteratorPrefix(TxPrefix)
defer txIter.Release()
for txIter.Next() {
batch.Delete(txIter.Key())
}
- txIndexIter := store.walletDB.IteratorPrefix([]byte(dbm.TxIndexPrefix))
+ txIndexIter := store.db.IteratorPrefix(TxIndexPrefix)
defer txIndexIter.Release()
for txIndexIter.Next() {
// DeleteWalletUTXOs delete all txs in wallet
func (store *WalletStore) DeleteWalletUTXOs() {
- batch := store.walletDB.NewBatch()
+ batch := store.db.NewBatch()
if store.batch != nil {
batch = store.batch
}
- ruIter := store.walletDB.IteratorPrefix([]byte(dbm.UTXOPrefix))
+
+ ruIter := store.db.IteratorPrefix(UTXOPrefix)
defer ruIter.Release()
+
for ruIter.Next() {
batch.Delete(ruIter.Key())
}
- suIter := store.walletDB.IteratorPrefix([]byte(dbm.SUTXOPrefix))
+ suIter := store.db.IteratorPrefix(SUTXOPrefix)
defer suIter.Release()
+
for suIter.Next() {
batch.Delete(suIter.Key())
}
// GetAsset get asset by assetID
func (store *WalletStore) GetAsset(assetID *bc.AssetID) (*asset.Asset, error) {
- definitionByte := store.walletDB.Get(asset.ExtAssetKey(assetID))
+ definitionByte := store.db.Get(asset.ExtAssetKey(assetID))
if definitionByte == nil {
return nil, errGetAsset
}
+
definitionMap := make(map[string]interface{})
if err := json.Unmarshal(definitionByte, &definitionMap); err != nil {
return nil, err
}
+
alias := assetID.String()
externalAsset := &asset.Asset{
AssetID: *assetID,
return externalAsset, nil
}
-// GetControlProgram get raw program by hash
-func (store *WalletStore) GetControlProgram(hash bc.Hash) (*acc.CtrlProgram, error) {
- rawProgram := store.walletDB.Get(ContractKey(hash))
- if rawProgram == nil {
- return nil, acc.ErrFindCtrlProgram
- }
- accountCP := new(acc.CtrlProgram)
- if err := json.Unmarshal(rawProgram, &accountCP); err != nil {
- return nil, err
- }
- return accountCP, nil
-}
-
// GetGlobalTransactionIndex get global tx by txID
func (store *WalletStore) GetGlobalTransactionIndex(txID string) []byte {
- return store.walletDB.Get(calcGlobalTxIndexKey(txID))
+ return store.db.Get(CalcGlobalTxIndexKey(txID))
}
// GetStandardUTXO get standard utxo by id
func (store *WalletStore) GetStandardUTXO(outid bc.Hash) (*acc.UTXO, error) {
- rawUTXO := store.walletDB.Get(StandardUTXOKey(outid))
+ rawUTXO := store.db.Get(StandardUTXOKey(outid))
if rawUTXO == nil {
return nil, fmt.Errorf("failed get standard UTXO, outputID: %s ", outid.String())
}
+
UTXO := new(acc.UTXO)
if err := json.Unmarshal(rawUTXO, UTXO); err != nil {
return nil, err
}
+
return UTXO, nil
}
// GetTransaction get tx by txid
func (store *WalletStore) GetTransaction(txID string) (*query.AnnotatedTx, error) {
- formatKey := store.walletDB.Get(calcTxIndexKey(txID))
+ formatKey := store.db.Get(calcTxIndexKey(txID))
if formatKey == nil {
return nil, errAccntTxIDNotFound
}
- rawTx := store.walletDB.Get(calcAnnotatedKey(string(formatKey)))
+
+ rawTx := store.db.Get(calcAnnotatedKey(string(formatKey)))
tx := new(query.AnnotatedTx)
if err := json.Unmarshal(rawTx, tx); err != nil {
return nil, err
}
+
return tx, nil
}
// GetUnconfirmedTransaction get unconfirmed tx by txID
func (store *WalletStore) GetUnconfirmedTransaction(txID string) (*query.AnnotatedTx, error) {
- rawUnconfirmedTx := store.walletDB.Get(calcUnconfirmedTxKey(txID))
+ rawUnconfirmedTx := store.db.Get(calcUnconfirmedTxKey(txID))
if rawUnconfirmedTx == nil {
return nil, fmt.Errorf("failed get unconfirmed tx, txID: %s ", txID)
}
+
tx := new(query.AnnotatedTx)
if err := json.Unmarshal(rawUnconfirmedTx, tx); err != nil {
return nil, err
}
+
return tx, nil
}
// GetRecoveryStatus delete recovery status
-func (store *WalletStore) GetRecoveryStatus(recoveryKey []byte) []byte {
- return store.walletDB.Get(recoveryKey)
+func (store *WalletStore) GetRecoveryStatus() (*wallet.RecoveryState, error) {
+ rawStatus := store.db.Get(RecoveryKey)
+ if rawStatus == nil {
+ return nil, wallet.ErrGetRecoveryStatus
+ }
+
+ state := new(wallet.RecoveryState)
+ if err := json.Unmarshal(rawStatus, state); err != nil {
+ return nil, err
+ }
+
+ return state, nil
}
// GetWalletInfo get wallet information
-func (store *WalletStore) GetWalletInfo() []byte {
- return store.walletDB.Get([]byte(dbm.WalletKey))
+func (store *WalletStore) GetWalletInfo() (*wallet.StatusInfo, error) {
+ rawStatus := store.db.Get(WalletKey)
+ if rawStatus == nil {
+ return nil, wallet.ErrGetWalletStatusInfo
+ }
+
+ status := new(wallet.StatusInfo)
+ if err := json.Unmarshal(rawStatus, status); err != nil {
+ return nil, err
+ }
+
+ return status, nil
}
// ListAccountUTXOs get all account unspent outputs
-func (store *WalletStore) ListAccountUTXOs(key string) ([]*acc.UTXO, error) {
- accountUtxoIter := store.walletDB.IteratorPrefix([]byte(key))
+func (store *WalletStore) ListAccountUTXOs(id string, isSmartContract bool) ([]*acc.UTXO, error) {
+ prefix := UTXOPrefix
+ if isSmartContract {
+ prefix = SUTXOPrefix
+ }
+
+ idBytes, err := hex.DecodeString(id)
+ if err != nil {
+ return nil, err
+ }
+
+ accountUtxoIter := store.db.IteratorPrefix(append(prefix, idBytes...))
defer accountUtxoIter.Release()
confirmedUTXOs := []*acc.UTXO{}
if err := json.Unmarshal(accountUtxoIter.Value(), utxo); err != nil {
return nil, err
}
+
confirmedUTXOs = append(confirmedUTXOs, utxo)
}
return confirmedUTXOs, nil
func (store *WalletStore) ListTransactions(accountID string, StartTxID string, count uint, unconfirmed bool) ([]*query.AnnotatedTx, error) {
annotatedTxs := []*query.AnnotatedTx{}
var startKey []byte
- preFix := dbm.TxPrefix
+ preFix := TxPrefix
if StartTxID != "" {
if unconfirmed {
startKey = calcUnconfirmedTxKey(StartTxID)
} else {
- formatKey := store.walletDB.Get(calcTxIndexKey(StartTxID))
+ formatKey := store.db.Get(calcTxIndexKey(StartTxID))
if formatKey == nil {
return nil, errAccntTxIDNotFound
}
+
startKey = calcAnnotatedKey(string(formatKey))
}
}
if unconfirmed {
- preFix = dbm.UnconfirmedTxPrefix
+ preFix = UnconfirmedTxPrefix
}
- itr := store.walletDB.IteratorPrefixWithStart([]byte(preFix), startKey, true)
+ itr := store.db.IteratorPrefixWithStart(preFix, startKey, true)
defer itr.Release()
for txNum := count; itr.Next() && txNum > 0; txNum-- {
if err := json.Unmarshal(itr.Value(), &annotatedTx); err != nil {
return nil, err
}
+
annotatedTxs = append(annotatedTxs, annotatedTx)
}
// ListUnconfirmedTransactions get all unconfirmed txs
func (store *WalletStore) ListUnconfirmedTransactions() ([]*query.AnnotatedTx, error) {
annotatedTxs := []*query.AnnotatedTx{}
- txIter := store.walletDB.IteratorPrefix([]byte(dbm.UnconfirmedTxPrefix))
+ txIter := store.db.IteratorPrefix(UnconfirmedTxPrefix)
defer txIter.Release()
for txIter.Next() {
if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
return nil, err
}
+
annotatedTxs = append(annotatedTxs, annotatedTx)
}
return annotatedTxs, nil
// SetAssetDefinition set assetID and definition
func (store *WalletStore) SetAssetDefinition(assetID *bc.AssetID, definition []byte) {
if store.batch == nil {
- store.walletDB.Set(asset.ExtAssetKey(assetID), definition)
+ store.db.Set(asset.ExtAssetKey(assetID), definition)
} else {
store.batch.Set(asset.ExtAssetKey(assetID), definition)
}
if err != nil {
return err
}
+
if store.batch == nil {
- store.walletDB.Set(ContractUTXOKey(outputID), data)
+ store.db.Set(ContractUTXOKey(outputID), data)
} else {
store.batch.Set(ContractUTXOKey(outputID), data)
}
// SetGlobalTransactionIndex set global tx index by blockhash and position
func (store *WalletStore) SetGlobalTransactionIndex(globalTxID string, blockHash *bc.Hash, position uint64) {
if store.batch == nil {
- store.walletDB.Set(calcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
+ store.db.Set(CalcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
} else {
- store.batch.Set(calcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
+ store.batch.Set(CalcGlobalTxIndexKey(globalTxID), CalcGlobalTxIndex(blockHash, position))
}
}
// SetRecoveryStatus set recovery status
-func (store *WalletStore) SetRecoveryStatus(recoveryKey, rawStatus []byte) {
+func (store *WalletStore) SetRecoveryStatus(recoveryState *wallet.RecoveryState) error {
+ rawStatus, err := json.Marshal(recoveryState)
+ if err != nil {
+ return err
+ }
+
if store.batch == nil {
- store.walletDB.Set(recoveryKey, rawStatus)
+ store.db.Set(RecoveryKey, rawStatus)
} else {
- store.batch.Set(recoveryKey, rawStatus)
+ store.batch.Set(RecoveryKey, rawStatus)
}
+ return nil
}
// SetTransaction set raw transaction by block height and tx position
func (store *WalletStore) SetTransaction(height uint64, tx *query.AnnotatedTx) error {
- batch := store.walletDB.NewBatch()
+ batch := store.db.NewBatch()
if store.batch != nil {
batch = store.batch
}
if err != nil {
return err
}
+
batch.Set(calcAnnotatedKey(formatKey(height, tx.Position)), rawTx)
batch.Set(calcTxIndexKey(tx.ID.String()), []byte(formatKey(height, tx.Position)))
if err != nil {
return err
}
+
if store.batch == nil {
- store.walletDB.Set(calcUnconfirmedTxKey(txID), rawTx)
+ store.db.Set(calcUnconfirmedTxKey(txID), rawTx)
} else {
store.batch.Set(calcUnconfirmedTxKey(txID), rawTx)
}
}
// SetWalletInfo get wallet information
-func (store *WalletStore) SetWalletInfo(rawWallet []byte) {
+func (store *WalletStore) SetWalletInfo(status *wallet.StatusInfo) error {
+ rawWallet, err := json.Marshal(status)
+ if err != nil {
+ return err
+ }
+
if store.batch == nil {
- store.walletDB.Set([]byte(dbm.WalletKey), rawWallet)
+ store.db.Set(WalletKey, rawWallet)
} else {
- store.batch.Set([]byte(dbm.WalletKey), rawWallet)
+ store.batch.Set(WalletKey, rawWallet)
}
+ return nil
}