Skip to content

Null safe migration #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion example/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ void main() async {
"original_amount": utxo.satoshis
});

totalBalance += utxo.satoshis;
totalBalance += utxo.satoshis!;
});

// set an address to send the remaining balance to
Expand Down
20 changes: 13 additions & 7 deletions lib/src/account.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,31 @@ import 'hdnode.dart';
class Account {
final HDNode accountNode;

int currentChild = 0;
int? currentChild = 0;

Account(this.accountNode, [this.currentChild]);

/// Returns address at the current position
String getCurrentAddress([legacyFormat = true]) {
String? getCurrentAddress([legacyFormat = true]) {
if (legacyFormat) {
return accountNode.derive(currentChild).toLegacyAddress();
return accountNode.derive(currentChild!).toLegacyAddress();
} else {
return accountNode.derive(currentChild).toCashAddress();
return accountNode.derive(currentChild!).toCashAddress();
}
}

/// moves the position forward and returns an address from the new position
String getNextAddress([legacyFormat = true]) {
String? getNextAddress([legacyFormat = true]) {
if (legacyFormat) {
return accountNode.derive(++currentChild).toLegacyAddress();
if (currentChild != null) {
currentChild = currentChild! + 1;
return accountNode.derive(currentChild!).toLegacyAddress();
}
} else {
return accountNode.derive(++currentChild).toCashAddress();
if (currentChild != null) {
currentChild = currentChild! + 1;
return accountNode.derive(currentChild!).toCashAddress();
}
}
}
}
70 changes: 51 additions & 19 deletions lib/src/address.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'utils/rest_api.dart';
Expand Down Expand Up @@ -54,8 +55,9 @@ class Address {
/// Returns information about the given Bitcoin Cash address.
///
/// See https://developer.bitcoin.com/bitbox/docs/util for details about returned format
static Future<Map<String, dynamic>> validateAddress(String address) async =>
await RestApi.sendGetRequest("util/validateAddress", address);
static Future<Map<String, dynamic>?> validateAddress(String address) async =>
await (RestApi.sendGetRequest("util/validateAddress", address)
as FutureOr<Map<String, dynamic>?>);

/// Returns details of the provided address or addresses
///
Expand Down Expand Up @@ -90,7 +92,7 @@ class Address {
return Utxo.convertMapListToUtxos(result["utxos"]);
} else if (result is List<Map>) {
final returnList = <Map>[];
final returnMap = <String, List>{};
final returnMap = <String?, List>{};

result.forEach((addressUtxoMap) {
if (returnAsMap) {
Expand Down Expand Up @@ -123,7 +125,7 @@ class Address {
return Utxo.convertMapListToUtxos(result["utxos"]);
} else if (result is List) {
final returnList = <Map>[];
final returnMap = <String, List>{};
final returnMap = <String?, List>{};

result.forEach((addressUtxoMap) {
if (returnAsMap) {
Expand All @@ -147,7 +149,7 @@ class Address {
final decoded = _decode(address);
final testnet =
decoded['prefix'] == "bchtest" || decoded['prefix'] == "slptest";
var version;
late var version;
if (testnet) {
switch (decoded['type']) {
case "P2PKH":
Expand All @@ -171,7 +173,7 @@ class Address {
}

/// Converts legacy address to cash address
static String toCashAddress(String address, [bool includePrefix = true]) {
static String? toCashAddress(String address, [bool includePrefix = true]) {
final decoded = _decode(address);
switch (decoded["prefix"]) {
case 'bitcoincash':
Expand All @@ -194,8 +196,34 @@ class Address {
}
}

/// Converts legacy address to cash address
static String? toTokenAddress(String address, [bool includePrefix = true]) {
final decoded = _decode(address);
switch (decoded["prefix"]) {
case 'bitcoincash':
case 'simpleledger':
decoded['prefix'] = "bitcoincash";
break;
case 'bchtest':
case 'slptest':
decoded['prefix'] = "bchtest";
break;
default:
throw FormatException("Unsupported address format: $address");
}

final tokenAddress =
_encode(decoded['prefix'], "P2PKHWITHTOKENS", decoded["hash"]);

if (!includePrefix) {
return tokenAddress.split(":")[1];
} else {
return tokenAddress;
}
}

/// Converts legacy or cash address to SLP address
static String toSLPAddress(String address, [bool includePrefix = true]) {
static String? toSLPAddress(String address, [bool includePrefix = true]) {
final decoded = Address._decode(address);
switch (decoded["prefix"]) {
case 'bitcoincash':
Expand Down Expand Up @@ -231,7 +259,7 @@ class Address {
}

/// Detects type of the address and returns [legacy], [cashaddr] or [slpaddr]
static String detectAddressFormat(String address) {
static String? detectAddressFormat(String address) {
// decode the address to determine the format
final decoded = _decode(address);
// return the format
Expand Down Expand Up @@ -261,7 +289,7 @@ class Address {
/// [prefix] - Network prefix. E.g.: 'bitcoincash'.
/// [type] Type of address to generate. Either 'P2PKH' or 'P2SH'.
/// [hash] is the address hash, which can be decode either using [_decodeCashAddress()] or [_decodeLegacyAddress()]
static _encode(String prefix, String type, Uint8List hash) {
static _encode(String prefix, String? type, Uint8List hash) {
final prefixData = _prefixToUint5List(prefix) + Uint8List(1);
final versionByte = _getTypeBits(type) + _getHashSizeBits(hash);
final payloadData =
Expand All @@ -278,7 +306,7 @@ class Address {
assert(addresses is String || addresses is List<String>);

if (addresses is String) {
return await RestApi.sendGetRequest("address/$path", addresses) as Map;
return await RestApi.sendGetRequest("address/$path", addresses) as Map?;
} else if (addresses is List<String>) {
return await RestApi.sendPostRequest(
"address/$path", "addresses", addresses,
Expand Down Expand Up @@ -351,6 +379,8 @@ class Address {
return 'P2PKH';
case 8:
return 'P2SH';
case 16:
return 'P2PKHWITHTOKENS';
default:
throw FormatException(
'Invalid address type in version byte: ' + versionByte + '.');
Expand All @@ -363,6 +393,8 @@ class Address {
return 0;
case 'P2SH':
return 8;
case 'P2PKHWITHTOKENS':
return 16;
default:
throw new FormatException('Invalid type: ' + type + '.');
}
Expand Down Expand Up @@ -453,7 +485,7 @@ class Address {
throw FormatException("Invalid Address Format: $address");
}

String exception;
late String exception;
// try to decode the address with either one or all three possible prefixes
for (int i = 0; i < prefixes.length; i++) {
final payload = _base32Decode(address);
Expand Down Expand Up @@ -514,7 +546,7 @@ class Address {
throw FormatException("Invalid Address Format: $address");
}

String exception;
late String exception;

// try to decode the address with either one or all three possible prefixes
for (int i = 0; i < prefixes.length; i++) {
Expand Down Expand Up @@ -642,7 +674,7 @@ class Address {
final value = string[i];
if (!_CHARSET_INVERSE_INDEX.containsKey(value))
throw FormatException("Invalid character '$value'");
data[i] = _CHARSET_INVERSE_INDEX[string[i]];
data[i] = _CHARSET_INVERSE_INDEX[string[i]]!;
}

return data;
Expand Down Expand Up @@ -672,12 +704,12 @@ class Address {

/// Container for to make it easier to work with Utxos
class Utxo {
final String txid;
final int vout;
final double amount;
final int satoshis;
final int height;
final int confirmations;
final String? txid;
final int? vout;
final double? amount;
final int? satoshis;
final int? height;
final int? confirmations;

Utxo(this.txid, this.vout, this.amount, this.satoshis, this.height,
this.confirmations);
Expand Down
2 changes: 1 addition & 1 deletion lib/src/bitcoincash.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class BitcoinCash {
return signatureBuffer;
}

static Uint8List getOpReturnScript(String data) {
static Uint8List? getOpReturnScript(String data) {
return compile([Opcodes.OP_RETURN, utf8.encode(data)]);
}
}
10 changes: 5 additions & 5 deletions lib/src/cashaccounts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'utils/rest_api.dart';
import 'package:http/http.dart' as http;

class CashAccounts {
static Future<Map> lookup(String account, int number, {int collision}) async {
static Future<Map?> lookup(String account, int number, {int? collision}) async {
String col = "";
if (collision != null) {
col = collision.toString();
Expand All @@ -13,19 +13,19 @@ class CashAccounts {
return json.decode(response.body);
}

static Future<Map> check(String account, int number) async {
static Future<Map?> check(String account, int number) async {
final response = await http.get(Uri.parse(
"https://rest.bitcoin.com/v2/cashAccounts/check/$account/$number"));
return json.decode(response.body);
}

static Future<Map> reverseLookup(String cashAddress) async {
static Future<Map?> reverseLookup(String cashAddress) async {
final response = await http.get(Uri.parse(
"https://rest.bitcoin.com/v2/cashAccounts/reverseLookup/$cashAddress"));
return json.decode(response.body);
}

static Future<Map> register(String name, String address) async {
static Future<Map?> register(String name, String address) async {
Map register = {
'name': name,
'payments': [address]
Expand All @@ -34,7 +34,7 @@ class CashAccounts {
Uri.parse('https://api.cashaccount.info/register'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(register));
Map data = jsonDecode(response.body);
Map? data = jsonDecode(response.body);
return data;
}
}
26 changes: 13 additions & 13 deletions lib/src/crypto/ecies.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ class Ecies {
/// [recipientPublicKey] - Public Key of the party who can decrypt the message
///
static String encryptData(
{String message,
String senderPrivateKeyHex,
String recipientPublicKeyHex,
{required String message,
required String senderPrivateKeyHex,
required String recipientPublicKeyHex,
String magicValue = "BIE1"}) {
//Encryption requires derivation of a cipher using the other party's Public Key
// Bob is sender, Alice is recipient of encrypted message
Expand All @@ -51,10 +51,10 @@ class Ecies {

List<int> messageBuffer = Uint8List.fromList(message.codeUnits);

final ECPoint S = (recipientPublicKey.point *
senderPrivateKey.privateKey); //point multiplication
final ECPoint S = (recipientPublicKey.point! *
senderPrivateKey.privateKey)!; //point multiplication

final pubkeyS = BCHPublicKey.fromXY(S.x.toBigInteger(), S.y.toBigInteger());
final pubkeyS = BCHPublicKey.fromXY(S.x!.toBigInteger()!, S.y!.toBigInteger()!);
final pubkeyBuffer = HEX.decode(pubkeyS.getEncoded(true));
final pubkeyHash = SHA512Digest().process(pubkeyBuffer as Uint8List);

Expand All @@ -73,7 +73,7 @@ class Ecies {
final magic = utf8.encode(magicValue);

final encodedBuffer = Uint8List.fromList(
magic + HEX.decode(senderPrivateKey.publicKey.toHex()) + cipherText);
magic + HEX.decode(senderPrivateKey.publicKey!.toHex()) + cipherText);

//calc checksum
final hmac = _calculateHmac(kM, encodedBuffer);
Expand Down Expand Up @@ -101,8 +101,8 @@ class Ecies {
/// [recipientPrivateKey] - Private Key of the receiving party
///
static String decryptData(
{String cipherTextStr,
String recipientPrivateKeyHex,
{required String cipherTextStr,
required String recipientPrivateKeyHex,
String magicValue = "BIE1"}) {
//AES Cipher is calculated as
//1) S = recipientPrivateKey o senderPublicKey
Expand All @@ -128,8 +128,8 @@ class Ecies {
BCHPublicKey.fromHex(HEX.encode(senderPubkeyBuffer));

//calculate S = recipientPrivateKey o senderPublicKey
final S = (senderPublicKey.point *
recipientPrivateKey.privateKey); //point multiplication
final S = (senderPublicKey.point! *
recipientPrivateKey.privateKey)!; //point multiplication
final cipher = S.x;

if (cipherText.length - _tagLength <= 37) {
Expand All @@ -138,7 +138,7 @@ class Ecies {
}

//validate the checksum bytes
final pubkeyS = BCHPublicKey.fromXY(S.x.toBigInteger(), S.y.toBigInteger());
final pubkeyS = BCHPublicKey.fromXY(S.x!.toBigInteger()!, S.y!.toBigInteger()!);
final pubkeyBuffer = HEX.decode(pubkeyS.getEncoded(true));
final pubkeyHash = SHA512Digest().process(pubkeyBuffer as Uint8List);

Expand All @@ -153,7 +153,7 @@ class Ecies {
final Uint8List hmac = _calculateHmac(kM, message);

final Uint8List messageChecksum =
cipherText.sublist(cipherText.length - _tagLength, cipherText.length);
cipherText.sublist(cipherText.length - _tagLength, cipherText.length) as Uint8List;

// ignore: prefer_const_constructors
if (!ListEquality().equals(messageChecksum, hmac)) {
Expand Down
Loading