import (
"context"
- "encoding/binary"
"encoding/json"
"strings"
"sync"
const (
maxAccountCache = 1000
aliasPrefix = "ALI:"
- accountPrefix = "ACC:"
accountCPPrefix = "ACP:"
contractIndexPrefix = "ACPI:"
)
var (
miningAddressKey = []byte("miningAddress")
accountIndexKey = []byte("accountIndex")
+ accountPrefix = []byte("ACC:")
)
// pre-define errors for supporting bytom errorFormatter
//Key account store prefix
func Key(name string) []byte {
- return []byte(accountPrefix + name)
+ return append(accountPrefix, []byte(name)...)
}
//CPKey account control promgram store prefix
return append([]byte(contractIndexPrefix), []byte(accountID)...)
}
-func convertUnit64ToBytes(nextIndex uint64) []byte {
- buf := make([]byte, 8)
- binary.PutUvarint(buf, nextIndex)
- return buf
-}
-
-func convertBytesToUint64(rawIndex []byte) uint64 {
- result, _ := binary.Uvarint(rawIndex)
- return result
-}
-
// NewManager creates a new account manager
func NewManager(walletDB dbm.DB, chain *protocol.Chain) *Manager {
return &Manager{
var nextIndex uint64 = 1
if rawIndexBytes := m.db.Get(accountIndexKey); rawIndexBytes != nil {
- nextIndex = convertBytesToUint64(rawIndexBytes) + 1
+ nextIndex = common.BytesToUnit64(rawIndexBytes) + 1
}
- m.db.Set(accountIndexKey, convertUnit64ToBytes(nextIndex))
+ m.db.Set(accountIndexKey, common.Unit64ToBytes(nextIndex))
return nextIndex
}
m.accIndexMu.Lock()
defer m.accIndexMu.Unlock()
- var nextIndex uint64 = 1
+ nextIndex := uint64(1)
if rawIndexBytes := m.db.Get(contractIndexKey(accountID)); rawIndexBytes != nil {
- nextIndex = convertBytesToUint64(rawIndexBytes) + 1
+ nextIndex = common.BytesToUnit64(rawIndexBytes) + 1
}
- m.db.Set(contractIndexKey(accountID), convertUnit64ToBytes(nextIndex))
+ m.db.Set(contractIndexKey(accountID), common.Unit64ToBytes(nextIndex))
return nextIndex
}
}
// ListAccounts will return the accounts in the db
-func (m *Manager) ListAccounts(id string) ([]*Account, error) {
+func (m *Manager) ListAccounts() ([]*Account, error) {
accounts := []*Account{}
- accountIter := m.db.IteratorPrefix([]byte(accountPrefix + id))
+ accountIter := m.db.IteratorPrefix(accountPrefix)
defer accountIter.Release()
for accountIter.Next() {
import (
"encoding/json"
+
+ "github.com/bytom/common"
)
-type AccountSlice struct {
- Account *Account
- ContractIndex uint64
+// ImageSlice record info of single account
+type ImageSlice struct {
+ Account *Account `json:"account"`
+ ContractIndex uint64 `json:"contract_index"`
}
-type AccountImage struct {
- AccountSlice []*AccountSlice
- AccountIndex uint64
+// Image is the struct for hold export account data
+type Image struct {
+ Slice []*ImageSlice `json:"slices"`
+ AccountIndex uint64 `json:"account_index"`
}
-func (m *Manager) Backup() (*AccountImage, error) {
- accountSlices := []*AccountSlice{}
- accountIter := m.db.IteratorPrefix([]byte(accountPrefix))
- defer accountIter.Release()
+// Backup export all the account info into image
+func (m *Manager) Backup() (*Image, error) {
+ image := &Image{
+ Slice: []*ImageSlice{},
+ AccountIndex: m.getNextAccountIndex(),
+ }
+ accountIter := m.db.IteratorPrefix(accountPrefix)
+ defer accountIter.Release()
for accountIter.Next() {
a := &Account{}
if err := json.Unmarshal(accountIter.Value(), a); err != nil {
return nil, err
}
- accountSlice := &AccountSlice{
+ image.Slice = append(image.Slice, &ImageSlice{
Account: a,
ContractIndex: m.getNextContractIndex(a.ID),
- }
- accountSlices = append(accountSlices, accountSlice)
+ })
}
-
- accountImage := &AccountImage{
- AccountSlice: accountSlices,
- AccountIndex: m.getNextAccountIndex(),
- }
- return accountImage, nil
+ return image, nil
}
-func (m *Manager) Restore(image *AccountImage) error {
- if localIndex := m.getNextAccountIndex(); localIndex > image.AccountIndex {
- image.AccountIndex = localIndex
- }
-
+// Restore import the accountImages into account manage
+func (m *Manager) Restore(image *Image) error {
storeBatch := m.db.NewBatch()
- for _, accountSlice := range image.AccountSlice {
- rawAccount, err := json.Marshal(accountSlice.Account)
- if err != nil {
- return ErrMarshalAccount
+ for _, slice := range image.Slice {
+ if existed := m.db.Get(aliasKey(slice.Account.Alias)); existed != nil {
+ return ErrDuplicateAlias
}
- if existed := m.db.Get(aliasKey(accountSlice.Account.Alias)); existed != nil {
- return ErrDuplicateAlias
+ rawAccount, err := json.Marshal(slice.Account)
+ if err != nil {
+ return ErrMarshalAccount
}
- 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(Key(slice.Account.ID), rawAccount)
+ storeBatch.Set(aliasKey(slice.Account.Alias), []byte(slice.Account.ID))
+ storeBatch.Set(contractIndexKey(slice.Account.ID), common.Unit64ToBytes(slice.ContractIndex))
}
- storeBatch.Set(accountIndexKey, convertUnit64ToBytes(image.AccountIndex))
+ if localIndex := m.getNextAccountIndex(); localIndex < image.AccountIndex {
+ storeBatch.Set(accountIndexKey, common.Unit64ToBytes(image.AccountIndex))
+ }
storeBatch.Write()
+
+ for _, slice := range image.Slice {
+ for i := uint64(1); i < slice.ContractIndex; i++ {
+ if _, err := m.createAddress(nil, slice.Account, false); err != nil {
+ return err
+ }
+ }
+ }
return nil
}
)
// POST /list-accounts
-func (a *API) listAccounts(ctx context.Context, filter struct {
- ID string `json:"id"`
-}) Response {
- accounts, err := a.wallet.AccountMgr.ListAccounts(filter.ID)
+func (a *API) listAccounts(ctx context.Context) Response {
+ accounts, err := a.wallet.AccountMgr.ListAccounts()
if err != nil {
log.Errorf("listAccounts: %v", err)
return NewErrorResponse(err)
return NewErrorResponse(errors.New("wallet not found, please check that the wallet is open"))
}
+// WalletImage hold the ziped wallet data
type WalletImage struct {
- AccountImage *account.AccountImage
- AssetImage *asset.AssetImage
- KeyImages []*pseudohsm.KeyImage
+ AccountImage *account.Image `json:"account_image"`
+ AssetImage *asset.Image `json:"asset_image"`
+ KeyImages []*pseudohsm.KeyImage `json:"key_images"`
}
func (a *API) restoreWalletImage(ctx context.Context, image WalletImage) Response {
if err := a.wallet.Hsm.Restore(image.KeyImages); err != nil {
- return NewErrorResponse(err)
+ return NewErrorResponse(errors.Wrap(err, "restore key images"))
}
if err := a.wallet.AssetReg.Restore(image.AssetImage); err != nil {
- return NewErrorResponse(err)
+ return NewErrorResponse(errors.Wrap(err, "restore asset image"))
}
if err := a.wallet.AccountMgr.Restore(image.AccountImage); err != nil {
- return NewErrorResponse(err)
+ return NewErrorResponse(errors.Wrap(err, "restore account image"))
}
+ a.wallet.RescanBlocks()
return NewSuccessResponse(nil)
}
func (a *API) backupWalletImage() Response {
keyImages, err := a.wallet.Hsm.Backup()
if err != nil {
- return NewErrorResponse(err)
+ return NewErrorResponse(errors.Wrap(err, "backup key images"))
}
assetImage, err := a.wallet.AssetReg.Backup()
if err != nil {
- return NewErrorResponse(err)
+ return NewErrorResponse(errors.Wrap(err, "backup asset image"))
}
accountImage, err := a.wallet.AccountMgr.Backup()
if err != nil {
- return NewErrorResponse(err)
+ return NewErrorResponse(errors.Wrap(err, "backup account image"))
}
image := &WalletImage{
import (
"context"
- "encoding/binary"
"encoding/json"
"strings"
"sync"
"golang.org/x/crypto/sha3"
"github.com/bytom/blockchain/signers"
+ "github.com/bytom/common"
"github.com/bytom/consensus"
"github.com/bytom/crypto/ed25519"
"github.com/bytom/crypto/ed25519/chainkd"
const (
maxAssetCache = 1000
- assetPrefix = "ASS:"
//AliasPrefix is asset alias prefix
AliasPrefix = "ALS:"
//ExternalAssetPrefix is external definition assets prefix
ExternalAssetPrefix = "EXA"
)
-var assetIndexKey = []byte("ASSETINDEX")
+var (
+ assetIndexKey = []byte("assetIndex")
+ assetPrefix = []byte("ASS:")
+)
func initNativeAsset() {
signer := &signers.Signer{Type: "internal"}
//Key asset store prefix
func Key(id *bc.AssetID) []byte {
- name := id.String()
- return []byte(assetPrefix + name)
+ return append(assetPrefix, id.Bytes()...)
}
//CalcExtAssetKey return store external assets key
DefinitionMap map[string]interface{} `json:"definition"`
}
-func (reg *Registry) getNextAssetIndex() (uint64, error) {
+func (reg *Registry) getNextAssetIndex() uint64 {
reg.assetIndexMu.Lock()
defer reg.assetIndexMu.Unlock()
- var nextIndex uint64 = 1
-
+ nextIndex := uint64(1)
if rawIndex := reg.db.Get(assetIndexKey); rawIndex != nil {
- nextIndex = binary.LittleEndian.Uint64(rawIndex) + 1
+ nextIndex = common.BytesToUnit64(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(assetIndexKey, buf)
+ reg.db.Set(assetIndexKey, common.Unit64ToBytes(nextIndex))
+ return nextIndex
}
// Define defines a new Asset.
return nil, ErrDuplicateAlias
}
- nextAssetIndex, err := reg.getNextAssetIndex()
- if err != nil {
- return nil, errors.Wrap(err, "get asset index error")
- }
-
+ nextAssetIndex := reg.getNextAssetIndex()
assetSigner, err := signers.Create("asset", xpubs, quorum, nextAssetIndex)
if err != nil {
return nil, err
// ListAssets returns the accounts in the db
func (reg *Registry) ListAssets(id string) ([]*Asset, error) {
assets := []*Asset{DefaultNativeAsset}
- assetIter := reg.db.IteratorPrefix([]byte(assetPrefix + id))
+ assetIter := reg.db.IteratorPrefix(assetPrefix)
defer assetIter.Release()
for assetIter.Next() {
import (
"encoding/json"
+
+ log "github.com/sirupsen/logrus"
+
+ "github.com/bytom/common"
)
-type AssetImage struct {
- Assets []*Asset
- AssetIndex uint64
+// Image is the struct for hold export asset data
+type Image struct {
+ Assets []*Asset `json:"assets"`
+ AssetIndex uint64 `json:"asset_index"`
}
-func (reg *Registry) Backup() (*AssetImage, error) {
- assetIndex, err := reg.getNextAssetIndex()
- if err != nil {
- return nil, err
+// Backup export all the asset info into image
+func (reg *Registry) Backup() (*Image, error) {
+ assetImage := &Image{
+ AssetIndex: reg.getNextAssetIndex(),
+ Assets: []*Asset{},
}
- 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.Assets = append(assetImage.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
- }
-
+// Restore load the image data into asset manage
+func (reg *Registry) Restore(image *Image) error {
storeBatch := reg.db.NewBatch()
for _, asset := range image.Assets {
- if existed := reg.db.Get(AliasKey(*asset.Alias)); existed != nil {
- return ErrDuplicateAlias
+ if localAssetID := reg.db.Get(AliasKey(*asset.Alias)); localAssetID != nil {
+ if string(localAssetID) != asset.AssetID.String() {
+ return ErrDuplicateAlias
+ }
+
+ log.WithFields(log.Fields{"alias": asset.Alias, "id": asset.AssetID}).Warning("skip restore asset due to already existed")
+ continue
}
rawAsset, err := json.Marshal(asset)
return err
}
- storeBatch.Set(AliasKey(*asset.Alias), []byte(asset.AssetID.String()))
+ storeBatch.Set(AliasKey(*asset.Alias), asset.AssetID.Bytes())
storeBatch.Set(Key(&asset.AssetID), rawAsset)
}
+
+ if localIndex := reg.getNextAssetIndex(); localIndex < image.AssetIndex {
+ storeBatch.Set(assetIndexKey, common.Unit64ToBytes(image.AssetIndex))
+ }
storeBatch.Write()
- reg.saveNextAssetIndex(image.AssetIndex)
return nil
}
"path/filepath"
)
+// KeyImage is the struct for hold export key data
type KeyImage struct {
XPub XPub `json:"xpub"`
XKey []byte `json:"xkey"`
}
+// Backup export all the HSM keys into array
func (h *HSM) Backup() ([]*KeyImage, error) {
images := []*KeyImage{}
xpubs := h.cache.keys()
return images, nil
}
+// Restore import the keyImages into HSM
func (h *HSM) Restore(images []*KeyImage) error {
for _, image := range images {
if ok := h.cache.hasAlias(image.XPub.Alias); ok {
return
}
+
+func Unit64ToBytes(n uint64) []byte {
+ buf := make([]byte, 8)
+ binary.LittleEndian.PutUint64(buf, n)
+ return buf
+}
+
+func BytesToUnit64(b []byte) uint64 {
+ return binary.LittleEndian.Uint64(b)
+}
import (
"encoding/json"
+
log "github.com/sirupsen/logrus"
"github.com/tendermint/tmlibs/db"
//StatusInfo is base valid block info to handle orphan block rollback
type StatusInfo struct {
- WorkHeight uint64
- WorkHash bc.Hash
- BestHeight uint64
- BestHash bc.Hash
+ WorkHeight uint64
+ WorkHash bc.Hash
+ BestHeight uint64
+ BestHash bc.Hash
}
//Wallet is related to storing account unspent outputs
type Wallet struct {
- DB db.DB
- status StatusInfo
- AccountMgr *account.Manager
- AssetReg *asset.Registry
- Hsm *pseudohsm.HSM
- chain *protocol.Chain
+ DB db.DB
+ status StatusInfo
+ AccountMgr *account.Manager
+ AssetReg *asset.Registry
+ Hsm *pseudohsm.HSM
+ chain *protocol.Chain
+ rescanCh chan struct{}
}
//NewWallet return a new wallet instance
func NewWallet(walletDB db.DB, account *account.Manager, asset *asset.Registry, hsm *pseudohsm.HSM, chain *protocol.Chain) (*Wallet, error) {
w := &Wallet{
- DB: walletDB,
- AccountMgr: account,
- AssetReg: asset,
- chain: chain,
- Hsm: hsm,
+ DB: walletDB,
+ AccountMgr: account,
+ AssetReg: asset,
+ chain: chain,
+ Hsm: hsm,
+ rescanCh: make(chan struct{}, 1),
}
if err := w.loadWalletInfo(); err != nil {
//WalletUpdate process every valid block and reverse every invalid block which need to rollback
func (w *Wallet) walletUpdater() {
for {
+ w.getRescanNotification()
for !w.chain.InMainChain(w.status.BestHash) {
block, err := w.chain.GetBlockByHash(&w.status.BestHash)
if err != nil {
}
}
+func (w *Wallet) RescanBlocks() {
+ select {
+ case w.rescanCh <- struct{}{}:
+ default:
+ return
+ }
+}
+
+func (w *Wallet) getRescanNotification() {
+ select {
+ case <-w.rescanCh:
+ w.status.WorkHeight = 0
+ block, _ := w.chain.GetBlockByHeight(w.status.WorkHeight)
+ w.status.WorkHash = block.Hash()
+ default:
+ return
+ }
+}
func (w *Wallet) createProgram(account *account.Account, XPub *pseudohsm.XPub, index uint64) error {
for i := uint64(0); i < index; i++ {