OSDN Git Service

Thanos did someting
[bytom/vapor.git] / wallet / recovery.go
diff --git a/wallet/recovery.go b/wallet/recovery.go
deleted file mode 100644 (file)
index ad2ca40..0000000
+++ /dev/null
@@ -1,459 +0,0 @@
-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"
-       "github.com/vapor/crypto/sha3pool"
-       "github.com/vapor/errors"
-       "github.com/vapor/protocol/bc"
-       "github.com/vapor/protocol/bc/types"
-)
-
-const (
-       // acctRecoveryWindow defines the account derivation lookahead used when
-       // attempting to recover the set of used accounts.
-       acctRecoveryWindow = uint64(6)
-
-       // addrRecoveryWindow defines the address derivation lookahead used when
-       // attempting to recover the set of used addresses.
-       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")
-)
-
-// branchRecoveryState maintains the required state in-order to properly
-// recover addresses derived from a particular account's internal or external
-// derivation branch.
-//
-// A branch recovery state supports operations for:
-//  - Expanding the look-ahead horizon based on which indexes have been found.
-//  - Registering derived addresses with indexes within the horizon.
-//  - Reporting an invalid child index that falls into the horizon.
-//  - Reporting that an address has been found.
-//  - Retrieving all currently derived addresses for the branch.
-//  - Looking up a particular address by its child index.
-type branchRecoveryState struct {
-       // recoveryWindow defines the key-derivation lookahead used when
-       // attempting to recover the set of addresses on this branch.
-       RecoveryWindow uint64
-
-       // horizion records the highest child index watched by this branch.
-       Horizon uint64
-
-       // nextUnfound maintains the child index of the successor to the highest
-       // index that has been found during recovery of this branch.
-       NextUnfound uint64
-}
-
-// newBranchRecoveryState creates a new branchRecoveryState that can be used to
-// track either the external or internal branch of an account's derivation path.
-func newBranchRecoveryState(recoveryWindow uint64) *branchRecoveryState {
-       return &branchRecoveryState{
-               RecoveryWindow: recoveryWindow,
-               Horizon:        1,
-               NextUnfound:    1,
-       }
-}
-
-// extendHorizon returns the current horizon and the number of addresses that
-// must be derived in order to maintain the desired recovery window.
-func (brs *branchRecoveryState) extendHorizon() (uint64, uint64) {
-       // Compute the new horizon, which should surpass our last found address
-       // by the recovery window.
-       curHorizon := brs.Horizon
-
-       minValidHorizon := brs.NextUnfound + brs.RecoveryWindow
-
-       // If the current horizon is sufficient, we will not have to derive any
-       // new keys.
-       if curHorizon >= minValidHorizon {
-               return curHorizon, 0
-       }
-
-       // Otherwise, the number of addresses we should derive corresponds to
-       // the delta of the two horizons, and we update our new horizon.
-       delta := minValidHorizon - curHorizon
-       brs.Horizon = minValidHorizon
-
-       return curHorizon, delta
-}
-
-// reportFound updates the last found index if the reported index exceeds the
-// current value.
-func (brs *branchRecoveryState) reportFound(index uint64) {
-       if index >= brs.NextUnfound {
-               brs.NextUnfound = index + 1
-       }
-}
-
-// addressRecoveryState is used to manage the recovery of addresses generated
-// under a particular BIP32/BIP44 account. Each account tracks both an external and
-// internal branch recovery state, both of which use the same recovery window.
-type addressRecoveryState struct {
-       // ExternalBranch is the recovery state of addresses generated for
-       // external use, i.e. receiving addresses.
-       ExternalBranch *branchRecoveryState
-
-       // InternalBranch is the recovery state of addresses generated for
-       // internal use, i.e. change addresses.
-       InternalBranch *branchRecoveryState
-
-       Account *account.Account
-}
-
-func newAddressRecoveryState(recoveryWindow uint64, account *account.Account) *addressRecoveryState {
-       return &addressRecoveryState{
-               ExternalBranch: newBranchRecoveryState(recoveryWindow),
-               InternalBranch: newBranchRecoveryState(recoveryWindow),
-               Account:        account,
-       }
-}
-
-// recoveryState used to record the status of a recovery process.
-type recoveryState struct {
-       // XPubs recovery account xPubs
-       XPubs []chainkd.XPub
-
-       // The time to start the recovery task, used to detemine whether
-       // recovery task is completed.
-       StartTime time.Time
-
-       // XPubsStatus maintains a map of each requested XPub to its active
-       // account recovery state.
-       XPubsStatus *branchRecoveryState
-
-       // AcctStatus maintains a map of each requested key scope to its active
-       // recovery state.
-       AccountsStatus map[string]*addressRecoveryState
-}
-
-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) {
-       // If the account recovery state already exists, return it.
-       if _, ok := rs.AccountsStatus[account.ID]; ok {
-               return
-       }
-
-       // Otherwise, initialize the recovery state for this scope with the
-       // chosen recovery window.
-       rs.AccountsStatus[account.ID] = newAddressRecoveryState(addrRecoveryWindow, account)
-}
-
-// recoveryManager manage recovery wallet from key.
-type recoveryManager struct {
-       mu sync.Mutex
-
-       db         db.DB
-       accountMgr *account.Manager
-
-       locked int32
-
-       started bool
-
-       // state encapsulates and allocates the necessary recovery state for all
-       // key scopes and subsidiary derivation paths.
-       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 {
-       return &recoveryManager{
-               db:         db,
-               accountMgr: accountMgr,
-               addresses:  make(map[bc.Hash]*account.CtrlProgram),
-               state:      newRecoveryState(),
-       }
-}
-
-func (m *recoveryManager) AddrResurrect(accts []*account.Account) error {
-       m.mu.Lock()
-       defer m.mu.Unlock()
-
-       for _, acct := range accts {
-               m.state.stateForScope(acct)
-               if err := m.extendScanAddresses(acct.ID, true); err != nil {
-                       return err
-               }
-
-               if err := m.extendScanAddresses(acct.ID, false); err != nil {
-                       return err
-               }
-       }
-
-       m.state.StartTime = time.Now()
-       m.started = true
-       return nil
-}
-
-func (m *recoveryManager) AcctResurrect(xPubs []chainkd.XPub) error {
-       m.mu.Lock()
-       defer m.mu.Unlock()
-
-       if !m.tryStartXPubsRec() {
-               return ErrRecoveryBusy
-       }
-
-       m.state.XPubs = xPubs
-       m.state.XPubsStatus = newBranchRecoveryState(acctRecoveryWindow)
-
-       if err := m.extendScanAccounts(); err != nil {
-               m.stopXPubsRec()
-               return err
-       }
-       m.state.StartTime = time.Now()
-       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
-}
-
-func genAcctAlias(xPubs []chainkd.XPub, index uint64) string {
-       var tmp []byte
-       for _, xPub := range xPubs {
-               tmp = append(tmp, xPub[:6]...)
-       }
-       return fmt.Sprintf("%x:%x", tmp, index)
-}
-
-func (m *recoveryManager) extendScanAccounts() error {
-       if m.state.XPubsStatus == nil {
-               return nil
-       }
-
-       curHorizon, delta := m.state.XPubsStatus.extendHorizon()
-       for index := curHorizon; index < curHorizon+delta; index++ {
-               alias := genAcctAlias(m.state.XPubs, index)
-               account, err := account.CreateAccount(m.state.XPubs, len(m.state.XPubs), alias, index, signers.BIP0044)
-               if err != nil {
-                       return err
-               }
-
-               m.state.stateForScope(account)
-               //generate resurrect address for new account.
-               if err := m.extendScanAddresses(account.ID, true); err != nil {
-                       return err
-               }
-
-               if err := m.extendScanAddresses(account.ID, false); err != nil {
-                       return err
-               }
-       }
-
-       return nil
-}
-
-func getCPHash(cp []byte) bc.Hash {
-       var h [32]byte
-       sha3pool.Sum256(h[:], cp)
-       return bc.NewHash(h)
-}
-
-func (m *recoveryManager) extendAddress(acct *account.Account, index uint64, change bool) error {
-       cp, err := account.CreateCtrlProgram(acct, index, change)
-       if err != nil {
-               return err
-       }
-
-       m.addresses[getCPHash(cp.ControlProgram)] = cp
-       return nil
-}
-
-func (m *recoveryManager) extendScanAddresses(accountID string, change bool) error {
-       state, ok := m.state.AccountsStatus[accountID]
-       if !ok {
-               return ErrInvalidAcctID
-       }
-
-       var curHorizon, delta uint64
-       if change {
-               curHorizon, delta = state.InternalBranch.extendHorizon()
-       } else {
-               curHorizon, delta = state.ExternalBranch.extendHorizon()
-       }
-       for index := curHorizon; index < curHorizon+delta; index++ {
-               if err := m.extendAddress(state.Account, index, change); err != nil {
-                       return err
-               }
-       }
-       return nil
-}
-
-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 {
-                               status, ok := m.state.AccountsStatus[cp.AccountID]
-                               if !ok {
-                                       return ErrInvalidAcctID
-                               }
-
-                               if err := m.reportFound(status.Account, cp); err != nil {
-                                       return err
-                               }
-                       }
-               }
-       }
-
-       return nil
-}
-
-// FilterRecoveryTxs Filter transactions that meet the recovery address
-func (m *recoveryManager) FilterRecoveryTxs(b *types.Block) error {
-       m.mu.Lock()
-       defer m.mu.Unlock()
-
-       if !m.started {
-               return nil
-       }
-       if b.Time().After(m.state.StartTime) {
-               m.finished()
-               return nil
-       }
-       return m.processBlock(b)
-}
-
-func (m *recoveryManager) finished() {
-       m.db.Delete(recoveryKey)
-       m.started = false
-       m.addresses = make(map[bc.Hash]*account.CtrlProgram)
-       m.state = newRecoveryState()
-       m.stopXPubsRec()
-}
-
-func (m *recoveryManager) LoadStatusInfo() error {
-       m.mu.Lock()
-       defer m.mu.Unlock()
-
-       rawStatus := m.db.Get(recoveryKey)
-       if rawStatus == nil {
-               return nil
-       }
-
-       if err := json.Unmarshal(rawStatus, m.state); err != nil {
-               return err
-       }
-
-       if m.state.XPubs != nil && !m.tryStartXPubsRec() {
-               return ErrRecoveryBusy
-       }
-
-       if err := m.restoreAddresses(); err != nil {
-               m.stopXPubsRec()
-               return err
-       }
-
-       m.started = true
-       return nil
-}
-
-// restoreAddresses resume addresses for unfinished tasks
-func (m *recoveryManager) restoreAddresses() error {
-       for _, state := range m.state.AccountsStatus {
-               for index := uint64(1); index <= state.InternalBranch.Horizon; index++ {
-                       if err := m.extendAddress(state.Account, index, true); err != nil {
-                               return err
-                       }
-               }
-
-               for index := uint64(1); index <= state.ExternalBranch.Horizon; index++ {
-                       if err := m.extendAddress(state.Account, index, false); err != nil {
-                               return err
-                       }
-               }
-       }
-       return nil
-}
-
-// reportFound found your own address operation.
-func (m *recoveryManager) reportFound(account *account.Account, cp *account.CtrlProgram) error {
-       if m.state.XPubsStatus != nil && reflect.DeepEqual(m.state.XPubs, account.XPubs) {
-               //recovery from XPubs need save account to db.
-               if err := m.saveAccount(account); err != nil {
-                       return err
-               }
-
-               m.state.XPubsStatus.reportFound(account.KeyIndex)
-               if err := m.extendScanAccounts(); err != nil {
-                       return err
-               }
-       }
-
-       if cp.Change {
-               m.state.AccountsStatus[account.ID].InternalBranch.reportFound(cp.KeyIndex)
-       } else {
-               m.state.AccountsStatus[account.ID].ExternalBranch.reportFound(cp.KeyIndex)
-       }
-
-       if err := m.extendScanAddresses(account.ID, cp.Change); err != nil {
-               return err
-       }
-
-       if err := m.accountMgr.CreateBatchAddresses(account.ID, cp.Change, cp.KeyIndex); err != nil {
-               return err
-       }
-
-       return m.commitStatusInfo()
-}
-
-func (m *recoveryManager) saveAccount(acct *account.Account) error {
-       tmp, err := m.accountMgr.FindByID(acct.ID)
-       if err != nil && errors.Root(err) != account.ErrFindAccount {
-               return err
-       }
-
-       if tmp != nil {
-               return nil
-       }
-       return m.accountMgr.SaveAccount(acct)
-}
-
-//tryStartXPubsRec guarantee that only one xPubs recovery is in progress.
-func (m *recoveryManager) tryStartXPubsRec() bool {
-       return atomic.CompareAndSwapInt32(&m.locked, 0, 1)
-}
-
-//stopXPubsRec release xPubs recovery lock.
-func (m *recoveryManager) stopXPubsRec() {
-       m.state.XPubs = nil
-       atomic.StoreInt32(&m.locked, 0)
-}