uses
Generics.Collections;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
const
Charset = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';
Generator: array[0..4] of UInt32 = ($3B6A57B2, $26508E6D, $1EA119FA, $3D4233DD, $2A1462B3);
var
Form1: TForm1;
implementation
{$R *.dfm}
function Bech32Polymod(const values: array of UInt32): UInt32;
var
chk, b, i, j: UInt32;
begin
chk := 1;
for i := 0 to Length(values) - 1 do
begin
b := chk shr 25;
chk := ((chk and $1FFFFFF) shl 5) xor values[i];
for j := 0 to 4 do
begin
if (b shr j) and 1 = 1 then
chk := chk xor Generator[j];
end;
end;
Result := chk;
end;
function HrpExpand(const hrp: string): TList<UInt32>;
var
i: Integer;
begin
Result := TList<UInt32>.Create;
for i := 1 to Length(hrp) do
Result.Add(Ord(hrp[i]) shr 5);
Result.Add(0);
for i := 1 to Length(hrp) do
Result.Add(Ord(hrp[i]) and 31);
end;
function VerifyChecksum(const hrp: string; const data: TList<UInt32>): Boolean;
var
lhrpExpand: TList<UInt32>;
begin
lhrpExpand := hrpExpand(hrp);
lhrpExpand.AddRange(data.ToArray);
Result := Bech32Polymod(lhrpExpand.ToArray) = 1;
end;
function Decode(const str: string): TPair<string, TList<UInt32>>;
var
lastOneIndex: Integer;
hrp, data: string;
dataValues: TList<UInt32>;
i, v: Integer;
begin
lastOneIndex := Pos('1', str);
hrp := Copy(str, 1, lastOneIndex - 1);
data := Copy(str, lastOneIndex + 1, Length(str));
dataValues := TList<UInt32>.Create;
for i := 1 to Length(data) do
begin
v := Pos(data[i], Charset);
if v = 0 then
raise Exception.Create('invalid bech32 address')
else
dataValues.Add(v - 1);
end;
if VerifyChecksum(hrp, dataValues) then
Result := TPair<string, TList<UInt32>>.Create(hrp, dataValues)
else
raise Exception.Create('invalid bech32 address');
ShowMessage('Valid.');
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Decode('bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4');
Decode('abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw');
end;