4
4
"bytes"
5
5
"context"
6
6
"crypto"
7
+ "crypto/tls"
7
8
"encoding/base64"
8
9
"encoding/hex"
9
10
"encoding/json"
@@ -45,7 +46,6 @@ import (
45
46
// A wallet corresponds to a single Uphold "card"
46
47
type Wallet struct {
47
48
walletutils.Info
48
- Logger * zerolog.Logger
49
49
PrivKey crypto.Signer
50
50
PubKey httpsignature.Verifier
51
51
}
@@ -54,6 +54,7 @@ const (
54
54
dateFormat = "2006-01-02T15:04:05.000Z"
55
55
batchSize = 50
56
56
listTransactionsRetries = 5
57
+ httpTimeout = time .Second * 60
57
58
)
58
59
59
60
const (
90
91
"sandbox" : sandboxFingerprint ,
91
92
"prod" : prodFingerprint ,
92
93
}[environment ]
93
- client * http.Client
94
+
95
+ // The client to connect to Uphold servers while performing fingerprint
96
+ // checks on the server certificates.
97
+ defaultHTTPClient * http.Client
98
+
99
+ // The client without fingerprint checks.
100
+ httpClientNoFP * http.Client
94
101
)
95
102
96
103
func init () {
@@ -115,12 +122,33 @@ func init() {
115
122
} else {
116
123
proxy = nil
117
124
}
118
- client = & http.Client {
119
- Timeout : time .Second * 60 ,
125
+
126
+ fingerprintDialer := pindialer .MakeContextDialer (upholdCertFingerprint )
127
+
128
+ // Uphold reports HTTP 401 error when connecting with HTTP2, so disable
129
+ // HTTP/2 via setting TLSNextProto to an empty map. We do not need to set
130
+ // this field on defaultHTTPClient as we set DialTLSContext without setting
131
+ // ForceAttemptHTTP2 and that disables HTTP/2 also. But for clarity we
132
+ // always set TLSNextProto.
133
+ disableHTTP2 := make (
134
+ map [string ]func (authority string , c * tls.Conn ) http.RoundTripper ,
135
+ 0 ,
136
+ )
137
+ defaultHTTPClient = & http.Client {
138
+ Timeout : httpTimeout ,
120
139
Transport : middleware .InstrumentRoundTripper (
121
140
& http.Transport {
141
+ DialTLSContext : fingerprintDialer ,
122
142
Proxy : proxy ,
123
- DialTLSContext : pindialer .MakeContextDialer (upholdCertFingerprint ),
143
+ TLSNextProto : disableHTTP2 ,
144
+ }, "uphold" ),
145
+ }
146
+ httpClientNoFP = & http.Client {
147
+ Timeout : httpTimeout ,
148
+ Transport : middleware .InstrumentRoundTripper (
149
+ & http.Transport {
150
+ Proxy : proxy ,
151
+ TLSNextProto : disableHTTP2 ,
124
152
}, "uphold" ),
125
153
}
126
154
}
@@ -141,7 +169,11 @@ func New(ctx context.Context, info walletutils.Info, privKey crypto.Signer, pubK
141
169
if ! info .AltCurrency .IsValid () {
142
170
return nil , errors .New ("a wallet must have a valid altcurrency" )
143
171
}
144
- return & Wallet {Info : info , PrivKey : privKey , PubKey : pubKey }, nil
172
+ return & Wallet {
173
+ Info : info ,
174
+ PrivKey : privKey ,
175
+ PubKey : pubKey ,
176
+ }, nil
145
177
}
146
178
147
179
// FromWalletInfo returns an uphold wallet matching the provided wallet info
@@ -169,7 +201,11 @@ func newRequest(method, path string, body io.Reader) (*http.Request, error) {
169
201
return req , err
170
202
}
171
203
172
- func submit (logger * zerolog.Logger , req * http.Request ) ([]byte , * http.Response , error ) {
204
+ func submit (
205
+ logger * zerolog.Logger ,
206
+ client * http.Client ,
207
+ req * http.Request ,
208
+ ) ([]byte , * http.Response , error ) {
173
209
req .Header .Add ("content-type" , "application/json" )
174
210
175
211
dump , err := httputil .DumpRequestOut (req , true )
@@ -262,8 +298,18 @@ func (w *Wallet) IsUserKYC(ctx context.Context, destination string) (string, boo
262
298
return "" , false , "" , fmt .Errorf ("failed to prepare transaction: %w" , err )
263
299
}
264
300
265
- // submit the transaction the payload
266
- uhResp , err := grantWallet .SubmitTransaction (ctx , transactionB64 , false )
301
+ // Submit the transaction payload.
302
+ //
303
+ // As we use the wallet only for validation but not for a payment, skip
304
+ // fingerprint checks to avoid outages when Uphold change the certificate.
305
+ // For payments it is not a problem as they are done in batch and can be
306
+ // repeated.
307
+ uhResp , err := grantWallet .submitTransaction (
308
+ ctx ,
309
+ httpClientNoFP ,
310
+ transactionB64 ,
311
+ false , /*confirm*/
312
+ )
267
313
if err != nil {
268
314
logger .Error ().Err (err ).Msg ("failed to submit transaction" )
269
315
return "" , false , "" , fmt .Errorf ("failed to submit transaction: %w" , err )
@@ -361,7 +407,7 @@ func (w *Wallet) Register(ctx context.Context, label string) error {
361
407
return err
362
408
}
363
409
364
- body , _ , err := submit (logger , req )
410
+ body , _ , err := submit (logger , defaultHTTPClient , req )
365
411
if err != nil {
366
412
return err
367
413
}
@@ -400,7 +446,7 @@ func (w *Wallet) SubmitRegistration(ctx context.Context, registrationB64 string)
400
446
return err
401
447
}
402
448
403
- body , _ , err := submit (logger , req )
449
+ body , _ , err := submit (logger , defaultHTTPClient , req )
404
450
if err != nil {
405
451
return err
406
452
}
@@ -456,7 +502,7 @@ func (w *Wallet) GetCardDetails(ctx context.Context) (*CardDetails, error) {
456
502
if err != nil {
457
503
return nil , err
458
504
}
459
- body , _ , err := submit (logger , req )
505
+ body , _ , err := submit (logger , defaultHTTPClient , req )
460
506
if err != nil {
461
507
return nil , err
462
508
}
@@ -588,7 +634,7 @@ func (w *Wallet) Transfer(ctx context.Context, altcurrency altcurrency.AltCurren
588
634
return nil , fmt .Errorf ("failed to sign the transfer: %w" , err )
589
635
}
590
636
591
- respBody , _ , err := submit (logger , req )
637
+ respBody , _ , err := submit (logger , defaultHTTPClient , req )
592
638
if err != nil {
593
639
// we need this to be draincoded wrapped error so we get the reason for failure in drains
594
640
if codedErr , ok := err .(Coded ); ok {
@@ -843,10 +889,21 @@ func (resp upholdTransactionResponse) ToTransactionInfo() *walletutils.Transacti
843
889
return & txInfo
844
890
}
845
891
846
- // SubmitTransaction submits the base64 encoded transaction for verification but does not move funds
847
- //
848
- // unless confirm is set to true.
892
+ // SubmitTransaction submits the base64 encoded transaction for verification but
893
+ // does not move funds unless confirm is set to true.
849
894
func (w * Wallet ) SubmitTransaction (ctx context.Context , transactionB64 string , confirm bool ) (* walletutils.TransactionInfo , error ) {
895
+ return w .submitTransaction (ctx , defaultHTTPClient , transactionB64 , confirm )
896
+ }
897
+
898
+ func (w * Wallet ) submitTransaction (
899
+ ctx context.Context ,
900
+ client * http.Client ,
901
+ transactionB64 string ,
902
+ confirm bool ,
903
+ ) (* walletutils.TransactionInfo , error ) {
904
+ if confirm && client != defaultHTTPClient {
905
+ panic ("TLS fingerprint checks must be enabled when confirming" )
906
+ }
850
907
logger := logging .FromContext (ctx )
851
908
852
909
_ , err := w .VerifyTransaction (ctx , transactionB64 )
@@ -879,7 +936,7 @@ func (w *Wallet) SubmitTransaction(ctx context.Context, transactionB64 string, c
879
936
return nil , err
880
937
}
881
938
882
- respBody , _ , err := submit (logger , req )
939
+ respBody , _ , err := submit (logger , client , req )
883
940
if err != nil {
884
941
return nil , err
885
942
}
@@ -901,7 +958,7 @@ func (w *Wallet) ConfirmTransaction(ctx context.Context, id string) (*walletutil
901
958
if err != nil {
902
959
return nil , err
903
960
}
904
- body , _ , err := submit (logger , req )
961
+ body , _ , err := submit (logger , defaultHTTPClient , req )
905
962
if err != nil {
906
963
return nil , err
907
964
}
@@ -927,7 +984,7 @@ func (w *Wallet) GetTransaction(ctx context.Context, id string) (*walletutils.Tr
927
984
if err != nil {
928
985
return nil , err
929
986
}
930
- body , _ , err := submit (logger , req )
987
+ body , _ , err := submit (logger , defaultHTTPClient , req )
931
988
if err != nil {
932
989
return nil , err
933
990
}
@@ -970,7 +1027,7 @@ func (w *Wallet) ListTransactions(ctx context.Context, limit int, startDate time
970
1027
var body []byte
971
1028
var resp * http.Response
972
1029
for i := 0 ; i < listTransactionsRetries ; i ++ {
973
- body , resp , err = submit (logger , req )
1030
+ body , resp , err = submit (logger , defaultHTTPClient , req )
974
1031
if nerr , ok := err .(net.Error ); ok && nerr .Temporary () {
975
1032
logger .Debug ().
976
1033
Str ("path" , "github.com/brave-intl/bat-go/wallet/provider/uphold" ).
@@ -1070,7 +1127,7 @@ func (w *Wallet) CreateCardAddress(ctx context.Context, network string) (string,
1070
1127
return "" , err
1071
1128
}
1072
1129
1073
- body , _ , err := submit (logger , req )
1130
+ body , _ , err := submit (logger , defaultHTTPClient , req )
1074
1131
if err != nil {
1075
1132
return "" , err
1076
1133
}
0 commit comments