Skip to content

Commit d994556

Browse files
authored
wallet: No taking voting addresses.
Prevent methods from getting voting account addresses. These addresses should not be sent funds and are intended to be used only for submission scripts and signatures.
1 parent 9a8688e commit d994556

File tree

2 files changed

+139
-35
lines changed

2 files changed

+139
-35
lines changed

rpc/grpc_example/main.go

Lines changed: 113 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ import (
55
"crypto/tls"
66
"crypto/x509"
77
"encoding/hex"
8+
"errors"
89
"fmt"
910
"io/ioutil"
11+
"os"
1012
"path/filepath"
1113

14+
"google.golang.org/grpc/codes"
15+
"google.golang.org/grpc/status"
16+
1217
pb "decred.org/dcrwallet/v2/rpc/walletrpc"
1318
"google.golang.org/grpc"
1419
"google.golang.org/grpc/credentials"
@@ -24,29 +29,35 @@ var (
2429
)
2530

2631
func main() {
32+
if err := run(); err != nil {
33+
fmt.Println(err)
34+
os.Exit(1)
35+
}
36+
os.Exit(0)
37+
}
38+
39+
func run() error {
40+
ctx, cancel := context.WithCancel(context.Background())
41+
defer cancel()
2742
serverCAs := x509.NewCertPool()
2843
serverCert, err := ioutil.ReadFile(certificateFile)
2944
if err != nil {
30-
fmt.Println(err)
31-
return
45+
return err
3246
}
3347
if !serverCAs.AppendCertsFromPEM(serverCert) {
34-
fmt.Printf("no certificates found in %s\n", certificateFile)
35-
return
48+
return fmt.Errorf("no certificates found in %s\n", certificateFile)
3649
}
3750
keypair, err := tls.LoadX509KeyPair(walletClientCertFile, walletClientKeyFile)
3851
if err != nil {
39-
fmt.Println(err)
40-
return
52+
return err
4153
}
4254
creds := credentials.NewTLS(&tls.Config{
4355
Certificates: []tls.Certificate{keypair},
4456
RootCAs: serverCAs,
4557
})
4658
conn, err := grpc.Dial("localhost:19111", grpc.WithTransportCredentials(creds))
4759
if err != nil {
48-
fmt.Println(err)
49-
return
60+
return err
5061
}
5162
defer conn.Close()
5263
c := pb.NewWalletServiceClient(conn)
@@ -55,33 +66,34 @@ func main() {
5566
AccountNumber: 0,
5667
RequiredConfirmations: 1,
5768
}
58-
balanceResponse, err := c.Balance(context.Background(), balanceRequest)
69+
balanceResponse, err := c.Balance(ctx, balanceRequest)
5970
if err != nil {
60-
fmt.Println(err)
61-
return
71+
return err
6272
}
6373

6474
fmt.Println("Spendable balance: ", dcrutil.Amount(balanceResponse.Spendable))
6575

66-
decodedTx := func(tx string) {
76+
decodedTx := func(tx string) error {
6777
rawTx, _ := hex.DecodeString(tx)
6878
dmClient := pb.NewDecodeMessageServiceClient(conn)
6979
decodeRequest := &pb.DecodeRawTransactionRequest{
7080
SerializedTransaction: rawTx,
7181
}
72-
decodeResponse, err := dmClient.DecodeRawTransaction(context.Background(), decodeRequest)
82+
decodeResponse, err := dmClient.DecodeRawTransaction(ctx, decodeRequest)
7383
if err != nil {
74-
fmt.Println(err)
75-
return
84+
return err
7685
}
7786

7887
// tj, _ := json.MarshalIndent(decodeResponse.Transaction, "", " ")
7988
// fmt.Println(string(tj))
8089
fmt.Println(prototext.MarshalOptions{Multiline: true}.Format(decodeResponse.Transaction))
90+
return nil
8191
}
8292

8393
for _, tx := range txns {
84-
decodedTx(tx)
94+
if err := decodedTx(tx); err != nil {
95+
return err
96+
}
8597
}
8698

8799
wsClient := pb.NewWalletServiceClient(conn)
@@ -91,39 +103,98 @@ func main() {
91103
// testing wallet.
92104
scriptB, err := hex.DecodeString(script)
93105
if err != nil {
94-
fmt.Println(err)
95-
return
106+
return err
96107
}
97108
importScriptRequest := &pb.ImportScriptRequest{
98109
Script: scriptB,
99110
}
100-
_, err = wsClient.ImportScript(context.Background(), importScriptRequest)
111+
_, err = wsClient.ImportScript(ctx, importScriptRequest)
101112
if err != nil {
102-
fmt.Println(err)
103-
return
113+
return err
104114
}
105115
}
106116

107-
// Add an owned address to validated addresses.
108-
nextAddressResp, err := wsClient.NextAddress(context.Background(), new(pb.NextAddressRequest))
117+
const acctName = "testing_acct"
118+
var (
119+
seed [32]byte
120+
acctPass = []byte("pass123")
121+
acctN uint32
122+
)
123+
seed[31] = 1
124+
125+
// Import a voting account from seed.
126+
importVAFSRequest := &pb.ImportVotingAccountFromSeedRequest{
127+
Seed: seed[:],
128+
Name: acctName,
129+
Passphrase: acctPass,
130+
}
131+
importVAFSReqResp, err := wsClient.ImportVotingAccountFromSeed(ctx, importVAFSRequest)
109132
if err != nil {
110-
fmt.Println(err)
111-
return
133+
if status.Code(err) != codes.AlreadyExists {
134+
return err
135+
}
136+
acctsResp, err := wsClient.Accounts(ctx, &pb.AccountsRequest{})
137+
if err != nil {
138+
return err
139+
}
140+
var found bool
141+
for _, acct := range acctsResp.Accounts {
142+
if acct.AccountName == acctName {
143+
found = true
144+
acctN = acct.AccountNumber
145+
}
146+
}
147+
if !found {
148+
return errors.New("testing account not found")
149+
}
150+
} else {
151+
acctN = importVAFSReqResp.Account
112152
}
113-
validateAddrs = append(validateAddrs, nextAddressResp.Address)
153+
154+
fmt.Println("Testing account number is", acctN)
155+
fmt.Println()
156+
157+
// Add requests for addresses from the voting account which should fail.
158+
nextAddrs = append(nextAddrs,
159+
nextAddr{name: "imported voting account external", acct: acctN, branch: 0, wantErr: true},
160+
nextAddr{name: "imported voting account internal", acct: acctN, branch: 1, wantErr: true})
161+
162+
for _, addr := range nextAddrs {
163+
// Add an owned address to validated addresses.
164+
nextAddrReq := &pb.NextAddressRequest{
165+
Account: addr.acct,
166+
Kind: addr.branch,
167+
GapPolicy: pb.NextAddressRequest_GAP_POLICY_IGNORE,
168+
}
169+
nextAddressResp, err := wsClient.NextAddress(context.Background(), nextAddrReq)
170+
if addr.wantErr {
171+
if err != nil {
172+
continue
173+
}
174+
return fmt.Errorf("nextAddrs: expected error for %s", addr.name)
175+
}
176+
if err != nil {
177+
return err
178+
}
179+
validateAddrs = append(validateAddrs, nextAddressResp.Address)
180+
}
181+
182+
fmt.Println("ValidateAddress...")
183+
fmt.Println()
114184

115185
for _, addr := range validateAddrs {
116186
validateAddrRequest := &pb.ValidateAddressRequest{
117187
Address: addr,
118188
}
119-
validateAddrResp, err := wsClient.ValidateAddress(context.Background(), validateAddrRequest)
189+
validateAddrResp, err := wsClient.ValidateAddress(ctx, validateAddrRequest)
120190
if err != nil {
121-
fmt.Println(err)
122-
return
191+
return err
123192
}
124193
fmt.Println(validateAddrResp.ScriptType)
125194
fmt.Println(prototext.MarshalOptions{Multiline: true}.Format(validateAddrResp))
126195
}
196+
197+
return nil
127198
}
128199

129200
var txns = []string{
@@ -168,4 +239,17 @@ var validateAddrs = []string{
168239
"TcfdqCrK2fiFJBZnGj5N6xs6rMsbQBsJBYf", // TSPEND
169240
"TcrzaAVMbFURm1PpukWru8yE2uBTjvQePoa", // 2 of 2 multisig
170241
"TckSpBht36nMZgnDDjv7xaHUrgCyJpxQiLA", // NonStandard
242+
243+
"TsSAzyUaa2KSytEuu1hiGdeYJqu4om63ZQb", // Address at imported account/0/0
171244
}
245+
246+
type nextAddr struct {
247+
name string
248+
acct uint32
249+
branch pb.NextAddressRequest_Kind
250+
wantErr bool
251+
}
252+
253+
var nextAddrs = []nextAddr{{
254+
name: "default external",
255+
}}

wallet/addresses.go

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -742,22 +742,33 @@ func (w *Wallet) markUsedAddress(op errors.Op, dbtx walletdb.ReadWriteTx, addr u
742742
func (w *Wallet) NewExternalAddress(ctx context.Context, account uint32, callOpts ...NextAddressCallOption) (stdaddr.Address, error) {
743743
const op errors.Op = "wallet.NewExternalAddress"
744744

745+
// Imported voting accounts must not be used for normal transactions.
746+
if err := w.notVotingAcct(ctx, op, account); err != nil {
747+
return nil, err
748+
}
749+
745750
accountName, _ := w.AccountName(ctx, account)
746751
return w.nextAddress(ctx, op, w.persistReturnedChild(ctx, nil),
747752
accountName, account, udb.ExternalBranch, callOpts...)
748753
}
749754

750755
// NewInternalAddress returns an internal address.
751756
func (w *Wallet) NewInternalAddress(ctx context.Context, account uint32, callOpts ...NextAddressCallOption) (stdaddr.Address, error) {
752-
const op errors.Op = "wallet.NewExternalAddress"
757+
const op errors.Op = "wallet.NewInternalAddress"
758+
759+
// Imported voting accounts must not be used for normal transactions.
760+
if err := w.notVotingAcct(ctx, op, account); err != nil {
761+
return nil, err
762+
}
753763

754764
accountName, _ := w.AccountName(ctx, account)
755765
return w.nextAddress(ctx, op, w.persistReturnedChild(ctx, nil),
756766
accountName, account, udb.InternalBranch, callOpts...)
757767
}
758768

759-
func (w *Wallet) newChangeAddress(ctx context.Context, op errors.Op, persist persistReturnedChildFunc,
760-
accountName string, account uint32, gap gapPolicy) (stdaddr.Address, error) {
769+
// notVotingAcct errors if an account is a special voting type. This account
770+
// should not be used to receive funds.
771+
func (w *Wallet) notVotingAcct(ctx context.Context, op errors.Op, account uint32) error {
761772
var accountType uint8
762773
if err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
763774
ns := dbtx.ReadBucket(waddrmgrNamespaceKey)
@@ -768,11 +779,20 @@ func (w *Wallet) newChangeAddress(ctx context.Context, op errors.Op, persist per
768779
accountType = props.AccountType
769780
return nil
770781
}); err != nil {
771-
return nil, errors.E(op, err)
782+
return errors.E(op, err)
772783
}
773-
// Imported voting accounts must not be used for change.
774784
if udb.IsImportedVoting(accountType) {
775-
return nil, errors.E(op, errors.Invalid, "cannot use voting accounts for change addresses")
785+
return errors.E(op, errors.Invalid, "cannot use voting accounts for normal transactions")
786+
}
787+
return nil
788+
}
789+
790+
func (w *Wallet) newChangeAddress(ctx context.Context, op errors.Op, persist persistReturnedChildFunc,
791+
accountName string, account uint32, gap gapPolicy) (stdaddr.Address, error) {
792+
793+
// Imported voting accounts must not be used for change.
794+
if err := w.notVotingAcct(ctx, op, account); err != nil {
795+
return nil, err
776796
}
777797

778798
// Addresses can not be generated for the imported account, so as a

0 commit comments

Comments
 (0)