CW-254-proper-bitcoin-address-validation-in-exchange-screen (#900)

* feat: Proper Bitcoin address validation in exchange screen * fix: use custom validation in addition to RegEx to make sure all address cases are validated - for cases like P2SH addresses starting with a 3, which are not validated by bitcoin_flutter functions * feat: add bitcoin_flutter to root project * refactor: improve conditional isValid return chain

Rafael Saes committed May 15, 2023 at 09:39 UTC 0231298381ac6ca739ba773ffd41407e682c8761
3 files changed +46 -23
lib/core/address_validator.dart
+8 -6
@@ -1,4 +1,4 @@
1 -import 'package:flutter/foundation.dart';
1 +import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
2 import 'package:cake_wallet/generated/i18n.dart';
3 import 'package:cake_wallet/core/validator.dart';
4 import 'package:cw_core/crypto_currency.dart';
@@ -7,6 +7,9 @@ class AddressValidator extends TextValidator {
7 AddressValidator({required CryptoCurrency type})
8 : super(
9 errorMessage: S.current.error_text_address,
10 + useAdditionalValidation: type == CryptoCurrency.btc
11 + ? bitcoin.Address.validateAddress
12 + : null,
13 pattern: getPattern(type),
14 length: getLength(type));
15
@@ -18,8 +21,7 @@ class AddressValidator extends TextValidator {
21 return '^[0-9a-zA-Z]{59}\$|^[0-9a-zA-Z]{92}\$|^[0-9a-zA-Z]{104}\$'
22 '|^[0-9a-zA-Z]{105}\$|^addr1[0-9a-zA-Z]{98}\$';
23 case CryptoCurrency.btc:
21 - return '^1[0-9a-zA-Z]{32}\$|^1[0-9a-zA-Z]{33}\$|^3[0-9a-zA-Z]{32}\$'
22 - '|^3[0-9a-zA-Z]{33}\$|^bc1[0-9a-zA-Z]{39}\$|^bc1[0-9a-zA-Z]{59}\$';
24 + return '^3[0-9a-zA-Z]{32}\$|^3[0-9a-zA-Z]{33}\$|^bc1[0-9a-zA-Z]{59}\$';
25 case CryptoCurrency.nano:
26 return '[0-9a-zA-Z_]';
27 case CryptoCurrency.usdc:
@@ -63,7 +65,7 @@ class AddressValidator extends TextValidator {
65 case CryptoCurrency.bch:
66 case CryptoCurrency.bnb:
67 return '[0-9a-zA-Z]';
66 - case CryptoCurrency.hbar:
68 + case CryptoCurrency.hbar:
69 return '[0-9a-zA-Z.]';
70 case CryptoCurrency.zaddr:
71 return '^zs[0-9a-zA-Z]{75}';
@@ -165,9 +167,9 @@ class AddressValidator extends TextValidator {
167 return [34];
168 case CryptoCurrency.hbar:
169 return [4, 5, 6, 7, 8, 9, 10, 11];
168 - case CryptoCurrency.xvg:
170 + case CryptoCurrency.xvg:
171 return [34];
170 - case CryptoCurrency.zen:
172 + case CryptoCurrency.zen:
173 return [35];
174 case CryptoCurrency.zaddr:
175 return null;
lib/core/validator.dart
+34 -17
@@ -1,24 +1,26 @@
1 -import 'package:flutter/foundation.dart';
2 -
1 abstract class Validator<T> {
4 - Validator({required this.errorMessage});
2 + Validator({required this.errorMessage, this.useAdditionalValidation});
3
4 final String errorMessage;
5 + final bool Function(T)? useAdditionalValidation;
6
7 bool isValid(T? value);
8
10 - String? call(T? value) => !isValid(value) ? errorMessage : null;
9 + String? call(T? value) => !isValid(value) ? errorMessage : null;
10 }
11
12 class TextValidator extends Validator<String> {
14 - TextValidator(
15 - {this.minLength,
16 - this.maxLength,
17 - this.pattern,
18 - String errorMessage = '',
19 - this.length,
20 - this.isAutovalidate = false})
21 - : super(errorMessage: errorMessage);
13 + TextValidator({
14 + bool Function(String)? useAdditionalValidation,
15 + this.minLength,
16 + this.maxLength,
17 + this.pattern,
18 + String errorMessage = '',
19 + this.length,
20 + this.isAutovalidate = false,
21 + }) : super(
22 + errorMessage: errorMessage,
23 + useAdditionalValidation: useAdditionalValidation);
24
25 final int? minLength;
26 final int? maxLength;
@@ -32,11 +34,26 @@ class TextValidator extends Validator<String> {
34 return isAutovalidate ? true : false;
35 }
36
35 - return value.length > (minLength ?? 0) &&
36 - (length?.contains(value.length) ?? true) &&
37 - ((maxLength ?? 0) > 0 ? (value.length <= maxLength!) : true) &&
38 - (pattern != null ? match(value) : true);
37 + final greaterThanMinLength = value.length > (minLength ?? 0);
38 + if (!greaterThanMinLength) return false;
39 +
40 + final lengthMatched = length?.contains(value.length) ?? true;
41 + if (!lengthMatched) return false;
42 +
43 + final lowerThanMaxLength =
44 + (maxLength ?? 0) > 0 ? (value.length <= maxLength!) : true;
45 + if (!lowerThanMaxLength) return false;
46 +
47 + if (pattern == null) return true;
48 +
49 + final valueMatched = match(value);
50 + final valueValidated = useAdditionalValidation != null
51 + ? useAdditionalValidation!(value) || valueMatched
52 + : valueMatched;
53 +
54 + return valueValidated;
55 }
56
41 - bool match(String value) => pattern != null ? RegExp(pattern!).hasMatch(value) : false;
57 + bool match(String value) =>
58 + pattern != null ? RegExp(pattern!).hasMatch(value) : false;
59 }
pubspec_base.yaml
+4
@@ -81,6 +81,10 @@ dependencies:
81 path_provider_android: 2.0.24
82 shared_preferences_android: 2.0.17
83 url_launcher_android: 6.0.24
84 + bitcoin_flutter:
85 + git:
86 + url: https://github.com/cake-tech/bitcoin_flutter.git
87 + ref: cake-update-v2
88
89 dev_dependencies:
90 flutter_test: