aboutsummaryrefslogtreecommitdiff
path: root/challenge-016
diff options
context:
space:
mode:
authorJoelle Maslak <jmaslak@antelope.net>2019-07-13 11:51:10 -0600
committerJoelle Maslak <jmaslak@antelope.net>2019-07-13 11:51:10 -0600
commitaf4ea3159c336090b70db92f3685832252103094 (patch)
treed86dafdc0c82bd15e35c3903293c01e1e2fda47d /challenge-016
parent628064c7bae60468e5157e50d9b81163eb4ddf87 (diff)
downloadperlweeklychallenge-club-af4ea3159c336090b70db92f3685832252103094.tar.gz
perlweeklychallenge-club-af4ea3159c336090b70db92f3685832252103094.tar.bz2
perlweeklychallenge-club-af4ea3159c336090b70db92f3685832252103094.zip
Solution for 16.2 in P5, improvements for P6 version
Diffstat (limited to 'challenge-016')
-rwxr-xr-xchallenge-016/joelle-maslak/perl5/ch-2.pl128
-rwxr-xr-xchallenge-016/joelle-maslak/perl6/ch-2.p622
2 files changed, 139 insertions, 11 deletions
diff --git a/challenge-016/joelle-maslak/perl5/ch-2.pl b/challenge-016/joelle-maslak/perl5/ch-2.pl
new file mode 100755
index 0000000000..f35499cead
--- /dev/null
+++ b/challenge-016/joelle-maslak/perl5/ch-2.pl
@@ -0,0 +1,128 @@
+#!/usr/bin/env perl
+use v5.22;
+use strict;
+use warnings;
+
+# Turn on method signatures
+use feature 'signatures';
+no warnings 'experimental::signatures';
+
+use autodie;
+use bigint;
+
+use Digest::SHA qw(sha256);
+
+die("Please provide bitcoint address as argument") unless scalar(@ARGV) eq 1;
+
+if ($ARGV[0] eq '--test') {
+ test();
+} else {
+ say validate($ARGV[0]) ? 'True' : 'False';
+}
+
+sub test() {
+ my @tests = (
+ ['1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2' => 1 ],
+ ['1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN211' => undef ], # Too long
+ ['1BvBMSEYstWetqTFn5Au4m4GFg0xJaNVN2' => undef ], # Invalid char
+ ['1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVM2' => undef ], # Bad checksum
+ ['3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy' => 1 ],
+ ['1111111111111111111114oLvT2' => 1 ],
+ ['111111111111111111114oLvT2' => undef ], # Too short
+ ['11111111111111111111114oLvT2' => undef ], # Too long
+ );
+
+ use Test2::V0;
+
+ for my $test (@tests) {
+ is validate($test->[0]), $test->[1], $test->[0] . ' ' . ($test->[1] ? 'True' : 'False');
+ }
+
+
+ done_testing;
+
+}
+
+sub validate($addr) {
+ # Is it valid base-58?
+ my $val = base58_decode($addr);
+ return if !defined($val);
+
+ # Format we expect:
+ # <version> <20 bytes> <4 byte checksum>
+ return if $val >= (2**200);
+
+ # Is the first byte a 1 or a 5? Those are the only types we support.
+ my $ver = $val / (2**(24*8)) & 0xff;
+ return if ($ver != 0 and $ver != 5);
+
+ # Is it in cannonical format - I.E. the standard format it would be
+ # encoded as?
+ return unless $addr eq base58_encode($val, 25);
+
+ # Does the checksum match?
+ my (@buf) = int_to_buf($val, 25)->@*;
+ my $sha = sha256(sha256(join '', @buf[0..20]));
+ return unless substr($sha, 0, 4) eq join('',@buf[21..24]);
+
+ return 1;
+}
+
+sub base58_decode($txt) {
+ state $chars;
+ if (!defined($chars)) {
+ $chars = {};
+
+ my (@txt) = split //, '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
+ for (my $i=0; $i<58; $i++) {
+ $chars->{$txt[$i]} = $i;
+ }
+ }
+
+ my $val = 0;
+ for my $c (split //, $txt) {
+ return unless exists $chars->{$c};
+ $val = $val * 58 + $chars->{$c};
+ }
+
+ return $val;
+}
+
+sub base58_encode($int, $bytes) {
+ state $vals;
+ if (!defined($vals)) {
+ $vals = {};
+
+ my (@txt) = split //, '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
+ for (my $i=0; $i<58; $i++) {
+ $vals->{$i} = $txt[$i];
+ }
+ }
+
+ # Handle leading zero bytes
+ my $zeros = '';
+ my $buf = int_to_buf($int, $bytes);
+ for my $byte (@$buf) {
+ if (ord($byte) == 0) { $zeros .= '1'; } # Leading zero comparison
+ if (ord($byte) != 0) { last; }
+ }
+
+ my $encoded = '';
+ while ($int > 0) {
+ $encoded = $vals->{$int % 58} . $encoded;
+ $int = $int / 58;
+ }
+
+ return "$zeros$encoded";
+}
+
+sub int_to_buf($int, $bytes) {
+ my $buf = [];
+
+ for (my $i=0; $i<$bytes; $i++) {
+ my $byte = ( $int / (2**(8 * (($bytes-1) - $i)) )) % 256;
+ push @$buf, chr($byte);
+ }
+
+ return $buf;
+}
diff --git a/challenge-016/joelle-maslak/perl6/ch-2.p6 b/challenge-016/joelle-maslak/perl6/ch-2.p6
index f9174c8620..65cff90009 100755
--- a/challenge-016/joelle-maslak/perl6/ch-2.p6
+++ b/challenge-016/joelle-maslak/perl6/ch-2.p6
@@ -10,14 +10,14 @@ multi sub MAIN(Bool :$test where * == True) {
use Test;
my @tests =
- '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2' => True,
- '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN21' => False, # Too long
- '1BvBMSEYstWetqTFn5Au4m4GFg0xJaNVN2' => False, # Invalid char
- '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVM2' => False, # Bad checksum
- '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy' => True,
- '1111111111111111111114oLvT2' => True,
- '111111111111111111114oLvT2' => False, # Too short
- '11111111111111111111114oLvT2' => False, # Too long
+ '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2' => True,
+ '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN211' => False, # Too long
+ '1BvBMSEYstWetqTFn5Au4m4GFg0xJaNVN2' => False, # Invalid char
+ '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVM2' => False, # Bad checksum
+ '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy' => True,
+ '1111111111111111111114oLvT2' => True,
+ '111111111111111111114oLvT2' => False, # Too short
+ '11111111111111111111114oLvT2' => False, # Too long
;
for @tests -> $test {
@@ -37,10 +37,10 @@ sub validate(Str:D $addr --> Bool) {
return False unless $val.defined;
# Format we expect:
- # 0x00 <20 bytes - hash> <4 byte checksum> # Is it too big (>25 bytes decoded)?
- return False if $val > 2²⁰⁰; # Too big
+ # <version> <20 bytes> <4 byte checksum> # Is it too big (>25 bytes decoded)?
+ return False if $val ≥ 2²⁰⁰; # Too big
- # Is the first digit a 1 or 5? That's the only type we support.
+ # Is the first byte a 1 or 5? Those are the only typew we support.
my $ver = $val +& ( 0xff +< ( 24 * 8 ) ) +> ( 24 * 8 );
return False if $ver ≠ 0|5; # Unknown version