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 Polymod(
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 ExpandHrp(
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
HrpExpand: TList<UInt32>;
begin
HrpExpand := ExpandHrp(Hrp);
try
HrpExpand.AddRange(Data.ToArray);
Result := Polymod(HrpExpand.ToArray) = 1;
finally
HrpExpand.Free;
end;
end;
function IsBech32Address(
const Str:
string): Boolean;
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;
try
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 := True
else
raise Exception.Create('
invalid bech32 address');
finally
DataValues.Free;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if IsBech32Address('
bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4')
then
ShowMessage('
Valid.');
end;
end.