Use common.Address type for accounts.Address

This commit is contained in:
Gustav Simonsson 2015-04-02 21:14:25 +02:00
parent 6b23094cff
commit da9fe951da
11 changed files with 70 additions and 65 deletions

View File

@ -33,7 +33,6 @@ and accounts persistence is derived from stored keys' addresses
package accounts package accounts
import ( import (
"bytes"
"crypto/ecdsa" "crypto/ecdsa"
crand "crypto/rand" crand "crypto/rand"
"errors" "errors"
@ -41,6 +40,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
) )
@ -50,12 +50,12 @@ var (
) )
type Account struct { type Account struct {
Address []byte Address common.Address
} }
type Manager struct { type Manager struct {
keyStore crypto.KeyStore2 keyStore crypto.KeyStore2
unlocked map[string]*unlocked unlocked map[common.Address]*unlocked
mutex sync.RWMutex mutex sync.RWMutex
} }
@ -67,40 +67,40 @@ type unlocked struct {
func NewManager(keyStore crypto.KeyStore2) *Manager { func NewManager(keyStore crypto.KeyStore2) *Manager {
return &Manager{ return &Manager{
keyStore: keyStore, keyStore: keyStore,
unlocked: make(map[string]*unlocked), unlocked: make(map[common.Address]*unlocked),
} }
} }
func (am *Manager) HasAccount(addr []byte) bool { func (am *Manager) HasAccount(addr common.Address) bool {
accounts, _ := am.Accounts() accounts, _ := am.Accounts()
for _, acct := range accounts { for _, acct := range accounts {
if bytes.Compare(acct.Address, addr) == 0 { if acct.Address == addr {
return true return true
} }
} }
return false return false
} }
func (am *Manager) Primary() (addr []byte, err error) { func (am *Manager) Primary() (addr common.Address, err error) {
addrs, err := am.keyStore.GetKeyAddresses() addrs, err := am.keyStore.GetKeyAddresses()
if os.IsNotExist(err) { if os.IsNotExist(err) {
return nil, ErrNoKeys return common.Address{}, ErrNoKeys
} else if err != nil { } else if err != nil {
return nil, err return common.Address{}, err
} }
if len(addrs) == 0 { if len(addrs) == 0 {
return nil, ErrNoKeys return common.Address{}, ErrNoKeys
} }
return addrs[0], nil return addrs[0], nil
} }
func (am *Manager) DeleteAccount(address []byte, auth string) error { func (am *Manager) DeleteAccount(address common.Address, auth string) error {
return am.keyStore.DeleteKey(address, auth) return am.keyStore.DeleteKey(address, auth)
} }
func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error) { func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error) {
am.mutex.RLock() am.mutex.RLock()
unlockedKey, found := am.unlocked[string(a.Address)] unlockedKey, found := am.unlocked[a.Address]
am.mutex.RUnlock() am.mutex.RUnlock()
if !found { if !found {
return nil, ErrLocked return nil, ErrLocked
@ -111,7 +111,7 @@ func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error)
// TimedUnlock unlocks the account with the given address. // TimedUnlock unlocks the account with the given address.
// When timeout has passed, the account will be locked again. // When timeout has passed, the account will be locked again.
func (am *Manager) TimedUnlock(addr []byte, keyAuth string, timeout time.Duration) error { func (am *Manager) TimedUnlock(addr common.Address, keyAuth string, timeout time.Duration) error {
key, err := am.keyStore.GetKey(addr, keyAuth) key, err := am.keyStore.GetKey(addr, keyAuth)
if err != nil { if err != nil {
return err return err
@ -124,7 +124,7 @@ func (am *Manager) TimedUnlock(addr []byte, keyAuth string, timeout time.Duratio
// Unlock unlocks the account with the given address. The account // Unlock unlocks the account with the given address. The account
// stays unlocked until the program exits or until a TimedUnlock // stays unlocked until the program exits or until a TimedUnlock
// timeout (started after the call to Unlock) expires. // timeout (started after the call to Unlock) expires.
func (am *Manager) Unlock(addr []byte, keyAuth string) error { func (am *Manager) Unlock(addr common.Address, keyAuth string) error {
key, err := am.keyStore.GetKey(addr, keyAuth) key, err := am.keyStore.GetKey(addr, keyAuth)
if err != nil { if err != nil {
return err return err
@ -157,10 +157,10 @@ func (am *Manager) Accounts() ([]Account, error) {
return accounts, err return accounts, err
} }
func (am *Manager) addUnlocked(addr []byte, key *crypto.Key) *unlocked { func (am *Manager) addUnlocked(addr common.Address, key *crypto.Key) *unlocked {
u := &unlocked{Key: key, abort: make(chan struct{})} u := &unlocked{Key: key, abort: make(chan struct{})}
am.mutex.Lock() am.mutex.Lock()
prev, found := am.unlocked[string(addr)] prev, found := am.unlocked[addr]
if found { if found {
// terminate dropLater for this key to avoid unexpected drops. // terminate dropLater for this key to avoid unexpected drops.
close(prev.abort) close(prev.abort)
@ -169,12 +169,12 @@ func (am *Manager) addUnlocked(addr []byte, key *crypto.Key) *unlocked {
// key, i.e. when Unlock was used. // key, i.e. when Unlock was used.
zeroKey(prev.PrivateKey) zeroKey(prev.PrivateKey)
} }
am.unlocked[string(addr)] = u am.unlocked[addr] = u
am.mutex.Unlock() am.mutex.Unlock()
return u return u
} }
func (am *Manager) dropLater(addr []byte, u *unlocked, timeout time.Duration) { func (am *Manager) dropLater(addr common.Address, u *unlocked, timeout time.Duration) {
t := time.NewTimer(timeout) t := time.NewTimer(timeout)
defer t.Stop() defer t.Stop()
select { select {
@ -186,9 +186,9 @@ func (am *Manager) dropLater(addr []byte, u *unlocked, timeout time.Duration) {
// was launched with. we can check that using pointer equality // was launched with. we can check that using pointer equality
// because the map stores a new pointer every time the key is // because the map stores a new pointer every time the key is
// unlocked. // unlocked.
if am.unlocked[string(addr)] == u { if am.unlocked[addr] == u {
zeroKey(u.PrivateKey) zeroKey(u.PrivateKey)
delete(am.unlocked, string(addr)) delete(am.unlocked, addr)
} }
am.mutex.Unlock() am.mutex.Unlock()
} }
@ -204,7 +204,7 @@ func zeroKey(k *ecdsa.PrivateKey) {
// USE WITH CAUTION = this will save an unencrypted private key on disk // USE WITH CAUTION = this will save an unencrypted private key on disk
// no cli or js interface // no cli or js interface
func (am *Manager) Export(path string, addr []byte, keyAuth string) error { func (am *Manager) Export(path string, addr common.Address, keyAuth string) error {
key, err := am.keyStore.GetKey(addr, keyAuth) key, err := am.keyStore.GetKey(addr, keyAuth)
if err != nil { if err != nil {
return err return err

View File

@ -391,7 +391,7 @@ func (js *jsre) unlock(call otto.FunctionCall) otto.Value {
} }
} }
am := js.ethereum.AccountManager() am := js.ethereum.AccountManager()
err = am.TimedUnlock(common.FromHex(addr), passphrase, time.Duration(seconds)*time.Second) err = am.TimedUnlock(common.HexToAddress(addr), passphrase, time.Duration(seconds)*time.Second)
if err != nil { if err != nil {
fmt.Printf("Unlock account failed '%v'\n", err) fmt.Printf("Unlock account failed '%v'\n", err)
return otto.FalseValue() return otto.FalseValue()
@ -433,7 +433,7 @@ func (js *jsre) newAccount(call otto.FunctionCall) otto.Value {
fmt.Printf("Could not create the account: %v", err) fmt.Printf("Could not create the account: %v", err)
return otto.UndefinedValue() return otto.UndefinedValue()
} }
return js.re.ToVal(common.ToHex(acct.Address)) return js.re.ToVal(acct.Address.Hex())
} }
func (js *jsre) nodeInfo(call otto.FunctionCall) otto.Value { func (js *jsre) nodeInfo(call otto.FunctionCall) otto.Value {

View File

@ -26,6 +26,7 @@ import (
"strings" "strings"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/docserver" "github.com/ethereum/go-ethereum/common/docserver"
"github.com/ethereum/go-ethereum/common/natspec" "github.com/ethereum/go-ethereum/common/natspec"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
@ -164,7 +165,7 @@ func (self *jsre) UnlockAccount(addr []byte) bool {
return false return false
} }
// TODO: allow retry // TODO: allow retry
if err := self.ethereum.AccountManager().Unlock(addr, pass); err != nil { if err := self.ethereum.AccountManager().Unlock(common.BytesToAddress(addr), pass); err != nil {
return false return false
} else { } else {
fmt.Println("Account is now unlocked for this session.") fmt.Println("Account is now unlocked for this session.")

View File

@ -365,11 +365,10 @@ func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (pass
// Load startup keys. XXX we are going to need a different format // Load startup keys. XXX we are going to need a different format
// Attempt to unlock the account // Attempt to unlock the account
passphrase = getPassPhrase(ctx, "", false) passphrase = getPassPhrase(ctx, "", false)
accbytes := common.FromHex(account) if len(account) == 0 {
if len(accbytes) == 0 {
utils.Fatalf("Invalid account address '%s'", account) utils.Fatalf("Invalid account address '%s'", account)
} }
err = am.Unlock(accbytes, passphrase) err = am.Unlock(common.StringToAddress(account), passphrase)
if err != nil { if err != nil {
utils.Fatalf("Unlock account failed '%v'", err) utils.Fatalf("Unlock account failed '%v'", err)
} }
@ -385,11 +384,11 @@ func startEth(ctx *cli.Context, eth *eth.Ethereum) {
account := ctx.GlobalString(utils.UnlockedAccountFlag.Name) account := ctx.GlobalString(utils.UnlockedAccountFlag.Name)
if len(account) > 0 { if len(account) > 0 {
if account == "primary" { if account == "primary" {
accbytes, err := am.Primary() primaryAcc, err := am.Primary()
if err != nil { if err != nil {
utils.Fatalf("no primary account: %v", err) utils.Fatalf("no primary account: %v", err)
} }
account = common.ToHex(accbytes) account = primaryAcc.Hex()
} }
unlockAccount(ctx, am, account) unlockAccount(ctx, am, account)
} }

View File

@ -232,7 +232,7 @@ func (self *Gui) loadMergedMiningOptions() {
func (gui *Gui) insertTransaction(window string, tx *types.Transaction) { func (gui *Gui) insertTransaction(window string, tx *types.Transaction) {
var inout string var inout string
from, _ := tx.From() from, _ := tx.From()
if gui.eth.AccountManager().HasAccount(common.Hex2Bytes(from.Hex())) { if gui.eth.AccountManager().HasAccount(from) {
inout = "send" inout = "send"
} else { } else {
inout = "recv" inout = "recv"

View File

@ -231,13 +231,13 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error
ecKey := ToECDSA(ethPriv) ecKey := ToECDSA(ethPriv)
key = &Key{ key = &Key{
Id: nil, Id: nil,
Address: PubkeyToAddress(ecKey.PublicKey), Address: common.BytesToAddress(PubkeyToAddress(ecKey.PublicKey)),
PrivateKey: ecKey, PrivateKey: ecKey,
} }
derivedAddr := common.Bytes2Hex(key.Address) derivedAddr := hex.EncodeToString(key.Address.Bytes()) // needed because .Hex() gives leading "0x"
expectedAddr := preSaleKeyStruct.EthAddr expectedAddr := preSaleKeyStruct.EthAddr
if derivedAddr != expectedAddr { if derivedAddr != expectedAddr {
err = errors.New("decrypted addr not equal to expected addr") err = errors.New(fmt.Sprintf("decrypted addr not equal to expected addr ", derivedAddr, expectedAddr))
} }
return key, err return key, err
} }

View File

@ -30,12 +30,13 @@ import (
"io" "io"
"code.google.com/p/go-uuid/uuid" "code.google.com/p/go-uuid/uuid"
"github.com/ethereum/go-ethereum/common"
) )
type Key struct { type Key struct {
Id uuid.UUID // Version 4 "random" for unique id not derived from key data Id uuid.UUID // Version 4 "random" for unique id not derived from key data
// to simplify lookups we also store the address // to simplify lookups we also store the address
Address []byte Address common.Address
// we only store privkey as pubkey/address can be derived from it // we only store privkey as pubkey/address can be derived from it
// privkey in this struct is always in plaintext // privkey in this struct is always in plaintext
PrivateKey *ecdsa.PrivateKey PrivateKey *ecdsa.PrivateKey
@ -63,7 +64,7 @@ type encryptedKeyJSON struct {
func (k *Key) MarshalJSON() (j []byte, err error) { func (k *Key) MarshalJSON() (j []byte, err error) {
jStruct := plainKeyJSON{ jStruct := plainKeyJSON{
k.Id, k.Id,
k.Address, k.Address.Bytes(),
FromECDSA(k.PrivateKey), FromECDSA(k.PrivateKey),
} }
j, err = json.Marshal(jStruct) j, err = json.Marshal(jStruct)
@ -80,7 +81,7 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) {
u := new(uuid.UUID) u := new(uuid.UUID)
*u = keyJSON.Id *u = keyJSON.Id
k.Id = *u k.Id = *u
k.Address = keyJSON.Address k.Address = common.BytesToAddress(keyJSON.Address)
k.PrivateKey = ToECDSA(keyJSON.PrivateKey) k.PrivateKey = ToECDSA(keyJSON.PrivateKey)
return err return err
@ -90,7 +91,7 @@ func NewKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
id := uuid.NewRandom() id := uuid.NewRandom()
key := &Key{ key := &Key{
Id: id, Id: id,
Address: PubkeyToAddress(privateKeyECDSA.PublicKey), Address: common.BytesToAddress(PubkeyToAddress(privateKeyECDSA.PublicKey)),
PrivateKey: privateKeyECDSA, PrivateKey: privateKeyECDSA,
} }
return key return key

View File

@ -68,7 +68,6 @@ import (
"bytes" "bytes"
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
"io" "io"
@ -76,6 +75,7 @@ import (
"path/filepath" "path/filepath"
"code.google.com/p/go-uuid/uuid" "code.google.com/p/go-uuid/uuid"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto/randentropy" "github.com/ethereum/go-ethereum/crypto/randentropy"
"golang.org/x/crypto/scrypt" "golang.org/x/crypto/scrypt"
) )
@ -100,7 +100,7 @@ func (ks keyStorePassphrase) GenerateNewKey(rand io.Reader, auth string) (key *K
return GenerateNewKeyDefault(ks, rand, auth) return GenerateNewKeyDefault(ks, rand, auth)
} }
func (ks keyStorePassphrase) GetKey(keyAddr []byte, auth string) (key *Key, err error) { func (ks keyStorePassphrase) GetKey(keyAddr common.Address, auth string) (key *Key, err error) {
keyBytes, keyId, err := DecryptKey(ks, keyAddr, auth) keyBytes, keyId, err := DecryptKey(ks, keyAddr, auth)
if err != nil { if err != nil {
return nil, err return nil, err
@ -113,7 +113,7 @@ func (ks keyStorePassphrase) GetKey(keyAddr []byte, auth string) (key *Key, err
return key, err return key, err
} }
func (ks keyStorePassphrase) GetKeyAddresses() (addresses [][]byte, err error) { func (ks keyStorePassphrase) GetKeyAddresses() (addresses []common.Address, err error) {
return GetKeyAddresses(ks.keysDirPath) return GetKeyAddresses(ks.keysDirPath)
} }
@ -150,7 +150,7 @@ func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
} }
keyStruct := encryptedKeyJSON{ keyStruct := encryptedKeyJSON{
key.Id, key.Id,
key.Address, key.Address.Bytes(),
cipherStruct, cipherStruct,
} }
keyJSON, err := json.Marshal(keyStruct) keyJSON, err := json.Marshal(keyStruct)
@ -161,18 +161,18 @@ func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
return WriteKeyFile(key.Address, ks.keysDirPath, keyJSON) return WriteKeyFile(key.Address, ks.keysDirPath, keyJSON)
} }
func (ks keyStorePassphrase) DeleteKey(keyAddr []byte, auth string) (err error) { func (ks keyStorePassphrase) DeleteKey(keyAddr common.Address, auth string) (err error) {
// only delete if correct passphrase is given // only delete if correct passphrase is given
_, _, err = DecryptKey(ks, keyAddr, auth) _, _, err = DecryptKey(ks, keyAddr, auth)
if err != nil { if err != nil {
return err return err
} }
keyDirPath := filepath.Join(ks.keysDirPath, hex.EncodeToString(keyAddr)) keyDirPath := filepath.Join(ks.keysDirPath, keyAddr.Hex())
return os.RemoveAll(keyDirPath) return os.RemoveAll(keyDirPath)
} }
func DecryptKey(ks keyStorePassphrase, keyAddr []byte, auth string) (keyBytes []byte, keyId []byte, err error) { func DecryptKey(ks keyStorePassphrase, keyAddr common.Address, auth string) (keyBytes []byte, keyId []byte, err error) {
fileContent, err := GetKeyFile(ks.keysDirPath, keyAddr) fileContent, err := GetKeyFile(ks.keysDirPath, keyAddr)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View File

@ -27,6 +27,7 @@ import (
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/common"
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
@ -37,10 +38,10 @@ import (
type KeyStore2 interface { type KeyStore2 interface {
// create new key using io.Reader entropy source and optionally using auth string // create new key using io.Reader entropy source and optionally using auth string
GenerateNewKey(io.Reader, string) (*Key, error) GenerateNewKey(io.Reader, string) (*Key, error)
GetKey([]byte, string) (*Key, error) // key from addr and auth string GetKey(common.Address, string) (*Key, error) // key from addr and auth string
GetKeyAddresses() ([][]byte, error) // get all addresses GetKeyAddresses() ([]common.Address, error) // get all addresses
StoreKey(*Key, string) error // store key optionally using auth string StoreKey(*Key, string) error // store key optionally using auth string
DeleteKey([]byte, string) error // delete key by addr and auth string DeleteKey(common.Address, string) error // delete key by addr and auth string
} }
type keyStorePlain struct { type keyStorePlain struct {
@ -66,7 +67,7 @@ func GenerateNewKeyDefault(ks KeyStore2, rand io.Reader, auth string) (key *Key,
return key, err return key, err
} }
func (ks keyStorePlain) GetKey(keyAddr []byte, auth string) (key *Key, err error) { func (ks keyStorePlain) GetKey(keyAddr common.Address, auth string) (key *Key, err error) {
fileContent, err := GetKeyFile(ks.keysDirPath, keyAddr) fileContent, err := GetKeyFile(ks.keysDirPath, keyAddr)
if err != nil { if err != nil {
return nil, err return nil, err
@ -77,7 +78,7 @@ func (ks keyStorePlain) GetKey(keyAddr []byte, auth string) (key *Key, err error
return key, err return key, err
} }
func (ks keyStorePlain) GetKeyAddresses() (addresses [][]byte, err error) { func (ks keyStorePlain) GetKeyAddresses() (addresses []common.Address, err error) {
return GetKeyAddresses(ks.keysDirPath) return GetKeyAddresses(ks.keysDirPath)
} }
@ -90,19 +91,19 @@ func (ks keyStorePlain) StoreKey(key *Key, auth string) (err error) {
return err return err
} }
func (ks keyStorePlain) DeleteKey(keyAddr []byte, auth string) (err error) { func (ks keyStorePlain) DeleteKey(keyAddr common.Address, auth string) (err error) {
keyDirPath := filepath.Join(ks.keysDirPath, hex.EncodeToString(keyAddr)) keyDirPath := filepath.Join(ks.keysDirPath, keyAddr.Hex())
err = os.RemoveAll(keyDirPath) err = os.RemoveAll(keyDirPath)
return err return err
} }
func GetKeyFile(keysDirPath string, keyAddr []byte) (fileContent []byte, err error) { func GetKeyFile(keysDirPath string, keyAddr common.Address) (fileContent []byte, err error) {
fileName := hex.EncodeToString(keyAddr) fileName := keyAddr.Hex()
return ioutil.ReadFile(filepath.Join(keysDirPath, fileName, fileName)) return ioutil.ReadFile(filepath.Join(keysDirPath, fileName, fileName))
} }
func WriteKeyFile(addr []byte, keysDirPath string, content []byte) (err error) { func WriteKeyFile(addr common.Address, keysDirPath string, content []byte) (err error) {
addrHex := hex.EncodeToString(addr) addrHex := addr.Hex()
keyDirPath := filepath.Join(keysDirPath, addrHex) keyDirPath := filepath.Join(keysDirPath, addrHex)
keyFilePath := filepath.Join(keyDirPath, addrHex) keyFilePath := filepath.Join(keyDirPath, addrHex)
err = os.MkdirAll(keyDirPath, 0700) // read, write and dir search for user err = os.MkdirAll(keyDirPath, 0700) // read, write and dir search for user
@ -112,7 +113,7 @@ func WriteKeyFile(addr []byte, keysDirPath string, content []byte) (err error) {
return ioutil.WriteFile(keyFilePath, content, 0600) // read, write for user return ioutil.WriteFile(keyFilePath, content, 0600) // read, write for user
} }
func GetKeyAddresses(keysDirPath string) (addresses [][]byte, err error) { func GetKeyAddresses(keysDirPath string) (addresses []common.Address, err error) {
fileInfos, err := ioutil.ReadDir(keysDirPath) fileInfos, err := ioutil.ReadDir(keysDirPath)
if err != nil { if err != nil {
return nil, err return nil, err
@ -122,7 +123,7 @@ func GetKeyAddresses(keysDirPath string) (addresses [][]byte, err error) {
if err != nil { if err != nil {
continue continue
} }
addresses = append(addresses, address) addresses = append(addresses, common.BytesToAddress(address))
} }
return addresses, err return addresses, err
} }

View File

@ -386,14 +386,17 @@ func (s *Ethereum) StartMining(threads int) error {
func (s *Ethereum) Etherbase() (eb common.Address, err error) { func (s *Ethereum) Etherbase() (eb common.Address, err error) {
eb = s.etherbase eb = s.etherbase
if (eb == common.Address{}) { if (eb == common.Address{}) {
var ebbytes []byte primary, err := s.accountManager.Primary()
ebbytes, err = s.accountManager.Primary() if err != nil {
eb = common.BytesToAddress(ebbytes) return eb, err
if (eb == common.Address{}) {
err = fmt.Errorf("no accounts found")
} }
if (primary == common.Address{}) {
err = fmt.Errorf("no accounts found")
return eb, err
}
eb = primary
} }
return return eb, nil
} }
func (s *Ethereum) StopMining() { s.miner.Stop() } func (s *Ethereum) StopMining() { s.miner.Stop() }

View File

@ -365,7 +365,7 @@ func (self *XEth) Accounts() []string {
accounts, _ := self.backend.AccountManager().Accounts() accounts, _ := self.backend.AccountManager().Accounts()
accountAddresses := make([]string, len(accounts)) accountAddresses := make([]string, len(accounts))
for i, ac := range accounts { for i, ac := range accounts {
accountAddresses[i] = common.ToHex(ac.Address) accountAddresses[i] = ac.Address.Str()
} }
return accountAddresses return accountAddresses
} }