package wallet
import (
- "encoding/json"
"fmt"
"reflect"
"sync"
"sync/atomic"
"time"
- "github.com/tendermint/tmlibs/db"
-
"github.com/vapor/account"
"github.com/vapor/blockchain/signers"
"github.com/vapor/crypto/ed25519/chainkd"
addrRecoveryWindow = uint64(128)
)
-//recoveryKey key for db store recovery info.
var (
- recoveryKey = []byte("RecoveryInfo")
-
// ErrRecoveryBusy another recovery in progress, can not get recovery manager lock
ErrRecoveryBusy = errors.New("another recovery in progress")
// ErrInvalidAcctID can not find account by account id
- ErrInvalidAcctID = errors.New("invalid account id")
+ ErrInvalidAcctID = errors.New("invalid account id")
+ ErrGetRecoveryStatus = errors.New("failed to get recovery status.")
+ ErrRecoveryStatus = errors.New("recovery status is nil.")
)
// branchRecoveryState maintains the required state in-order to properly
}
}
-// recoveryState used to record the status of a recovery process.
-type recoveryState struct {
+// RecoveryState used to record the status of a recovery process.
+type RecoveryState struct {
// XPubs recovery account xPubs
XPubs []chainkd.XPub
AccountsStatus map[string]*addressRecoveryState
}
-func newRecoveryState() *recoveryState {
- return &recoveryState{
+func newRecoveryState() *RecoveryState {
+ return &RecoveryState{
AccountsStatus: make(map[string]*addressRecoveryState),
StartTime: time.Now(),
}
// stateForScope returns a ScopeRecoveryState for the provided key scope. If one
// does not already exist, a new one will be generated with the RecoveryState's
// recoveryWindow.
-func (rs *recoveryState) stateForScope(account *account.Account) {
+func (rs *RecoveryState) stateForScope(account *account.Account) {
// If the account recovery state already exists, return it.
if _, ok := rs.AccountsStatus[account.ID]; ok {
return
type recoveryManager struct {
mu sync.Mutex
- db db.DB
+ store WalletStore
accountMgr *account.Manager
locked int32
// state encapsulates and allocates the necessary recovery state for all
// key scopes and subsidiary derivation paths.
- state *recoveryState
+ state *RecoveryState
//addresses all addresses derivation lookahead used when
// attempting to recover the set of used addresses.
addresses map[bc.Hash]*account.CtrlProgram
}
-// newRecoveryManager create recovery manger.
-func newRecoveryManager(db db.DB, accountMgr *account.Manager) *recoveryManager {
+// NewRecoveryManager create recovery manger.
+func NewRecoveryManager(store WalletStore, accountMgr *account.Manager) *recoveryManager {
return &recoveryManager{
- db: db,
+ store: store,
accountMgr: accountMgr,
addresses: make(map[bc.Hash]*account.CtrlProgram),
state: newRecoveryState(),
for _, acct := range accts {
m.state.stateForScope(acct)
- if err := m.extendScanAddresses(acct.ID, true); err != nil {
+ if err := m.extendScanAddresses(acct.ID, false); err != nil {
return err
}
- if err := m.extendScanAddresses(acct.ID, false); err != nil {
+ //Bip32 path no change field, no need to create addresses repeatedly.
+ if acct.DeriveRule == signers.BIP0032 {
+ continue
+ }
+ if err := m.extendScanAddresses(acct.ID, true); err != nil {
return err
}
}
m.state.StartTime = time.Now()
+ if err := m.commitStatusInfo(); err != nil {
+ return err
+ }
+
m.started = true
return nil
}
return err
}
m.state.StartTime = time.Now()
+ if err := m.commitStatusInfo(); err != nil {
+ return err
+ }
+
m.started = true
return nil
}
func (m *recoveryManager) commitStatusInfo() error {
- rawStatus, err := json.Marshal(m.state)
- if err != nil {
- return err
- }
-
- m.db.Set(recoveryKey, rawStatus)
- return nil
+ return m.store.SetRecoveryStatus(m.state)
}
func genAcctAlias(xPubs []chainkd.XPub, index uint64) string {
func (m *recoveryManager) processBlock(b *types.Block) error {
for _, tx := range b.Transactions {
for _, output := range tx.Outputs {
- if cp, ok := m.addresses[getCPHash(output.ControlProgram)]; ok {
+ if cp, ok := m.addresses[getCPHash(output.ControlProgram())]; ok {
status, ok := m.state.AccountsStatus[cp.AccountID]
if !ok {
return ErrInvalidAcctID
}
func (m *recoveryManager) finished() {
- m.db.Delete(recoveryKey)
+ m.store.DeleteRecoveryStatus()
m.started = false
m.addresses = make(map[bc.Hash]*account.CtrlProgram)
m.state = newRecoveryState()
m.mu.Lock()
defer m.mu.Unlock()
- rawStatus := m.db.Get(recoveryKey)
- if rawStatus == nil {
+ if m.state == nil {
+ return ErrRecoveryStatus
+ }
+ status, err := m.store.GetRecoveryStatus()
+ if err == ErrGetRecoveryStatus {
return nil
}
-
- if err := json.Unmarshal(rawStatus, m.state); err != nil {
+ if err != nil {
return err
}
+ m.state = status
if m.state.XPubs != nil && !m.tryStartXPubsRec() {
return ErrRecoveryBusy