@@ -80,78 +80,97 @@ defmodule LambdaEthereumConsensus.SszEx do
80
80
else: decode_fixed_container ( binary , module )
81
81
end
82
82
83
- @ spec hash_tree_root! ( boolean , atom ) :: Types . root ( )
84
- def hash_tree_root! ( value , :bool ) , do: pack ( value , :bool )
83
+ @ spec hash_tree_root! ( any , any ) :: Types . root ( )
84
+ def hash_tree_root! ( value , schema ) do
85
+ { :ok , root } = hash_tree_root ( value , schema )
86
+ root
87
+ end
85
88
86
- @ spec hash_tree_root! ( non_neg_integer , { :int , non_neg_integer } ) :: Types . root ( )
87
- def hash_tree_root! ( value , { :int , size } ) , do: pack ( value , { :int , size } )
89
+ @ spec hash_tree_root ( boolean , atom ) :: Types . root ( )
90
+ def hash_tree_root ( value , :bool ) , do: { :ok , pack ( value , :bool ) }
88
91
89
- @ spec hash_tree_root! ( binary , { :bytes , non_neg_integer } ) :: Types . root ( )
90
- def hash_tree_root! ( value , { :bytes , size } ) do
92
+ @ spec hash_tree_root ( non_neg_integer , { :int , non_neg_integer } ) :: Types . root ( )
93
+ def hash_tree_root ( value , { :int , size } ) , do: { :ok , pack ( value , { :int , size } ) }
94
+
95
+ @ spec hash_tree_root ( binary , { :bytes , non_neg_integer } ) :: Types . root ( )
96
+ def hash_tree_root ( value , { :bytes , size } ) do
91
97
packed_chunks = pack ( value , { :bytes , size } )
92
98
leaf_count = packed_chunks |> get_chunks_len ( ) |> next_pow_of_two ( )
93
99
root = merkleize_chunks_with_virtual_padding ( packed_chunks , leaf_count )
94
- root
95
- end
96
-
97
- @ spec hash_tree_root! ( list ( ) , { :list , any , non_neg_integer } ) :: Types . root ( )
98
- def hash_tree_root! ( list , { :list , type , size } ) do
99
- { :ok , root } = hash_tree_root ( list , { :list , type , size } )
100
- root
101
- end
102
-
103
- @ spec hash_tree_root! ( list ( ) , { :vector , any , non_neg_integer } ) :: Types . root ( )
104
- def hash_tree_root! ( vector , { :vector , type , size } ) do
105
- { :ok , root } = hash_tree_root ( vector , { :vector , type , size } )
106
- root
100
+ { :ok , root }
107
101
end
108
102
109
- @ spec hash_tree_root! ( struct ( ) , atom ( ) ) :: Types . root ( )
110
- def hash_tree_root! ( container , module ) when is_map ( container ) do
111
- chunks =
103
+ @ spec hash_tree_root ( struct ( ) , atom ( ) ) :: Types . root ( )
104
+ def hash_tree_root ( container , module ) when is_map ( container ) do
105
+ value =
112
106
module . schema ( )
113
- |> Enum . reduce ( << >> , fn { key , schema } , acc_root ->
107
+ |> Enum . reduce_while ( { :ok , << >> } , fn { key , schema } , { _ , acc_root } ->
114
108
value = container |> Map . get ( key )
115
- root = hash_tree_root! ( value , schema )
116
- acc_root <> root
109
+
110
+ case hash_tree_root ( value , schema ) do
111
+ { :ok , root } -> { :cont , { :ok , acc_root <> root } }
112
+ { :error , reason } -> { :halt , { :error , reason } }
113
+ end
117
114
end )
118
115
119
- leaf_count = chunks |> get_chunks_len ( ) |> next_pow_of_two ( )
120
- root = merkleize_chunks_with_virtual_padding ( chunks , leaf_count )
121
- root
116
+ case value do
117
+ { :ok , chunks } ->
118
+ leaf_count = chunks |> get_chunks_len ( ) |> next_pow_of_two ( )
119
+ root = chunks |> merkleize_chunks_with_virtual_padding ( leaf_count )
120
+ { :ok , root }
121
+
122
+ { :error , reason } ->
123
+ { :error , reason }
124
+ end
122
125
end
123
126
124
127
@ spec hash_tree_root ( list ( ) , { :list , any , non_neg_integer } ) ::
125
128
{ :ok , Types . root ( ) } | { :error , String . t ( ) }
126
- def hash_tree_root ( list , { :list , type , size } ) do
127
- if variable_size? ( type ) do
128
- # TODO
129
- # hash_tree_root_list_complex_type(list, {:list, type, size}, limit)
130
- { :error , "Not implemented" }
131
- else
132
- packed_chunks = pack ( list , { :list , type , size } )
133
- limit = chunk_count ( { :list , type , size } )
134
- len = length ( list )
135
- hash_tree_root_list_basic_type ( packed_chunks , limit , len )
129
+ def hash_tree_root ( list , { :list , type , _size } = schema ) do
130
+ limit = chunk_count ( schema )
131
+ len = length ( list )
132
+
133
+ value =
134
+ if basic_type? ( type ) do
135
+ pack ( list , schema )
136
+ else
137
+ list_hash_tree_root ( list , type )
138
+ end
139
+
140
+ case value do
141
+ { :ok , chunks } -> chunks |> hash_tree_root_list ( limit , len )
142
+ { :error , reason } -> { :error , reason }
143
+ chunks -> chunks |> hash_tree_root_list ( limit , len )
136
144
end
137
145
end
138
146
139
147
@ spec hash_tree_root ( list ( ) , { :vector , any , non_neg_integer } ) ::
140
148
{ :ok , Types . root ( ) } | { :error , String . t ( ) }
141
- def hash_tree_root ( vector , { :vector , type , size } ) do
142
- if variable_size? ( type ) do
143
- # TODO
144
- # hash_tree_root_vector_complex_type(vector, {:vector, type, size}, limit)
145
- { :error , "Not implemented" }
146
- else
147
- packed_chunks = pack ( vector , { :list , type , size } )
148
- hash_tree_root_vector_basic_type ( packed_chunks )
149
+ def hash_tree_root ( vector , { :vector , _type , size } ) when length ( vector ) != size ,
150
+ do: { :error , "invalid size" }
151
+
152
+ def hash_tree_root ( vector , { :vector , type , _size } = schema ) do
153
+ value =
154
+ if basic_type? ( type ) do
155
+ pack ( vector , schema )
156
+ else
157
+ list_hash_tree_root ( vector , type )
158
+ end
159
+
160
+ case value do
161
+ { :ok , chunks } -> chunks |> hash_tree_root_vector ( )
162
+ { :error , reason } -> { :error , reason }
163
+ chunks -> chunks |> hash_tree_root_vector ( )
149
164
end
150
165
end
151
166
152
- @ spec hash_tree_root_list_basic_type ( binary ( ) , non_neg_integer , non_neg_integer ) ::
153
- { :ok , Types . root ( ) } | { :error , String . t ( ) }
154
- def hash_tree_root_list_basic_type ( chunks , limit , len ) do
167
+ def hash_tree_root_vector ( chunks ) do
168
+ leaf_count = chunks |> get_chunks_len ( ) |> next_pow_of_two ( )
169
+ root = merkleize_chunks_with_virtual_padding ( chunks , leaf_count )
170
+ { :ok , root }
171
+ end
172
+
173
+ def hash_tree_root_list ( chunks , limit , len ) do
155
174
chunks_len = chunks |> get_chunks_len ( )
156
175
157
176
if chunks_len > limit do
@@ -162,14 +181,6 @@ defmodule LambdaEthereumConsensus.SszEx do
162
181
end
163
182
end
164
183
165
- @ spec hash_tree_root_vector_basic_type ( binary ( ) ) ::
166
- { :ok , Types . root ( ) } | { :error , String . t ( ) }
167
- def hash_tree_root_vector_basic_type ( chunks ) do
168
- leaf_count = chunks |> get_chunks_len ( ) |> next_pow_of_two ( )
169
- root = merkleize_chunks_with_virtual_padding ( chunks , leaf_count )
170
- { :ok , root }
171
- end
172
-
173
184
@ spec mix_in_length ( Types . root ( ) , non_neg_integer ) :: Types . root ( )
174
185
def mix_in_length ( root , len ) do
175
186
{ :ok , serialized_len } = encode_int ( len , @ bits_per_chunk )
@@ -260,18 +271,21 @@ defmodule LambdaEthereumConsensus.SszEx do
260
271
261
272
@ spec pack ( list ( ) , { :list | :vector , any , non_neg_integer } ) :: binary ( ) | :error
262
273
def pack ( list , { type , schema , _ } ) when type in [ :vector , :list ] do
263
- if variable_size? ( schema ) do
264
- # TODO
265
- # pack_complex_type_list(list)
266
- :error
267
- else
268
- pack_basic_type_list ( list , schema )
269
- end
274
+ list
275
+ |> Enum . reduce ( << >> , fn x , acc ->
276
+ { :ok , encoded } = encode ( x , schema )
277
+ acc <> encoded
278
+ end )
279
+ |> pack_bytes ( )
270
280
end
271
281
272
282
def chunk_count ( { :list , type , max_size } ) do
273
- size = size_of ( type )
274
- ( max_size * size + 31 ) |> div ( 32 )
283
+ if basic_type? ( type ) do
284
+ size = size_of ( type )
285
+ ( max_size * size + 31 ) |> div ( 32 )
286
+ else
287
+ max_size
288
+ end
275
289
end
276
290
277
291
#################
@@ -779,19 +793,19 @@ defmodule LambdaEthereumConsensus.SszEx do
779
793
|> Enum . any? ( )
780
794
end
781
795
796
+ defp basic_type? ( { :int , _ } ) , do: true
797
+ defp basic_type? ( :bool ) , do: true
798
+ defp basic_type? ( { :bytes , _ } ) , do: true
799
+ defp basic_type? ( { :list , _ , _ } ) , do: false
800
+ defp basic_type? ( { :vector , _ , _ } ) , do: false
801
+ defp basic_type? ( { :bitlist , _ } ) , do: false
802
+ defp basic_type? ( { :bitvector , _ } ) , do: false
803
+ defp basic_type? ( module ) when is_atom ( module ) , do: false
804
+
782
805
defp size_of ( :bool ) , do: @ bytes_per_boolean
783
806
784
807
defp size_of ( { :int , size } ) , do: size |> div ( @ bits_per_byte )
785
808
786
- defp pack_basic_type_list ( list , schema ) do
787
- list
788
- |> Enum . reduce ( << >> , fn x , acc ->
789
- { :ok , encoded } = encode ( x , schema )
790
- acc <> encoded
791
- end )
792
- |> pack_bytes ( )
793
- end
794
-
795
809
defp pack_bytes ( value ) when is_binary ( value ) do
796
810
incomplete_chunk_len = value |> bit_size ( ) |> rem ( @ bits_per_chunk )
797
811
@@ -894,4 +908,14 @@ defmodule LambdaEthereumConsensus.SszEx do
894
908
<< _ :: binary - size ( offset ) , hash :: binary - size ( @ bytes_per_chunk ) , _ :: binary >> = @ zero_hashes
895
909
hash
896
910
end
911
+
912
+ def list_hash_tree_root ( list , inner_schema ) do
913
+ list
914
+ |> Enum . reduce_while ( { :ok , << >> } , fn value , { _ , acc_roots } ->
915
+ case hash_tree_root ( value , inner_schema ) do
916
+ { :ok , root } -> { :cont , { :ok , acc_roots <> root } }
917
+ { :error , reason } -> { :halt , { :error , reason } }
918
+ end
919
+ end )
920
+ end
897
921
end
0 commit comments