forked from hectorchu/gonano
/
wallet.go
144 lines (133 loc) · 3.13 KB
/
wallet.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package wallet
import (
"github.com/hectorchu/gonano/rpc"
"github.com/hectorchu/gonano/util"
)
// Wallet represents a wallet.
type Wallet struct {
seed []byte
isBip39 bool
nextIndex uint32
accounts map[string]*Account
RPC, RPCWork rpc.Client
impl interface {
deriveAccount(*Account) error
signBlock(*Account, *rpc.Block) error
}
}
// NewWallet creates a new wallet.
func NewWallet(seed []byte) (w *Wallet, err error) {
w = newWallet(seed)
return
}
// NewBip39Wallet creates a new BIP39 wallet.
func NewBip39Wallet(mnemonic, password string) (w *Wallet, err error) {
seed, err := newBip39Seed(mnemonic, password)
if err != nil {
return
}
w = newWallet(seed)
w.isBip39 = true
return
}
// NewLedgerWallet creates a new Ledger wallet.
func NewLedgerWallet() (w *Wallet, err error) {
w = newWallet(nil)
w.impl = ledgerImpl{}
return
}
func newWallet(seed []byte) *Wallet {
return &Wallet{
seed: seed,
accounts: make(map[string]*Account),
RPC: rpc.Client{URL: "https://mynano.ninja/api/node"},
RPCWork: rpc.Client{URL: "http://[::1]:7076"},
impl: seedImpl{},
}
}
// ScanForAccounts scans for accounts.
func (w *Wallet) ScanForAccounts() (err error) {
accounts := make([]string, 10)
for i := range accounts {
a, err := w.NewAccount(nil)
if err != nil {
return err
}
accounts[i] = a.Address()
}
balances, err := w.RPC.AccountsBalances(accounts)
if err != nil {
return
}
frontiers, err := w.RPC.AccountsFrontiers(accounts)
if err != nil {
return
}
i := len(accounts) - 1
for ; i >= 0; i-- {
if balances[accounts[i]].Pending.Sign() > 0 {
break
}
if frontiers[accounts[i]] != nil {
break
}
w.nextIndex = w.accounts[accounts[i]].index
delete(w.accounts, accounts[i])
}
if i < 5 {
return
}
return w.ScanForAccounts()
}
// NewAccount creates a new account.
func (w *Wallet) NewAccount(index *uint32) (a *Account, err error) {
index2 := w.nextIndex
if index != nil {
index2 = *index
}
a = &Account{w: w, index: index2}
if err = w.impl.deriveAccount(a); err != nil {
return
}
if a.address, err = util.PubkeyToAddress(a.pubkey); err != nil {
return
}
if index == nil {
w.nextIndex++
}
if _, ok := w.accounts[a.address]; !ok {
w.accounts[a.address] = a
} else if index == nil {
return w.NewAccount(nil)
}
return
}
// GetAccount gets the account with address or nil if not found.
func (w *Wallet) GetAccount(address string) *Account {
return w.accounts[address]
}
// GetAccounts gets all the accounts in the wallet.
func (w *Wallet) GetAccounts() (accounts []*Account) {
accounts = make([]*Account, 0, len(w.accounts))
for _, account := range w.accounts {
accounts = append(accounts, account)
}
return
}
// ReceivePendings pockets all pending amounts.
func (w *Wallet) ReceivePendings() (err error) {
accounts := make([]string, 0, len(w.accounts))
for address := range w.accounts {
accounts = append(accounts, address)
}
pendings, err := w.RPC.AccountsPending(accounts, -1)
if err != nil {
return
}
for account, pendings := range pendings {
if err = w.accounts[account].receivePendings(pendings); err != nil {
return
}
}
return
}