OSDN Git Service

init push for backup wallet
authorpaladz <453256728@qq.com>
Wed, 18 Apr 2018 06:20:48 +0000 (14:20 +0800)
committerpaladz <453256728@qq.com>
Wed, 18 Apr 2018 06:20:48 +0000 (14:20 +0800)
account/accounts.go
account/image.go [new file with mode: 0644]
api/api.go
api/wallet.go
asset/asset.go
asset/image.go [new file with mode: 0644]
blockchain/pseudohsm/image.go [new file with mode: 0644]
blockchain/pseudohsm/pseudohsm.go

index a499269..3e4f799 100644 (file)
@@ -5,7 +5,6 @@ import (
        "context"
        "encoding/binary"
        "encoding/json"
-       "sort"
        "strings"
        "sync"
        "time"
@@ -27,14 +26,17 @@ import (
 )
 
 const (
-       maxAccountCache = 1000
-       aliasPrefix     = "ALI:"
-       accountPrefix   = "ACC:"
-       accountCPPrefix = "ACP:"
-       indexPrefix     = "ACIDX:"
+       maxAccountCache     = 1000
+       aliasPrefix         = "ALI:"
+       accountPrefix       = "ACC:"
+       accountCPPrefix     = "ACP:"
+       contractIndexPrefix = "ACPI:"
 )
 
-var miningAddressKey = []byte("miningAddress")
+var (
+       miningAddressKey = []byte("miningAddress")
+       accountIndexKey  = []byte("accountIndex")
+)
 
 // pre-define errors for supporting bytom errorFormatter
 var (
@@ -47,17 +49,6 @@ func aliasKey(name string) []byte {
        return []byte(aliasPrefix + name)
 }
 
-func indexKeys(xpubs []chainkd.XPub) []byte {
-       xpubStrings := make([]string, len(xpubs))
-       for i, xpub := range xpubs {
-               xpubStrings[i] = xpub.String()
-       }
-       sort.Strings(xpubStrings)
-       suffix := strings.Join(xpubStrings, "")
-
-       return []byte(indexPrefix + suffix)
-}
-
 //Key account store prefix
 func Key(name string) []byte {
        return []byte(accountPrefix + name)
@@ -68,6 +59,10 @@ func CPKey(hash common.Hash) []byte {
        return append([]byte(accountCPPrefix), hash[:]...)
 }
 
+func contractIndexKey(accountID string) []byte {
+       return append([]byte(contractIndexPrefix), []byte(accountID)...)
+}
+
 func convertUnit64ToBytes(nextIndex uint64) []byte {
        buf := make([]byte, 8)
        binary.PutUvarint(buf, nextIndex)
@@ -132,17 +127,29 @@ type Account struct {
        Alias string
 }
 
-func (m *Manager) getNextXpubsIndex(xpubs []chainkd.XPub) uint64 {
+func (m *Manager) getNextAccountIndex() uint64 {
        m.accIndexMu.Lock()
        defer m.accIndexMu.Unlock()
 
        var nextIndex uint64 = 1
-       if rawIndexBytes := m.db.Get(indexKeys(xpubs)); rawIndexBytes != nil {
+       if rawIndexBytes := m.db.Get(accountIndexKey); rawIndexBytes != nil {
                nextIndex = convertBytesToUint64(rawIndexBytes) + 1
        }
 
-       m.db.Set(indexKeys(xpubs), convertUnit64ToBytes(nextIndex))
+       m.db.Set(accountIndexKey, convertUnit64ToBytes(nextIndex))
+       return nextIndex
+}
+
+func (m *Manager) getNextContractIndex(accountID string) uint64 {
+       m.accIndexMu.Lock()
+       defer m.accIndexMu.Unlock()
+
+       var nextIndex uint64 = 1
+       if rawIndexBytes := m.db.Get(contractIndexKey(accountID)); rawIndexBytes != nil {
+               nextIndex = convertBytesToUint64(rawIndexBytes) + 1
+       }
 
+       m.db.Set(contractIndexKey(accountID), convertUnit64ToBytes(nextIndex))
        return nextIndex
 }
 
@@ -153,7 +160,7 @@ func (m *Manager) Create(ctx context.Context, xpubs []chainkd.XPub, quorum int,
                return nil, ErrDuplicateAlias
        }
 
-       signer, err := signers.Create("account", xpubs, quorum, m.getNextXpubsIndex(xpubs))
+       signer, err := signers.Create("account", xpubs, quorum, m.getNextAccountIndex())
        id := signers.IDGenerate()
        if err != nil {
                return nil, errors.Wrap(err)
@@ -286,7 +293,7 @@ func (m *Manager) ListCtrlProgramsByAccountId(ctx context.Context, accountId str
 }
 
 func (m *Manager) createP2PKH(ctx context.Context, account *Account, change bool) (*CtrlProgram, error) {
-       idx := m.getNextXpubsIndex(account.Signer.XPubs)
+       idx := m.getNextContractIndex(account.ID)
        path := signers.Path(account.Signer, signers.AccountKeySpace, idx)
        derivedXPubs := chainkd.DeriveXPubs(account.XPubs, path)
        derivedPK := derivedXPubs[0].PublicKey()
@@ -313,7 +320,7 @@ func (m *Manager) createP2PKH(ctx context.Context, account *Account, change bool
 }
 
 func (m *Manager) createP2SH(ctx context.Context, account *Account, change bool) (*CtrlProgram, error) {
-       idx := m.getNextXpubsIndex(account.Signer.XPubs)
+       idx := m.getNextContractIndex(account.ID)
        path := signers.Path(account.Signer, signers.AccountKeySpace, idx)
        derivedXPubs := chainkd.DeriveXPubs(account.XPubs, path)
        derivedPKs := chainkd.XPubKeys(derivedXPubs)
diff --git a/account/image.go b/account/image.go
new file mode 100644 (file)
index 0000000..bf88a38
--- /dev/null
@@ -0,0 +1,65 @@
+// Package account stores and tracks accounts within a Chain Core.
+package account
+
+import (
+       "encoding/json"
+)
+
+type AccountSlice struct {
+       Account       *Account
+       ContractIndex uint64
+}
+
+type AccountImage struct {
+       AccountSlice []*AccountSlice
+       AccountIndex uint64
+}
+
+func (m *Manager) Backup() (*AccountImage, error) {
+       accountSlices := []*AccountSlice{}
+       accountIter := m.db.IteratorPrefix([]byte(accountPrefix))
+       defer accountIter.Release()
+
+       for accountIter.Next() {
+               accountSlice := &AccountSlice{}
+               if err := json.Unmarshal(accountIter.Value(), accountSlice.Account); err != nil {
+                       return nil, err
+               }
+
+               accountSlice.ContractIndex = m.getNextContractIndex(accountSlice.Account.ID)
+               accountSlices = append(accountSlices, accountSlice)
+       }
+
+       accountImage := &AccountImage{
+               AccountSlice: accountSlices,
+               AccountIndex: m.getNextAccountIndex(),
+       }
+       return accountImage, nil
+}
+
+func (m *Manager) Restore(image *AccountImage) error {
+       if localIndex := m.getNextAccountIndex(); localIndex > image.AccountIndex {
+               image.AccountIndex = localIndex
+       }
+
+       storeBatch := m.db.NewBatch()
+       for _, accountSlice := range image.AccountSlice {
+               rawAccount, err := json.Marshal(accountSlice.Account)
+               if err != nil {
+                       return ErrMarshalAccount
+               }
+
+               if existed := m.db.Get(aliasKey(accountSlice.Account.Alias)); existed != nil {
+                       return ErrDuplicateAlias
+               }
+
+               accountID := Key(accountSlice.Account.ID)
+               storeBatch.Set(accountID, rawAccount)
+               storeBatch.Set(aliasKey(accountSlice.Account.Alias), accountID)
+               storeBatch.Set(contractIndexKey(accountSlice.Account.ID), convertUnit64ToBytes(accountSlice.ContractIndex))
+       }
+
+       storeBatch.Set(accountIndexKey, convertUnit64ToBytes(image.AccountIndex))
+       storeBatch.Write()
+       return nil
+}
index d1e0610..14191a5 100644 (file)
@@ -199,6 +199,9 @@ func (a *API) buildHandler() {
 
                m.Handle("/list-balances", jsonHandler(a.listBalances))
                m.Handle("/list-unspent-outputs", jsonHandler(a.listUnspentOutputs))
+
+               m.Handle("/backup-wallet", jsonHandler(a.backupWalletImage))
+               m.Handle("/restore-wallet", jsonHandler(a.restoreWalletImage))
        } else {
                log.Warn("Please enable wallet")
        }
index ac593ae..dabdb78 100644 (file)
@@ -1,6 +1,11 @@
 package api
 
 import (
+       "context"
+
+       "github.com/bytom/account"
+       "github.com/bytom/asset"
+       "github.com/bytom/blockchain/pseudohsm"
        "github.com/bytom/errors"
 )
 
@@ -8,3 +13,44 @@ import (
 func (a *API) walletError() Response {
        return NewErrorResponse(errors.New("wallet not found, please check that the wallet is open"))
 }
+
+type WalletImage struct {
+       AccountImage *account.AccountImage
+       AssetImage   *asset.AssetImage
+       KeyImages    []*pseudohsm.KeyImage
+}
+
+func (a *API) restoreWalletImage(ctx context.Context, image WalletImage) Response {
+       if err := a.wallet.Hsm.Restore(image.KeyImages); err != nil {
+               return NewErrorResponse(err)
+       }
+       if err := a.wallet.AssetReg.Restore(image.AssetImage); err != nil {
+               return NewErrorResponse(err)
+       }
+       if err := a.wallet.AccountMgr.Restore(image.AccountImage); err != nil {
+               return NewErrorResponse(err)
+       }
+       return NewSuccessResponse(nil)
+}
+
+func (a *API) backupWalletImage() Response {
+       keyImages, err := a.wallet.Hsm.Backup()
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+       assetImage, err := a.wallet.AssetReg.Backup()
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+       accountImage, err := a.wallet.AccountMgr.Backup()
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       image := &WalletImage{
+               KeyImages:    keyImages,
+               AssetImage:   assetImage,
+               AccountImage: accountImage,
+       }
+       return NewSuccessResponse(image)
+}
index 1c4045d..e23b4ae 100644 (file)
@@ -32,9 +32,10 @@ const (
        AliasPrefix = "ALS:"
        //ExternalAssetPrefix is external definition assets prefix
        ExternalAssetPrefix = "EXA"
-       indexPrefix         = "ASSIDX:"
 )
 
+var assetIndexKey = []byte("ASSETINDEX")
+
 func initNativeAsset() {
        signer := &signers.Signer{Type: "internal"}
        alias := consensus.BTMAlias
@@ -61,10 +62,6 @@ func Key(id *bc.AssetID) []byte {
        return []byte(assetPrefix + name)
 }
 
-func indexKey(xpub chainkd.XPub) []byte {
-       return []byte(indexPrefix + xpub.String())
-}
-
 //CalcExtAssetKey return store external assets key
 func CalcExtAssetKey(id *bc.AssetID) []byte {
        name := id.String()
@@ -116,21 +113,24 @@ type Asset struct {
        DefinitionMap     map[string]interface{} `json:"definition"`
 }
 
-func (reg *Registry) getNextAssetIndex(xpubs []chainkd.XPub) (*uint64, error) {
+func (reg *Registry) getNextAssetIndex() (uint64, error) {
        reg.assetIndexMu.Lock()
        defer reg.assetIndexMu.Unlock()
 
        var nextIndex uint64 = 1
 
-       if rawIndex := reg.db.Get(indexKey(xpubs[0])); rawIndex != nil {
+       if rawIndex := reg.db.Get(assetIndexKey); rawIndex != nil {
                nextIndex = binary.LittleEndian.Uint64(rawIndex) + 1
        }
 
+       reg.saveNextAssetIndex(nextIndex)
+       return nextIndex, nil
+}
+
+func (reg *Registry) saveNextAssetIndex(nextIndex uint64) {
        buf := make([]byte, 8)
        binary.LittleEndian.PutUint64(buf, nextIndex)
-       reg.db.Set(indexKey(xpubs[0]), buf)
-
-       return &nextIndex, nil
+       reg.db.Set(assetIndexKey, buf)
 }
 
 // Define defines a new Asset.
@@ -148,12 +148,12 @@ func (reg *Registry) Define(xpubs []chainkd.XPub, quorum int, definition map[str
                return nil, ErrDuplicateAlias
        }
 
-       nextAssetIndex, err := reg.getNextAssetIndex(xpubs)
+       nextAssetIndex, err := reg.getNextAssetIndex()
        if err != nil {
                return nil, errors.Wrap(err, "get asset index error")
        }
 
-       assetSigner, err := signers.Create("asset", xpubs, quorum, *nextAssetIndex)
+       assetSigner, err := signers.Create("asset", xpubs, quorum, nextAssetIndex)
        if err != nil {
                return nil, err
        }
diff --git a/asset/image.go b/asset/image.go
new file mode 100644 (file)
index 0000000..323f873
--- /dev/null
@@ -0,0 +1,64 @@
+package asset
+
+import (
+       "encoding/json"
+)
+
+type AssetImage struct {
+       Assets     []*Asset
+       AssetIndex uint64
+}
+
+func (reg *Registry) Backup() (*AssetImage, error) {
+       assetIndex, err := reg.getNextAssetIndex()
+       if err != nil {
+               return nil, err
+       }
+
+       assets := []*Asset{}
+       assetIter := reg.db.IteratorPrefix([]byte(assetPrefix))
+       defer assetIter.Release()
+
+       for assetIter.Next() {
+               asset := &Asset{}
+               if err := json.Unmarshal(assetIter.Value(), asset); err != nil {
+                       return nil, err
+               }
+               assets = append(assets, asset)
+       }
+
+       assetImage := &AssetImage{
+               AssetIndex: assetIndex,
+               Assets:     assets,
+       }
+       return assetImage, nil
+}
+
+func (reg *Registry) Restore(image *AssetImage) error {
+       localIndex, err := reg.getNextAssetIndex()
+       if err != nil {
+               return err
+       }
+
+       if localIndex > image.AssetIndex {
+               image.AssetIndex = localIndex
+       }
+
+       storeBatch := reg.db.NewBatch()
+       for _, asset := range image.Assets {
+               if existed := reg.db.Get(AliasKey(*asset.Alias)); existed != nil {
+                       return ErrDuplicateAlias
+               }
+
+               rawAsset, err := json.Marshal(asset)
+               if err != nil {
+                       return err
+               }
+
+               storeBatch.Set(AliasKey(*asset.Alias), []byte(asset.AssetID.String()))
+               storeBatch.Set(Key(&asset.AssetID), rawAsset)
+       }
+       storeBatch.Write()
+       reg.saveNextAssetIndex(image.AssetIndex)
+       return nil
+}
diff --git a/blockchain/pseudohsm/image.go b/blockchain/pseudohsm/image.go
new file mode 100644 (file)
index 0000000..4025f92
--- /dev/null
@@ -0,0 +1,42 @@
+// Package pseudohsm provides a pseudo HSM for development environments.
+package pseudohsm
+
+import (
+       "io/ioutil"
+       "path/filepath"
+)
+
+type KeyImage struct {
+       XPub XPub   `json:"xpub"`
+       XKey []byte `json:"xkey"`
+}
+
+func (h *HSM) Backup() ([]*KeyImage, error) {
+       images := []*KeyImage{}
+       xpubs := h.cache.keys()
+       for _, xpub := range xpubs {
+               xKey, err := ioutil.ReadFile(xpub.File)
+               if err != nil {
+                       return nil, err
+               }
+
+               images = append(images, &KeyImage{XPub: xpub, XKey: xKey})
+       }
+       return images, nil
+}
+
+func (h *HSM) Restore(images []*KeyImage) error {
+       for _, image := range images {
+               if ok := h.cache.hasAlias(image.XPub.Alias); ok {
+                       return ErrDuplicateKeyAlias
+               }
+
+               fileName := filepath.Base(image.XPub.File)
+               image.XPub.File = h.keyStore.JoinPath(fileName)
+               if err := writeKeyFile(image.XPub.File, image.XKey); err != nil {
+                       return nil
+               }
+               h.cache.add(image.XPub)
+       }
+       return nil
+}
index fe18e1a..ec69d36 100644 (file)
@@ -34,7 +34,7 @@ type HSM struct {
 type XPub struct {
        Alias string       `json:"alias"`
        XPub  chainkd.XPub `json:"xpub"`
-       File  string       `json:"-"`
+       File  string       `json:"file"`
 }
 
 // New method for HSM struct
@@ -180,31 +180,3 @@ func (h *HSM) HasAlias(alias string) bool {
 func (h *HSM) HasKey(xprv chainkd.XPrv) bool {
        return h.cache.hasKey(xprv.XPub())
 }
-
-//ImportXPrvKey import XPrv to chainkd
-func (h *HSM) ImportXPrvKey(auth string, alias string, xprv chainkd.XPrv) (*XPub, bool, error) {
-       if ok := h.cache.hasAlias(alias); ok {
-               return nil, false, ErrDuplicateKeyAlias
-       }
-
-       if ok := h.cache.hasKey(xprv.XPub()); ok {
-               return nil, false, ErrDuplicateKey
-       }
-
-       xpub := xprv.XPub()
-       id := uuid.NewRandom()
-       key := &XKey{
-               ID:      id,
-               KeyType: "bytom_kd",
-               XPub:    xpub,
-               XPrv:    xprv,
-               Alias:   alias,
-       }
-       file := h.keyStore.JoinPath(keyFileName(key.ID.String()))
-       if err := h.keyStore.StoreKey(file, key, auth); err != nil {
-               return nil, false, errors.Wrap(err, "storing keys")
-       }
-
-       h.cache.add(XPub{XPub: xpub, Alias: alias, File: file})
-       return &XPub{XPub: xpub, Alias: alias, File: file}, true, nil
-}