Skip to content

Commit 49b6a89

Browse files
committed
Add all serialization / deserialization PSBT test cases from BIPs
1 parent de5854e commit 49b6a89

File tree

5 files changed

+335
-51
lines changed

5 files changed

+335
-51
lines changed

lib/Bitcoin/Crypto/PSBT.pm

+13-25
Original file line numberDiff line numberDiff line change
@@ -169,33 +169,21 @@ sub _check_integrity
169169
my ($self) = @_;
170170
my $version = $self->version;
171171

172-
my $check_field = sub {
173-
my ($name, $index) = @_;
174-
175-
my @values = $self->get_field($name, $index);
176-
Bitcoin::Crypto::Exception::PSBT->raise(
177-
"PSBT field $name is required in version $version"
178-
) unless @values == 1;
179-
};
180-
181172
my $required_fields = Bitcoin::Crypto::PSBT::FieldType->get_fields_required_in_version($version);
182-
foreach my $field (@{$required_fields}) {
183-
184-
# NOTE: no required fields need keydata
173+
foreach my $map (@{$self->maps}) {
174+
foreach my $field_type (@{$required_fields}) {
175+
next unless $field_type->map_type eq $map->type;
185176

186-
my $field_type = $field->get_map_type;
187-
if ($field_type eq Bitcoin::Crypto::Constants::psbt_global_map) {
188-
$check_field->($field->name);
177+
my @values = $map->find($field_type);
178+
Bitcoin::Crypto::Exception::PSBT->raise(
179+
"PSBT field " . $field_type->name . " is required in version $version"
180+
) unless @values == 1;
189181
}
190-
elsif ($field_type eq Bitcoin::Crypto::Constants::psbt_input_map) {
191-
for my $input_index (0 .. $self->input_count - 1) {
192-
$check_field->($field->name, $input_index);
193-
}
194-
}
195-
elsif ($field_type eq Bitcoin::Crypto::Constants::psbt_output_map) {
196-
for my $output_index (0 .. $self->output_count - 1) {
197-
$check_field->($field->name, $output_index);
198-
}
182+
183+
foreach my $field (@{$map->fields}) {
184+
Bitcoin::Crypto::Exception::PSBT->raise(
185+
"PSBT field " . $field->type->name . " is not available in version $version"
186+
) unless $field->type->available_in_version($version);
199187
}
200188
}
201189
}
@@ -250,7 +238,7 @@ sub get_all_fields
250238
{
251239
my ($self, $type, $index) = @_;
252240

253-
my $map = $self->_get_map($type->get_map_type, index => $index);
241+
my $map = $self->_get_map($type->map_type, index => $index);
254242
return () unless $map;
255243
return $map->find($type);
256244
}

lib/Bitcoin/Crypto/PSBT/Field.pm

+1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ sub set_map
154154
{
155155
my ($self, $map) = @_;
156156

157+
$self->validate;
157158
$self->_set_map($map);
158159
$map->_check_integrity($self);
159160
return;

lib/Bitcoin/Crypto/PSBT/FieldType.pm

+34-22
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ has param 'code' => (
3232
isa => PositiveOrZeroInt,
3333
);
3434

35+
has param 'map_type' => (
36+
isa => PSBTMapType,
37+
lazy => sub {
38+
my $self = shift;
39+
my $name = $self->name;
40+
die unless $name =~ /^PSBT_([A-Z]+)_/;
41+
return lc $1;
42+
}
43+
);
44+
3545
has param 'serializer' => (
3646
isa => CodeRef,
3747
default => sub {
@@ -424,6 +434,11 @@ my %types = (
424434
key_data => undef,
425435
value_data => "<32-bit little endian uint locktime>",
426436
%uint_32bitLE_serializers,
437+
validator => sub {
438+
my ($value) = @_;
439+
die 'must be greather than or equal to 500000000'
440+
if $value < 500000000;
441+
},
427442
version_status => {
428443
2 => AVAILABLE,
429444
},
@@ -433,6 +448,11 @@ my %types = (
433448
key_data => undef,
434449
value_data => "<32-bit uint locktime>",
435450
%uint_32bitLE_serializers,
451+
validator => sub {
452+
my ($value) = @_;
453+
die 'must be less than 500000000'
454+
unless $value < 500000000;
455+
},
436456
version_status => {
437457
2 => AVAILABLE,
438458
},
@@ -619,7 +639,7 @@ my %types = (
619639
%types = map { $_, __PACKAGE__->new(name => $_, %{$types{$_}}) } keys %types;
620640
my %types_reverse;
621641
foreach my $type (values %types) {
622-
$types_reverse{$type->get_map_type}{$type->code} = $type->name;
642+
$types_reverse{$type->map_type}{$type->code} = $type->name;
623643
}
624644

625645
signature_for get_field_by_code => (
@@ -631,11 +651,20 @@ sub get_field_by_code
631651
{
632652
my ($self, $map_type, $code) = @_;
633653

634-
Bitcoin::Crypto::Exception::PSBT->raise(
635-
"unknown field type code $code in map $map_type"
636-
) unless exists $types_reverse{$map_type}{$code};
654+
return $types{$types_reverse{$map_type}{$code}}
655+
if exists $types_reverse{$map_type}{$code};
637656

638-
return $types{$types_reverse{$map_type}{$code}};
657+
return $self->new(
658+
name => 'UNKNOWN',
659+
map_type => $map_type,
660+
code => $code,
661+
key_data => 'unknown',
662+
value_data => 'unknown',
663+
version_status => {
664+
0 => AVAILABLE,
665+
2 => AVAILABLE,
666+
},
667+
);
639668
}
640669

641670
signature_for get_field_by_name => (
@@ -706,22 +735,5 @@ sub required_in_version
706735
return ($self->version_status->{$version} // '') eq REQUIRED;
707736
}
708737

709-
signature_for get_map_type => (
710-
method => Object,
711-
positional => [],
712-
);
713-
714-
sub get_map_type
715-
{
716-
my ($self) = @_;
717-
my $name = $self->name;
718-
719-
# module programming error has occured if those die
720-
die unless $name =~ /^PSBT_([A-Z]+)_/;
721-
my $namespace = lc $1;
722-
723-
return $namespace;
724-
}
725-
726738
1;
727739

lib/Bitcoin/Crypto/PSBT/Map.pm

-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ sub add
8686
'This field is already used in another map'
8787
) if $field->map;
8888

89-
$field->validate;
9089
$field->set_map($self);
9190
push @{$self->fields}, $field;
9291

0 commit comments

Comments
 (0)