delayedACPsMu sync.Mutex
delayedACPs map[*txbuilder.TemplateBuilder][]*CtrlProgram
- accIndexMu sync.Mutex
+ accIndexMu sync.Mutex
}
// ExpireReservations removes reservations that have expired periodically.
cachedID, ok := m.aliasCache.Get(alias)
m.cacheMu.Unlock()
if ok {
- return m.findByID(ctx, cachedID.(string))
+ return m.FindByID(ctx, cachedID.(string))
}
rawID := m.db.Get(aliasKey(alias))
m.cacheMu.Lock()
m.aliasCache.Add(alias, accountID)
m.cacheMu.Unlock()
- return m.findByID(ctx, accountID)
+ return m.FindByID(ctx, accountID)
}
-// findByID returns an account's Signer record by its ID.
-func (m *Manager) findByID(ctx context.Context, id string) (*Account, error) {
+// FindByID returns an account's Signer record by its ID.
+func (m *Manager) FindByID(ctx context.Context, id string) (*Account, error) {
m.cacheMu.Lock()
cachedAccount, ok := m.cache.Get(id)
m.cacheMu.Unlock()
// CreateAddress generate an address for the select account
func (m *Manager) CreateAddress(ctx context.Context, accountID string, change bool) (cp *CtrlProgram, err error) {
- account, err := m.findByID(ctx, accountID)
+ account, err := m.FindByID(ctx, accountID)
if err != nil {
return nil, err
}
return cp, nil
}
+// listAddressesById
+func (m *Manager) ListCtrlProgramsByXpubs(ctx context.Context, xpubs []chainkd.XPub) ([]*CtrlProgram, error) {
+ cps, err := m.ListControlProgram()
+ if err != nil {
+ return nil, err
+ }
+
+ var result []*CtrlProgram
+ for _, cp := range cps {
+ if cp.Address == "" || chainkd.CompareTwoXPubs(cp.XPubs, xpubs) != 0 {
+ continue
+ }
+ result = append(result, cp)
+ }
+ return result, nil
+}
+
func (m *Manager) createP2PKH(ctx context.Context, account *Account, change bool) (*CtrlProgram, error) {
idx := m.getNextXpubsIndex(account.Signer.XPubs)
path := signers.Path(account.Signer, signers.AccountKeySpace, idx)
return &CtrlProgram{
AccountID: account.ID,
+ XPubs: account.Signer.XPubs,
Address: address.EncodeAddress(),
KeyIndex: idx,
ControlProgram: control,
return &CtrlProgram{
AccountID: account.ID,
+ XPubs: account.Signer.XPubs,
Address: address.EncodeAddress(),
KeyIndex: idx,
ControlProgram: control,
//CtrlProgram is structure of account control program
type CtrlProgram struct {
AccountID string
+ XPubs []chainkd.XPub
Address string
KeyIndex uint64
ControlProgram []byte
return nil
}
+func (m *Manager) DeleteAccountControlProgram(prog []byte) {
+ var hash common.Hash
+ sha3pool.Sum256(hash[:], prog)
+ m.db.Delete(CPKey(hash))
+}
+
// IsLocalControlProgram check is the input control program belong to local
func (m *Manager) IsLocalControlProgram(prog []byte) bool {
var hash common.Hash
func (m *Manager) DeleteAccount(aliasOrId string) (err error) {
account := &Account{}
if account, err = m.FindByAlias(nil, aliasOrId); err != nil {
- if account, err = m.findByID(nil, aliasOrId); err != nil {
+ if account, err = m.FindByID(nil, aliasOrId); err != nil {
return err
}
}
testutil.FatalErr(t, err)
}
- found, err := m.findByID(ctx, account.ID)
+ found, err := m.FindByID(ctx, account.ID)
if err != nil {
t.Errorf("unexpected error %v", err)
}
testutil.FatalErr(t, err)
}
- found, err := m.findByID(ctx, account1.ID)
+ found, err := m.FindByID(ctx, account1.ID)
if err != nil {
t.Errorf("expected account %v should be deleted", found)
}
testutil.FatalErr(t, err)
}
- found, err = m.findByID(ctx, account2.ID)
+ found, err = m.FindByID(ctx, account2.ID)
if err != nil {
t.Errorf("expected account %v should be deleted", found)
}
ctx := context.Background()
account := m.createTestAccount(ctx, t, "", nil)
- found, err := m.findByID(ctx, account.ID)
+ found, err := m.FindByID(ctx, account.ID)
if err != nil {
testutil.FatalErr(t, err)
}
return txbuilder.MissingFieldsError(missing...)
}
- acct, err := a.accounts.findByID(ctx, a.AccountID)
+ acct, err := a.accounts.FindByID(ctx, a.AccountID)
if err != nil {
return errors.Wrap(err, "get account info")
}
var accountSigner *signers.Signer
if len(res.Source.AccountID) != 0 {
- account, err := a.accounts.findByID(ctx, res.Source.AccountID)
+ account, err := a.accounts.FindByID(ctx, res.Source.AccountID)
if err != nil {
return err
}
import (
"context"
+
log "github.com/sirupsen/logrus"
"github.com/bytom/account"
// POST /create-account
func (a *API) createAccount(ctx context.Context, ins struct {
- RootXPubs []chainkd.XPub `json:"root_xpubs"`
- Quorum int `json:"quorum"`
- Alias string `json:"alias"`
+ RootXPubs []chainkd.XPub `json:"root_xpubs"`
+ Quorum int `json:"quorum"`
+ Alias string `json:"alias"`
}) Response {
acc, err := a.wallet.AccountMgr.Create(ctx, ins.RootXPubs, ins.Quorum, ins.Alias)
if err != nil {
AccountAlias string `json:"account_alias"`
}) Response {
accountID := ins.AccountID
+ var target *account.Account
if ins.AccountAlias != "" {
acc, err := a.wallet.AccountMgr.FindByAlias(ctx, ins.AccountAlias)
if err != nil {
return NewErrorResponse(err)
}
-
- accountID = acc.ID
+ target = acc
+ } else {
+ acc, err := a.wallet.AccountMgr.FindByID(ctx, accountID)
+ if err != nil {
+ return NewErrorResponse(err)
+ }
+ target = acc
}
- cps, err := a.wallet.AccountMgr.ListControlProgram()
+ cps, err := a.wallet.AccountMgr.ListCtrlProgramsByXpubs(ctx, target.XPubs)
if err != nil {
return NewErrorResponse(err)
}
var addresses []*addressResp
for _, cp := range cps {
- if cp.Address == "" || (accountID != "" && accountID != cp.AccountID) {
- continue
- }
-
- accountAlias := a.wallet.AccountMgr.GetAliasByID(cp.AccountID)
addresses = append(addresses, &addressResp{
- AccountAlias: accountAlias,
+ AccountAlias: target.Alias,
AccountID: cp.AccountID,
Address: cp.Address,
Change: cp.Change,
"github.com/bytom/crypto/ed25519"
"github.com/bytom/crypto/ed25519/ecmath"
+ "bytes"
)
type (
XPub [64]byte
)
-var one = [32]byte{1}
+// CompareTwoXPubs
+func CompareTwoXPubs(a, b []XPub) int {
+ for i, xpub := range a {
+ result := bytes.Compare(xpub[:], b[i][:])
+ if result != 0 {
+ return result
+ }
+ }
+ return 0
+}
// NewXPrv takes a source of random bytes and produces a new XPrv.
// If r is nil, crypto/rand.Reader is used.
transactionLoop:
for pos, tx := range b.Transactions {
statusFail, _ := txStatus.GetStatus(pos)
+ isLocal := false
for _, v := range tx.Outputs {
var hash [32]byte
sha3pool.Sum256(hash[:], v.ControlProgram)
if bytes := w.DB.Get(account.CPKey(hash)); bytes != nil {
+ cp := &account.CtrlProgram{}
+ if err := json.Unmarshal(bytes, cp); err == nil {
+ w.status.selfProgramsOnChain.Add(cp.Address)
+ }
+
annotatedTxs = append(annotatedTxs, w.buildAnnotatedTransaction(tx, b, statusFail, pos))
- continue transactionLoop
+ isLocal = true
}
}
+ if isLocal {
+ continue
+ }
+
for _, v := range tx.Inputs {
outid, err := v.SpentOutputID()
if err != nil {
--- /dev/null
+package wallet
+
+type Set map[interface{}]bool
+
+func NewSet() Set {
+ return make(Set)
+}
+
+// Add Add the specified element to this set if it is not already present (optional operation)
+func (s *Set) Add(i interface{}) bool {
+ _, found := (*s)[i]
+ if found {
+ return false //False if it existed already
+ }
+
+ (*s)[i] = true
+ return true
+}
+
+// Contains Returns true if this set contains the specified elements
+func (s *Set) Contains(i ...interface{}) bool {
+ for _, val := range i {
+ if _, ok := (*s)[val]; !ok {
+ return false
+ }
+ }
+ return true
+}
WorkHash bc.Hash
BestHeight uint64
BestHash bc.Hash
+ selfProgramsOnChain Set
}
//KeyInfo is key import status
type KeyInfo struct {
+ account account.Account
Alias string `json:"alias"`
XPub chainkd.XPub `json:"xpub"`
Percent uint8 `json:"percent"`
//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,
- rescanProgress: make(chan struct{}, 1),
- importingKeysInfo: make([]KeyInfo, 0),
+ DB: walletDB,
+ AccountMgr: account,
+ AssetReg: asset,
+ chain: chain,
+ Hsm: hsm,
+ rescanProgress: make(chan struct{}, 1),
+ importingKeysInfo: make([]KeyInfo, 0),
}
if err := w.loadWalletInfo(); err != nil {
return json.Unmarshal(rawWallet, &w.status)
}
+ w.status.selfProgramsOnChain = NewSet()
block, err := w.chain.GetBlockByHeight(0)
if err != nil {
return err
func (w *Wallet) walletUpdater() {
for {
getRescanNotification(w)
- updateRescanStatus(w)
+ w.updateRescanStatus()
for !w.chain.InMainChain(w.status.BestHash) {
block, err := w.chain.GetBlockByHash(&w.status.BestHash)
if err != nil {
}
w.ImportingPrivateKey = true
tmp := KeyInfo{
+ account: *account,
Alias: keyAlias,
XPub: XPub.XPub,
Complete: false,
}
//updateRescanStatus mark private key import process `Complete` if rescan finished
-func updateRescanStatus(w *Wallet) {
+func (w *Wallet) updateRescanStatus() {
if !w.ImportingPrivateKey {
return
}
for _, keyInfo := range w.importingKeysInfo {
keyInfo.Percent = percent
}
+ w.commitkeysInfo()
return
}
for _, keyInfo := range w.importingKeysInfo {
keyInfo.Percent = 100
keyInfo.Complete = true
+
+ if cps, err := w.AccountMgr.ListCtrlProgramsByXpubs(nil, keyInfo.account.XPubs); err == nil {
+ for _, cp := range cps {
+ if !w.status.selfProgramsOnChain.Contains(cp.Address) {
+ w.AccountMgr.DeleteAccountControlProgram(cp.ControlProgram)
+ }
+ }
+ }
}
w.commitkeysInfo()
- // TODO: delete the generated but not used addresses
}
func mockWallet(walletDB dbm.DB, account *account.Manager, asset *asset.Registry, chain *protocol.Chain) *Wallet {
return &Wallet{
- DB: walletDB,
- AccountMgr: account,
- AssetReg: asset,
- chain: chain,
- rescanProgress: make(chan struct{}, 1),
+ DB: walletDB,
+ AccountMgr: account,
+ AssetReg: asset,
+ chain: chain,
+ rescanProgress: make(chan struct{}, 1),
+ selfProgramsOnChain: NewSet(),
}
}