Skip to content

Commit

Permalink
Prepare v2.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
konstantinullrich committed Dec 9, 2023
1 parent f0e3341 commit 2b81a61
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 115 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@ Please file feature requests and bugs at the [issue tracker][tracker].

[cypherspace]: http://www.cypherspace.org/adam/
[papers]: http://hashcash.org/papers/
[bitcoin]: http://bitcoin.it/
[bitcoin]: https://bitcoin.it/
[tracker]: https://github.com/konstantinullrich/hashcash/issues
2 changes: 1 addition & 1 deletion example/hashcash_dart_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'package:hashcash_dart/hashcash_dart.dart';
void main() {
print('Hashcash Protocol version: ${Hashcash.version}');

var stamp = Hashcash.mint('dev@konstantinullrich.de');
final stamp = Hashcash.mint('dev@konstantinullrich.de');

print(Hashcash.check(stamp, resource: 'dev@konstantinullrich.de'));
}
171 changes: 70 additions & 101 deletions lib/src/hashcash.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'package:crypto/crypto.dart' as crypto;

class Hashcash {
static final int _version = 1;
static final String _ascii_chars =
static final String _asciiChars =
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/=';

/// Version of the implemented hashcash protocol
Expand Down Expand Up @@ -34,17 +34,17 @@ class Hashcash {
DateTime? now,
String extension = '',
int saltChars = 8,
bool stamp_seconds = false}) {
var iso_now =
now == null ? DateTime.now().toIso8601String() : now.toIso8601String();
iso_now = iso_now.replaceAll('-', '').replaceAll(':', '');
var date_time = iso_now.split('T');
var ts = date_time[0].substring(2, date_time[0].length);
if (stamp_seconds) {
ts = '$ts${date_time[1].substring(0, 6)}';
bool stampSeconds = false}) {
var isoNow = now?.toIso8601String() ?? DateTime.now().toIso8601String();
isoNow = isoNow.replaceAll('-', '').replaceAll(':', '');
final dateTime = isoNow.split('T');
var ts = dateTime[0].substring(2, dateTime[0].length);

if (stampSeconds) {
ts = '$ts${dateTime[1].substring(0, 6)}';
}

var challenge = <String>[
final challenge = <String>[
version.toString(),
bits.toString(),
ts,
Expand All @@ -53,17 +53,17 @@ class Hashcash {
_salt(saltChars)
];

var mint = _mint(challenge.join(':'), bits);
final mint = _mint(challenge.join(':'), bits);
challenge.add(mint);
return challenge.join(':');
}

/// Return a random string of specified length
static String _salt(int length) {
var random = Random();
final random = Random();
var result = '';
for (var i = 0; i < length; i++) {
result += _ascii_chars[random.nextInt(_ascii_chars.length)];
result += _asciiChars[random.nextInt(_asciiChars.length)];
}
return result;
}
Expand All @@ -76,130 +76,99 @@ class Hashcash {
///
/// NOTE: Number of requested bits is rounded up to the nearest multiple of 4
static String _mint(String challenge, int bits) {
final hexDigits = (bits / 4).ceil();
final zeros = '0' * hexDigits;
var counter = 0;
var hex_digits = (bits / 4).ceil();
var zeros = '0' * hex_digits;

while (true) {
var digest = crypto.sha1
.convert((challenge + ':' + counter.toRadixString(16)).codeUnits)
final digest = crypto.sha1
.convert(('$challenge:${counter.toRadixString(16)}').codeUnits)
.toString();
if (digest.startsWith(zeros)) {
return counter.toRadixString(16);
}
if (digest.startsWith(zeros)) return counter.toRadixString(16);
counter++;
}
}

static bool check(String stamp,
{String? resource, int bits = 20, Duration? check_expiration}) {
if (stamp.isEmpty) {
return false;
}
{String? resource, int bits = 20, Duration? checkExpiration}) {
if (stamp.isEmpty) return false;

if (stamp.startsWith('0:')) {
var stamp_parts = stamp.substring(2).split(':');
if (stamp_parts.length != 3) {
return false;
}
final stampParts = stamp.substring(2).split(':');

var date = stamp_parts[0];
var res = stamp_parts[1];
if (stampParts.length != 3) return false;

var dt = _currentDate(date);
if (dt == null) {
return false;
}
final date = stampParts[0];
final res = stampParts[1];

if (resource != null && resource != res) {
return false;
}
if (check_expiration != null) {
var good_until = dt.add(check_expiration);
var now = DateTime.now();
if (now.isAfter(good_until)) {
return false;
}
final dt = _currentDate(date);
if (dt == null) return false;

if (resource != null && resource != res) return false;

if (checkExpiration != null) {
final goodUntil = dt.add(checkExpiration);
final now = DateTime.now();

if (now.isAfter(goodUntil)) return false;
}
var hex_digits = (bits / 4).floor();

final hexDigits = (bits / 4).floor();
return crypto.sha1
.convert(stamp.codeUnits)
.toString()
.startsWith('0' * hex_digits);
.startsWith('0' * hexDigits);
} else if (stamp.startsWith('1:')) {
var stamp_parts = stamp.substring(2).split(':');
if (stamp_parts.length != 6) {
return false;
}
final stampParts = stamp.substring(2).split(':');
if (stampParts.length != 6) return false;

var claim = int.parse(stamp_parts[0]);
var date = stamp_parts[1];
var res = stamp_parts[2];
final claim = int.parse(stampParts[0]);
final date = stampParts[1];
final res = stampParts[2];
final dt = _currentDate(date);

var dt = _currentDate(date);
if (dt == null) {
return false;
}
if (dt == null) return false;
if (resource != null && resource != res) return false;
if (bits != claim) return false;

if (resource != null && resource != res) {
return false;
}
if (bits != claim) {
return false;
}
if (check_expiration != null) {
var good_until = dt.add(check_expiration);
var now = DateTime.now();
if (now.isAfter(good_until)) {
return false;
}
if (checkExpiration != null) {
final goodUntil = dt.add(checkExpiration);
final now = DateTime.now();

if (now.isAfter(goodUntil)) return false;
}
var hex_digits = (claim / 4).floor();

final hexDigits = (claim / 4).floor();
return crypto.sha1
.convert(stamp.codeUnits)
.toString()
.startsWith('0' * hex_digits);
.startsWith('0' * hexDigits);
} else {
if (resource != null && !stamp.contains(resource)) {
return false;
}
var hex_digits = (bits / 4).floor();
if (resource != null && !stamp.contains(resource)) return false;

final hexDigits = (bits / 4).floor();
return crypto.sha1
.convert(stamp.codeUnits)
.toString()
.startsWith('0' * hex_digits);
.startsWith('0' * hexDigits);
}
}

static DateTime? _currentDate(String date) {
var day;
var month;
var year;
var hour;
var minute;
var dt;

try {
day = int.parse(date.substring(4, 6));
month = int.parse(date.substring(2, 4));
year = int.parse(date.substring(0, 2));
} catch (e) {
return null;
}
final day = int.tryParse(date.substring(4, 6));
final month = int.tryParse(date.substring(2, 4));
final year = int.tryParse(date.substring(0, 2));

if (day == null || month == null || year == null) return null;

if (date.length >= 10) {
try {
hour = int.parse(date.substring(6, 8));
minute = int.parse(date.substring(8, 10));
} catch (e) {
return null;
}
}
final hour = int.tryParse(date.substring(6, 8));
final minute = int.tryParse(date.substring(8, 10));

if (hour != null && minute != null) {
dt = DateTime(year + 2000, month, day, hour, minute);
} else {
dt = DateTime(year + 2000, month, day);
if (hour == null || minute == null) return null;
return DateTime(year + 2000, month, day, hour, minute);
}

return dt;
return DateTime(year + 2000, month, day);
}
}
10 changes: 5 additions & 5 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
name: hashcash_dart
description: A hashcash proof of work implementation for Dart and Flutter
version: 1.0.1
version: 2.0.0
homepage: https://github.com/konstantinullrich
repository: https://github.com/konstantinullrich/hashcash
issue_tracker: https://github.com/konstantinullrich/hashcash/issues

environment:
sdk: '>=2.12.0-0 <3.0.0'
sdk: ^3.0.6

dependencies:
crypto: ^3.0.2
crypto: ^3.0.3

dev_dependencies:
lints: ^1.0.1
test: ^1.21.1
lints: ^3.0.0
test: ^1.24.9
12 changes: 5 additions & 7 deletions test/hashcash_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,14 @@ import 'package:test/test.dart';

void main() {
group('Hashcash Testgroup', () {
var stamp =
Hashcash.mint('dev@konstantinullrich.de', stamp_seconds: true);
final stamp = Hashcash.mint('dev@konstantinullrich.de', stampSeconds: true);

test('Wrong Resource', () {
expect(Hashcash.check(stamp, resource: 'test'), false);
});

test('Valid Resource', () {
expect(Hashcash.check(stamp, resource: 'dev@konstantinullrich.de'),
true);
expect(Hashcash.check(stamp, resource: 'dev@konstantinullrich.de'), true);
});

test('Correct bits', () {
Expand All @@ -26,19 +24,19 @@ void main() {
});

test('Valid Expiration', () {
expect(Hashcash.check(stamp, check_expiration: Duration(hours: 1)), true);
expect(Hashcash.check(stamp, checkExpiration: Duration(hours: 1)), true);
});

sleep(Duration(seconds: 10));

test('Bad Expiration', () {
expect(
Hashcash.check(stamp, check_expiration: Duration(seconds: 1)), false);
Hashcash.check(stamp, checkExpiration: Duration(seconds: 1)), false);
});

test('More Valid Expiration', () {
expect(
Hashcash.check(stamp, check_expiration: Duration(minutes: 1)), true);
Hashcash.check(stamp, checkExpiration: Duration(minutes: 1)), true);
});
});
}

0 comments on commit 2b81a61

Please sign in to comment.