From b826080c1bdcf725b5de0c8f152a3da0076703b5 Mon Sep 17 00:00:00 2001 From: Peter Harpending Date: Fri, 21 Oct 2022 20:35:01 -0600 Subject: [PATCH] wrote an rlp implementation in erlang that seems to work my current methodology for doing X - find library that does X in Python with lots of stars on GitHub - autogenerate test cases against Python library - do X in Erlang to work out logic in easier setting - make sure Erlang implementation works - do X in TypeScript - make sure TypeScript implementation works --- bindings/typescript/test/testgen/rlp.erl | 143 +++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 bindings/typescript/test/testgen/rlp.erl diff --git a/bindings/typescript/test/testgen/rlp.erl b/bindings/typescript/test/testgen/rlp.erl new file mode 100644 index 0000000..b8cb503 --- /dev/null +++ b/bindings/typescript/test/testgen/rlp.erl @@ -0,0 +1,143 @@ +-module(rlp). + +-compile(export_all). +-compile(nowarn_export_all). + +-type decoded_data() :: binary() | [decoded_data()]. + + + +-spec encode(Data) -> RLP + when Data :: decoded_data(), + RLP :: binary(). +%% @doc +%% encode some data + +encode(Binary) when is_binary(Binary) -> + encode_binary(Binary); +encode(List) when is_list(List) -> + encode_list(List). + + + +-spec encode_binary(Bytes) -> RLP + when Bytes :: binary(), + RLP :: binary(). +%% @private +%% encode a binary in rlp +%% @end + +% single byte case when the byte is between 0..127 +% result is the byte itself +encode_binary(<>) when Byte =< 127 -> + <>; +% if the bytestring is 0..55 items long, the first byte is 128 + Length, +% the rest of the string is the string +encode_binary(Bytes) when byte_size(Bytes) =< 55 -> + Size = byte_size(Bytes), + <<(128 + Size), Bytes/binary>>; +% more than 55 bytes long, first byte is 183 + ByteLengthOfLength +% max byte size is 2^64 - 1 +encode_binary(Bytes) when 55 =< byte_size(Bytes), byte_size(Bytes) < (1 bsl 64) -> + SizeInt = byte_size(Bytes), + SizeBytes = binary:encode_unsigned(SizeInt, big), + SizeOfSizeInt = byte_size(SizeBytes), + %% 183 = 128 + 55 + %% SizeOfSizeInt > 0 + <<(183 + SizeOfSizeInt), + SizeBytes/binary, + Bytes/binary>>. + + + +-spec encode_list(List) -> RLP + when List :: [decoded_data()], + RLP :: binary(). +%% @private +%% encode a list in rlp +%% @end + +% first we encode the total payload of the list +% depending on how long it is, we then branch +encode_list(List) -> + Payload = << (encode(Item)) || Item <- List>>, + Payload_Size = byte_size(Payload), + if + Payload_Size =< 55 -> + <<(192 + Payload_Size), Payload/binary>>; + 55 < Payload_Size -> + SizeInt = byte_size(Payload), + SizeBytes = binary:encode_unsigned(SizeInt, big), + SizeOfSizeInt = byte_size(SizeBytes), + %% 247 = 192 + 55 + %% SizeOfSizeInt > 0 + <<(247 + SizeOfSizeInt), + SizeBytes/binary, + Payload/binary>> + end. + + + +-spec decode(RLP) -> {Data, Rest} + when RLP :: binary(), + Data :: decoded_data(), + Rest :: binary(). +%% @doc +%% decode an RLP-encoded string +%% @end + +% if the first byte is between 0 and 127, that is the data +decode(<>) when Byte =< 127 -> + {<>, Rest}; +% if the first byte is between 128 and 183 = 128 + 55, it is a bytestring and +% the length is Byte - 128 +decode(<>) when Byte =< 183 -> + PayloadByteLength = Byte - 128, + %PayloadBitLength = 8 * PayloadByteLength, + %io:format("Byte : ~p~n" + % "Rest : ~w~n" + % "PayloadByteLength : ~p~n", + % %"PayloadBitLength : ~p~n", + % [Byte, Rest, PayloadByteLength]), + <> = Rest, + {Payload, Rest2}; +% If the first byte is between 184 = 183 + 1 and 191 = 183 + 8, it is a +% bytestring. The byte length of the byte length of bytestring is FirstByte - +% 183. Then pull out the actual data +decode(<>) when Byte =< 191 -> + ByteLengthOfByteLength = Byte - 183, + BitLengthOfByteLength = 8 * ByteLengthOfByteLength, + <> = Rest, + <> = Rest2, + {Payload, Rest3}; +% If the first byte is between 192 and 247 = 192 + 55, it is a list. The byte +% length of the list-payload is FirstByte - 192. Then the list payload, which +% needs to be decoded on its own. +decode(<>) when Byte =< 247 -> + ByteLengthOfListPayload = Byte - 192, + <> = Rest, + List = decode_list(ListPayload), + {List, Rest2}; +% If the first byte is between 248 = 247 + 1 and 255 = 247 + 8, it is a list. +% The byte length of the byte length of the list-payload is FirstByte - 247. +% Then the byte length of the list. Then the list payload, which needs to be +% decoded on its own. +decode(<>) -> + ByteLengthOfByteLengthOfListPayload_int = Byte - 247, + BitLengthOfByteLengthOfListPayload_int = 8 * ByteLengthOfByteLengthOfListPayload_int, + <> = Rest, + <> = Rest2, + List = decode_list(ListPayload_bytes), + {List, Rest3}. + +decode_list(<<>>) -> + []; +decode_list(Bytes) -> + {Item, Rest} = decode(Bytes), + [Item | decode_list(Rest)].