Skip to content

Instantly share code, notes, and snippets.

@grondilu
Created April 25, 2022 06:28
Show Gist options
  • Save grondilu/b21620bb55c15208990db3a90d405247 to your computer and use it in GitHub Desktop.
Save grondilu/b21620bb55c15208990db3a90d405247 to your computer and use it in GitHub Desktop.
Bech32 in raku
grammar Bech32 {
constant @data-charset = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'.comb;
constant %data-charset = @data-charset.pairs.invert;
sub polymod(@values --> UInt) {
constant @gen = 0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3;
my uint32 $chk = 1;
for @values -> $v {
my $b = $chk +> 25;
$chk = ($chk +& 0x1ffffff) +< 5 +^ $v;
$chk +^= (($b +> $_) +& 1) ?? @gen[$_] !! 0 for ^5;
}
return $chk;
}
sub hrp-expand(Str $s --> Array) {
[ord($_) +> 5 for $s.comb]
.append(0)
.append:
[ord($_) +& 31 for $s.comb]
}
regex TOP {
^ <hrp> '1' <data> $
<?{ polymod(hrp-expand(~$<hrp>).append: $<data>.made) == 1|0x2bc830a3 }>
}
regex hrp { <ascii> ** 1..83 <?{ 33 ≤ $/.ords.all ≤ 126 }> }
regex data { (<@data-charset>) ** 6..* { make %data-charset{$/[0]} } }
token ascii { . <?{ try $/.encode("ascii"); not $! }> }
}
use Test;
subtest {
ok Bech32.parse(.lc), $_ for <
A12UEL5L
a12uel5l
an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs
abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw
11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j
split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w
?1ezyfcl
>;
nok Bech32.parse(.key.lc), "{.key} : {.value}" for
chr(0x20) ~ "1nwldj5" => "HRP character out of range",
chr(0x7F) ~ "1axkwrx" => "HRP character out of range",
chr(0x80) ~ "1eym55h" => "HRP character out of range",
"an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx" => "overall max length exceeded",
"pzry9x0s0muk" => "No separator character",
"1pzry9x0s0muk" => "Empty HRP",
"x1b4n0q5v" => "Invalid data character",
"li1dgmt3" => "Too short checksum",
"de1lg7wt" ~ chr(0xFF) => "Invalid character in checksum",
"A1G7SGD8" => "checksum calculated with uppercase form of HRP",
"10a06t8" => "empty HRP",
"1qzzfhee" => "empty HRP",
;
}, "bip-0173";
subtest {
ok Bech32.parse(.lc), $_ for <
A1LQFN3A
a1lqfn3a
an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6
abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx
11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8
split1checkupstagehandshakeupstreamerranterredcaperredlc445v
?1v759aa
>;
nok Bech32.parse(.key.lc), "{.key} : {.value}" for
chr(0x20) ~ "1xj0phk" => "HRP character out of range",
chr(0x7F) ~ "1g6xzxy" => "HRP character out of range",
chr(0x80) ~ "1vctc34" => "HRP character out of range",
"an84characterslonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11d6pts4" => "overall max length exceeded",
"qyrz8wqd2c9m" => "No separator character",
"1qyrz8wqd2c9m" => "Empty HRP",
"y1b0jsk6g" => "Invalid data character",
"lt1igcx5c0" => "Invalid data character",
"in1muywd" => "Too short checksum",
"mm1crxm3i" => "Invalid character in checksum",
"au1s5cgom" => "Invalid character in checksum",
"M1VUXWEZ" => "checksum calculated with uppercase form of HRP",
"16plkw9" => "empty HRP",
"1p2gdwpf" => "empty HRP";
}, "bip-0350";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment