aergoio/aergo

View on GitHub
account/accountservice.go

Summary

Maintainability
C
1 day
Test Coverage
F
24%
package account

import (
    "sync"

    "github.com/aergoio/aergo-actor/actor"
    "github.com/aergoio/aergo-lib/log"
    "github.com/aergoio/aergo/v2/account/key"
    cfg "github.com/aergoio/aergo/v2/config"
    "github.com/aergoio/aergo/v2/contract/name"
    "github.com/aergoio/aergo/v2/pkg/component"
    "github.com/aergoio/aergo/v2/state"
    "github.com/aergoio/aergo/v2/state/statedb"
    "github.com/aergoio/aergo/v2/types"
    "github.com/aergoio/aergo/v2/types/message"
)

type AccountService struct {
    *component.BaseComponent
    cfg         *cfg.Config
    sdb         *state.ChainStateDB
    ks          *key.Store
    accountLock sync.RWMutex
    accounts    []*types.Account
    testConfig  bool
}

// NewAccountService create account service
func NewAccountService(cfg *cfg.Config, sdb *state.ChainStateDB) *AccountService {
    actor := &AccountService{
        cfg: cfg,
        sdb: sdb,
    }
    actor.BaseComponent = component.NewBaseComponent(message.AccountsSvc, actor, log.NewLogger("account"))

    return actor
}

func (as *AccountService) BeforeStart() {
    as.ks = key.NewStore(as.cfg.DataDir, as.cfg.Account.UnlockTimeout)

    as.accounts = []*types.Account{}
    addresses, err := as.ks.GetAddresses()
    if err != nil {
        as.Logger.Error().Err(err).Msg("could not open addresses")
    }
    for _, v := range addresses {
        as.accounts = append(as.accounts, &types.Account{Address: v})
    }
}

func (as *AccountService) AfterStart() {}

func (as *AccountService) BeforeStop() {
    as.ks.CloseStore()
    as.accounts = nil
}

func (as *AccountService) Statistics() *map[string]interface{} {
    return &map[string]interface{}{
        "totalaccounts": len(as.accounts),
        "personal":      as.cfg.Personal,
        "config":        as.cfg.Account,
    }
}
func (as *AccountService) resolveName(namedAddress []byte) ([]byte, error) {
    scs, err := statedb.GetNameAccountState(as.sdb.GetStateDB())
    if err != nil {
        return nil, err
    }
    return name.GetAddress(scs, namedAddress), nil
}

func (as *AccountService) Receive(context actor.Context) {

    switch msg := context.Message().(type) {
    case *message.GetAccounts:
        accountList := as.getAccounts()
        context.Respond(&message.GetAccountsRsp{Accounts: &types.AccountList{Accounts: accountList}})
    case *message.CreateAccount:
        account, _ := as.createAccount(msg.Passphrase)
        context.Respond(&message.CreateAccountRsp{Account: account})
    case *message.LockAccount:
        actualAddress := msg.Account.Address
        var err error
        if len(actualAddress) == types.NameLength {
            actualAddress, err = as.resolveName(actualAddress)
            if err != nil {
                context.Respond(&message.AccountRsp{
                    Account: &types.Account{Address: actualAddress},
                    Err:     err,
                })
            }
        }
        account, err := as.lockAccount(actualAddress, msg.Passphrase)
        context.Respond(&message.AccountRsp{Account: account, Err: err})
    case *message.UnlockAccount:
        actualAddress := msg.Account.Address
        var err error
        if len(actualAddress) == types.NameLength {
            actualAddress, err = as.resolveName(actualAddress)
            if err != nil {
                context.Respond(&message.AccountRsp{
                    Account: &types.Account{Address: actualAddress},
                    Err:     err,
                })
            }
        }
        account, err := as.unlockAccount(actualAddress, msg.Passphrase)
        context.Respond(&message.AccountRsp{Account: account, Err: err})
    case *message.ImportAccount:
        var account *types.Account
        var err error
        if msg.Wif != nil {
            account, err = as.importAccount(msg.Wif, msg.OldPass, msg.NewPass)
        } else {
            account, err = as.importAccountFromKeystore(msg.Keystore, msg.OldPass, msg.NewPass)
        }
        context.Respond(&message.ImportAccountRsp{Account: account, Err: err})
    case *message.ExportAccount:
        var wif []byte
        var err error
        if msg.AsKeystore {
            wif, err = as.exportAccountKeystore(msg.Account.Address, msg.Pass)
        } else {
            wif, err = as.exportAccount(msg.Account.Address, msg.Pass)
        }
        context.Respond(&message.ExportAccountRsp{Wif: wif, Err: err})
    case *message.SignTx:
        var err error
        actualAddress := msg.Tx.GetBody().GetAccount()
        if len(actualAddress) == types.NameLength {
            actualAddress, err = as.resolveName(msg.Tx.GetBody().GetAccount())
            if err != nil {
                context.Respond(&message.SignTxRsp{Tx: nil, Err: err})
            }
            msg.Requester = actualAddress
        }
        err = as.signTx(context, msg)
        if err != nil {
            context.Respond(&message.SignTxRsp{Tx: nil, Err: err})
        }
    case *message.VerifyTx:
        err := as.verifyTx(msg.Tx)
        if err != nil {
            context.Respond(&message.VerifyTxRsp{Tx: nil, Err: err})
        } else {
            context.Respond(&message.VerifyTxRsp{Tx: msg.Tx, Err: nil})
        }
    }
}

func (as *AccountService) getAccounts() []*types.Account {
    as.accountLock.RLock()
    defer as.accountLock.RUnlock()
    return as.accounts
}

func (as *AccountService) addAccount(account *types.Account) {
    as.accountLock.Lock()
    defer as.accountLock.Unlock()
    as.accounts = append(as.accounts, account)
}

func (as *AccountService) createAccount(passphrase string) (*types.Account, error) {
    address, err := as.ks.CreateKey(passphrase)
    if err != nil {
        return nil, err
    }
    account := types.NewAccount(address)
    as.addAccount(account)
    return account, nil
}

func (as *AccountService) importAccount(wif []byte, old string, new string) (*types.Account, error) {
    address, err := as.ks.ImportKey(wif, old, new)
    if err != nil {
        return nil, err
    }
    account := &types.Account{Address: address}
    as.addAccount(account)
    return account, nil
}

func (as *AccountService) importAccountFromKeystore(keystore []byte, old string, new string) (*types.Account, error) {
    privateKey, err := key.LoadKeystore(keystore, old)
    if err != nil {
        return nil, err
    }
    address, err := as.ks.AddKey(privateKey, new)
    if err != nil {
        return nil, err
    }
    account := &types.Account{Address: address}
    as.addAccount(account)
    return account, nil
}

func (as *AccountService) exportAccount(address []byte, pass string) ([]byte, error) {
    wif, err := as.ks.ExportKey(address, pass)
    if err != nil {
        return nil, err
    }
    return wif, nil
}

func (as *AccountService) exportAccountKeystore(address []byte, pass string) ([]byte, error) {
    privateKey, err := as.ks.GetKey(address, pass)
    if err != nil {
        return nil, err
    }
    wif, err := key.GetKeystore(privateKey, pass)
    if err != nil {
        return nil, err
    }
    return wif, nil
}

func (as *AccountService) unlockAccount(address []byte, passphrase string) (*types.Account, error) {
    addr, err := as.ks.Unlock(address, passphrase)
    if err != nil {
        as.Warn().Err(err).Msg("could not find the key")
        return nil, err
    }
    return &types.Account{Address: addr}, nil
}

func (as *AccountService) lockAccount(address []byte, passphrase string) (*types.Account, error) {
    addr, err := as.ks.Lock(address, passphrase)
    if err != nil {
        as.Warn().Err(err).Msg("could not load the key")
        return nil, err
    }
    return &types.Account{Address: addr}, nil
}

func (as *AccountService) signTx(c actor.Context, msg *message.SignTx) error {
    //sign tx
    prop := actor.FromInstance(NewSigner(as.ks))
    signer := c.Spawn(prop)
    signer.Request(msg, c.Sender())
    return nil
}

func (as *AccountService) verifyTx(tx *types.Tx) error {
    return as.ks.VerifyTx(tx)
}