OJ6KWAG7XUCYNQ6T3FRJK2QOP7DBO26PZEWDVH3Q2MJKIBMNWRPQC ARCALTEZ4GQXOBTHQ5443AWDZ6WSN2LFOBGTBLPKVMRM2ECGQK7QC JGA5PAC4QBPH62IPO4HPJ6ETR6BYZDR6SKE6NOTOP3YZVM4XY62AC CZG24QKIB45EL7PK4UA6XVG3VEVKI7OAQXN4YMFUKKI2MXLHRANQC 755UGKECZ3PFYEA2TFFOUZTF27CRQTBZWO7UYFU6WQDJMZDBVPRAC WHYWDNSL67TTB2AGAUACXERJ6X54YMNYVC32KJQFEXFFRYA4K3HQC MMWEGAYYQ4CX3U6JJH4MNEZHTAYK6WQBGXIVQ7DL7MVSPGNS7V7AC ] = Decoded,assert_header(Header, TestObj).% TODO: is this one supposed to break?hash_response2_test() ->[{binary, Bin}, {obj, _TestObj}] = examples:hash_response2(),tryError = wire:decode_hash_response(Bin),?assert(Error)catcherror:Reason ->?assert(Reason =:= "invalid hash_count - expected 2 but got 32")end.post_response_test() ->[{binary, Bin}, {obj, TestObj}] = examples:post_response(),Decoded = wire:decode_post_response(Bin),[ {header, Header}, {posts, Posts}
channel_list_response_test() ->[{binary, Bin}, {obj, TestObj}] = examples:channel_list_response(),Decoded = wire:decode_channel_list_response(Bin),[ {header, Header}, {channels, ["default", "dev", "introduction"]}] = Decoded,assert_header(Header, TestObj).post_text_test() ->[{binary, Bin}, {obj, TestObj}] = examples:post_text(),Decoded = posts:decode_post_text(Bin),[ {pubKey, _PubKey}, {links, _Links}, {postType, _PostType}, {timestamp, _TimeStamp}, {channel, Channel}, {text, Text}] = Decoded,?assertEqual("default", Channel),?assertEqual(<<"h€llo world"/utf8>>, Text),assert_post(Decoded, TestObj).post_delete_test() ->[{binary, Bin}, {obj, TestObj}] = examples:post_delete(),Decoded = posts:decode_post_delete(Bin),[ {pubKey, _PubKey}, {links, _Links}, {postType, _PostType}, {timestamp, _TimeStamp}, {hashes, Hashes}] = Decoded,% TODO: assert Hash values?assertEqual(length(maps:get(<<"hashes">>, TestObj)), length(Hashes)),assert_post(Decoded, TestObj).post_info_test() ->[{binary, Bin}, {obj, TestObj}] = examples:post_info(),Decoded = posts:decode_post_info(Bin),[ {pubKey, _PubKey}, {links, _Links}, {postType, _PostType}, {timestamp, _TimeStamp}, {infos, Infos}] = Decoded,?assertEqual(#{"name" => "cabler"}, Infos),assert_post(Decoded, TestObj).post_topic_test() ->[{binary, Bin}, {obj, TestObj}] = examples:post_topic(),Decoded = posts:decode_post_topic(Bin),[ {pubKey, _PubKey}, {links, _Links}, {postType, _PostType}, {timestamp, _TimeStamp}, {channel, Channel}, {topic, Topic}] = Decoded,?assertEqual(maps:get(<<"channel">>, TestObj), Channel),?assertEqual(maps:get(<<"topic">>, TestObj), Topic),assert_post(Decoded, TestObj).post_join_test() ->[{binary, Bin}, {obj, TestObj}] = examples:post_join(),Decoded = posts:decode_post_join(Bin),[ {pubKey, _PubKey}, {links, _Links}, {postType, _PostType}, {timestamp, _TimeStamp}, {channel, Channel}] = Decoded,?assertEqual(maps:get(<<"channel">>, TestObj), Channel),assert_post(Decoded, TestObj).post_leave_test() ->[{binary, Bin}, {obj, TestObj}] = examples:post_leave(),Decoded = posts:decode_post_leave(Bin),[ {pubKey, _PubKey}, {links, _Links}, {postType, _PostType}, {timestamp, _TimeStamp}, {channel, Channel}] = Decoded,?assertEqual(maps:get(<<"channel">>, TestObj), Channel),assert_post(Decoded, TestObj).
assert_post(Post, TestObj) ->?assertEqual(maps:get(<<"postType">>, TestObj), proplists:get_value(postType, Post)),?assertEqual(maps:get(<<"timestamp">>, TestObj), proplists:get_value(timestamp, Post)),?assertEqual(length(maps:get(<<"links">>, TestObj)), length(proplists:get_value(links, Post))),PubKey = hex:hexstr_to_bin(binary_to_list(maps:get(<<"publicKey">>, TestObj))),?assertEqual(PubKey, proplists:get_value(pubKey, Post)).
?assert(proplists:get_value(msgLen, Header) =:= maps:get(<<"msgLen">>, TestObj)),?assert(proplists:get_value(msgType, Header) =:= maps:get(<<"msgType">>, TestObj)),
?assertEqual(maps:get(<<"msgLen">>, TestObj), proplists:get_value(msgLen, Header)),?assertEqual(maps:get(<<"msgType">>, TestObj), proplists:get_value(msgType, Header)),
-export([decode_hash_response/1, decode_cancel_request/1, decode_channel_time_range_request/1, decode_channel_state_request/1, decode_channel_list_request/1]).
-export([decode_hash_response/1, decode_cancel_request/1, decode_channel_time_range_request/1, decode_channel_state_request/1, decode_channel_list_request/1, decode_post_response/1, decode_channel_list_response/1]).
].decode_post_response(Data) ->Header = decode_response_header(Data),Payload = proplists:get_value(payload, Header),[ {header, proplists:delete(payload, Header)}, {posts, decode_list_of_strings(Payload)}].decode_channel_list_response(Data) ->Header = decode_response_header(Data),Payload = proplists:get_value(payload, Header),[ {header, proplists:delete(payload, Header)}, {channels, decode_list_of_strings(Payload)}
decode_list_of_strings(Data) ->decode_list_of_strings(Data, []).decode_list_of_strings(Data, Acc) ->{Len, Rest} = decode_varint(Data),case Len =:= 0 oftrue -> Acc;false -><<Field:Len/binary, Rest2/binary >> = Rest,decode_list_of_strings(Rest2, Acc ++ [binary_to_list(Field)])end.
-module(posts).-export([decode_post_text/1, decode_post_delete/1, decode_post_info/1, decode_post_topic/1, decode_post_join/1, decode_post_leave/1]).decode_post_text(Data) ->Header = decode_post_header(Data),Body = proplists:get_value(postBody, Header),{ChannelLen, Rest} = wire:decode_varint(Body),<<Channel:(ChannelLen)/binary, Rest2/binary>> = Rest,{TextLen, Rest3} = wire:decode_varint(Rest2),<<Text:(TextLen)/binary>> = Rest3,proplists:delete(postBody, Header) ++ [ {channel, binary_to_list(Channel)}, {text, unicode:characters_to_binary(Text)}].decode_post_delete(Data) ->Header = decode_post_header(Data),Body = proplists:get_value(postBody, Header),{NumHashes, Rest} = wire:decode_varint(Body),<<HashData:(32*NumHashes)/binary >> = Rest,Hashes = [Hash || <<Hash:32/binary>> <= HashData],proplists:delete(postBody, Header) ++ [ {hashes, Hashes} ].decode_post_info(Data) ->Header = decode_post_header(Data),Body = proplists:get_value(postBody, Header),KVs = wire:decode_list_of_strings(Body),% turn [A, B, C, D] into #{A => B, C => D}% TODO: i wonder if there is a more direct way for this conversion{Indexed,_} = lists:mapfoldl(fun(V, Acc) -> { {V, Acc}, Acc+1 } end, 0, KVs),Even = [ K || {K, I} <- Indexed, I rem 2 =:= 0],Odd = [ K || {K, I} <- Indexed, I rem 2 =:= 1],Infos = maps:from_list(lists:zip(Even, Odd)),proplists:delete(postBody, Header) ++ [ {infos, Infos}].decode_post_topic(Data) ->Header = decode_post_header(Data),Body = proplists:get_value(postBody, Header),{ChannelLen, Rest} = wire:decode_varint(Body),<<Channel:(ChannelLen)/binary, Rest2/binary>> = Rest,{TopicLen, Rest3} = wire:decode_varint(Rest2),<<Topic:(TopicLen)/binary>> = Rest3,proplists:delete(postBody, Header) ++ [ {channel, Channel}, {topic, Topic}].decode_post_join(Data) ->Header = decode_post_header(Data),Body = proplists:get_value(postBody, Header),{ChannelLen, Rest} = wire:decode_varint(Body),<<Channel:(ChannelLen)/binary>> = Rest,proplists:delete(postBody, Header) ++ [ {channel, Channel}].decode_post_leave(Data) ->Header = decode_post_header(Data),Body = proplists:get_value(postBody, Header),{ChannelLen, Rest} = wire:decode_varint(Body),<<Channel:(ChannelLen)/binary>> = Rest,proplists:delete(postBody, Header) ++ [ {channel, Channel}].decode_post_header(Data) -><< PubKey:32/binary, Signature:64/binary, SignedData/binary>> = Data,true = enacl:sign_verify_detached(Signature, SignedData, PubKey),{NumLinks, Rest} = wire:decode_varint(SignedData),<<LinkData:(32*NumLinks)/binary, Rest2/binary>> = Rest,Links = [Link || <<Link:32/binary>> <= LinkData],case length(Links) =:= NumLinks offalse ->ErrMsg = io_lib:format("invalid num_links - expected ~p but got ~p", [NumLinks, length(Links)]),erlang:error(lists:flatten(ErrMsg));true ->[PostType, Timestamp, PostBody] = wire:decode_varints(Rest2, 2),[ {pubKey, PubKey}, {links, Links}, {postType, PostType}, {timestamp, Timestamp}, {postBody, PostBody}]end.
-export([hash_response/0, cancel_request/0, channel_time_range_request/0, channel_state_request/0, channel_list_request/0]).
-export([hash_response/0, cancel_request/0, channel_time_range_request/0,channel_state_request/0, channel_list_request/0,hash_response2/0, post_response/0, channel_list_response/0,post_text/0, post_delete/0, post_info/0, post_topic/0, post_join/0, post_leave/0]).
D = <<"{\"name\":\"hashresponse\",\"type\":\"response\",\"id\":2,\"binary\":\"6b020000000095050429010320265674e8aac2dfddd78f86fe5a3dd68d976ca3f5ba23645ec7381480921d0d10705340e5528f2ef03a6797b72b1bb9f37f9009ad408247387c4bcc4d2a3371af700793dd51d4cb3c18a6df46f88bfe1665fba9b277487ddecd1e031441d69d\",\"obj\":{\"msgLen\":107,\"msgType\":2,\"reqid\":\"95050429\",\"ttl\":1,\"hashes\":[\"20265674e8aac2dfddd78f86fe5a3dd68d976ca3f5ba23645ec7381480921d0d\",\"10705340e5528f2ef03a6797b72b1bb9f37f9009ad408247387c4bcc4d2a3371\",\"af700793dd51d4cb3c18a6df46f88bfe1665fba9b277487ddecd1e031441d69d\"]}}">>,
D = <<"{\"name\":\"hash response\",\"type\":\"response\",\"id\":2,\"binary\":\"6b020000000095050429010320265674e8aac2dfddd78f86fe5a3dd68d976ca3f5ba23645ec7381480921d0d10705340e5528f2ef03a6797b72b1bb9f37f9009ad408247387c4bcc4d2a3371af700793dd51d4cb3c18a6df46f88bfe1665fba9b277487ddecd1e031441d69d\",\"obj\":{\"msgLen\":107,\"msgType\":2,\"reqid\":\"95050429\",\"ttl\":1,\"hashes\":[\"20265674e8aac2dfddd78f86fe5a3dd68d976ca3f5ba23645ec7381480921d0d\",\"10705340e5528f2ef03a6797b72b1bb9f37f9009ad408247387c4bcc4d2a3371\",\"af700793dd51d4cb3c18a6df46f88bfe1665fba9b277487ddecd1e031441d69d\"]}}">>,
decode_example_json(D).hash_response2() ->D = <<"{\"name\":\"hash response\",\"type\":\"response\",\"id\":0,\"binary\":\"6a0000000000950504290320265674e8aac2dfddd78f86fe5a3dd68d976ca3f5ba23645ec7381480921d0d10705340e5528f2ef03a6797b72b1bb9f37f9009ad408247387c4bcc4d2a3371af700793dd51d4cb3c18a6df46f88bfe1665fba9b277487ddecd1e031441d69d\",\"obj\":{\"msgLen\":106,\"msgType\":0,\"reqid\":\"95050429\",\"hashes\":[\"20265674e8aac2dfddd78f86fe5a3dd68d976ca3f5ba23645ec7381480921d0d\",\"10705340e5528f2ef03a6797b72b1bb9f37f9009ad408247387c4bcc4d2a3371\",\"af700793dd51d4cb3c18a6df46f88bfe1665fba9b277487ddecd1e031441d69d\"]}}">>,decode_example_json(D).post_response() ->D = <<"{\"name\":\"post response\",\"type\":\"response\",\"id\":1,\"binary\":\"97010100000000950504298b0125b272a71555322d40efe449a7f99af8fd364b92d350f1664481b2da340a02d0abb083ecdca569f064564942ddf1944fbf550dc27ea36a7074be798d753cb029703de77b1a9532b6ca2ec5706e297dce073d6e508eeb425c32df8431e4677805015049d089a650aa896cb25ec35258653be4df196b4a5e5b6db7ed024aaa89e1b305500764656661756c7400\",\"obj\":{\"msgLen\":151,\"msgType\":1,\"reqid\":\"95050429\",\"posts\":[\"25b272a71555322d40efe449a7f99af8fd364b92d350f1664481b2da340a02d0abb083ecdca569f064564942ddf1944fbf550dc27ea36a7074be798d753cb029703de77b1a9532b6ca2ec5706e297dce073d6e508eeb425c32df8431e4677805015049d089a650aa896cb25ec35258653be4df196b4a5e5b6db7ed024aaa89e1b305500764656661756c74\"]}}">>,decode_example_json(D).channel_list_response() ->D = <<"{\"name\":\"channel list response\",\"type\":\"response\",\"id\":7,\"binary\":\"230700000000950504290764656661756c74036465760c696e74726f64756374696f6e00\",\"obj\":{\"msgLen\":35,\"msgType\":7,\"reqid\":\"95050429\",\"channels\":[\"default\",\"dev\",\"introduction\"]}}">>,decode_example_json(D).post_text() ->D = <<"{\"name\":\"post/text\",\"type\":\"post\",\"id\":0,\"binary\":\"25b272a71555322d40efe449a7f99af8fd364b92d350f1664481b2da340a02d06725733046b35fa3a7e8dc0099a2b3dff10d3fd8b0f6da70d094352e3f5d27a8bc3f5586cf0bf71befc22536c3c50ec7b1d64398d43c3f4cde778e579e88af05015049d089a650aa896cb25ec35258653be4df196b4a5e5b6db7ed024aaa89e1b300500764656661756c740d68e282ac6c6c6f20776f726c64\",\"obj\":{\"publicKey\":\"25b272a71555322d40efe449a7f99af8fd364b92d350f1664481b2da340a02d0\",\"signature\":\"6725733046b35fa3a7e8dc0099a2b3dff10d3fd8b0f6da70d094352e3f5d27a8bc3f5586cf0bf71befc22536c3c50ec7b1d64398d43c3f4cde778e579e88af05\",\"links\":[\"5049d089a650aa896cb25ec35258653be4df196b4a5e5b6db7ed024aaa89e1b3\"],\"postType\":0,\"channel\":\"default\",\"timestamp\":80,\"text\":\"h€llo world\"}}">>,decode_example_json(D).post_delete() ->D = <<"{\"name\":\"post/delete\",\"type\":\"post\",\"id\":1,\"binary\":\"25b272a71555322d40efe449a7f99af8fd364b92d350f1664481b2da340a02d0e8fc6c809f3086627879520abe6f76a4810a8bef77a668f41046c48dc98c13ed55aa54ca1e6913076bd7791c6c97aa807850bc6be7415fa5d251b9b26febd101015049d089a650aa896cb25ec35258653be4df196b4a5e5b6db7ed024aaa89e1b301500320265674e8aac2dfddd78f86fe5a3dd68d976ca3f5ba23645ec7381480921d0d10705340e5528f2ef03a6797b72b1bb9f37f9009ad408247387c4bcc4d2a3371af700793dd51d4cb3c18a6df46f88bfe1665fba9b277487ddecd1e031441d69d\",\"obj\":{\"publicKey\":\"25b272a71555322d40efe449a7f99af8fd364b92d350f1664481b2da340a02d0\",\"signature\":\"e8fc6c809f3086627879520abe6f76a4810a8bef77a668f41046c48dc98c13ed55aa54ca1e6913076bd7791c6c97aa807850bc6be7415fa5d251b9b26febd101\",\"links\":[\"5049d089a650aa896cb25ec35258653be4df196b4a5e5b6db7ed024aaa89e1b3\"],\"postType\":1,\"timestamp\":80,\"hashes\":[\"20265674e8aac2dfddd78f86fe5a3dd68d976ca3f5ba23645ec7381480921d0d\",\"10705340e5528f2ef03a6797b72b1bb9f37f9009ad408247387c4bcc4d2a3371\",\"af700793dd51d4cb3c18a6df46f88bfe1665fba9b277487ddecd1e031441d69d\"]}}">>,decode_example_json(D).post_info() ->D = <<"{\"name\":\"post/info\",\"type\":\"post\",\"id\":2,\"binary\":\"25b272a71555322d40efe449a7f99af8fd364b92d350f1664481b2da340a02d04ccb1c0063ef09a200e031ee89d874bcc99f3e6fd8fd667f5e28f4dbcf4b7de6bb1ce37d5f01cc055a7b70cef175d30feeb34531db98c91fa8b3fa4d7c5fd307015049d089a650aa896cb25ec35258653be4df196b4a5e5b6db7ed024aaa89e1b30250046e616d65066361626c657200\",\"obj\":{\"publicKey\":\"25b272a71555322d40efe449a7f99af8fd364b92d350f1664481b2da340a02d0\",\"signature\":\"4ccb1c0063ef09a200e031ee89d874bcc99f3e6fd8fd667f5e28f4dbcf4b7de6bb1ce37d5f01cc055a7b70cef175d30feeb34531db98c91fa8b3fa4d7c5fd307\",\"links\":[\"5049d089a650aa896cb25ec35258653be4df196b4a5e5b6db7ed024aaa89e1b3\"],\"postType\":2,\"timestamp\":80,\"key\":\"name\",\"value\":\"cabler\"}}">>,decode_example_json(D).post_topic() ->D = <<"{\"name\":\"post/topic\",\"type\":\"post\",\"id\":3,\"binary\":\"25b272a71555322d40efe449a7f99af8fd364b92d350f1664481b2da340a02d0bf7578e781caee4ca708281645b291a2100c4f2138f0e0ac98bc2b4a414b4ba8dca08285751114b05f131421a1745b648c43b17b05392593237dfacc8dff5208015049d089a650aa896cb25ec35258653be4df196b4a5e5b6db7ed024aaa89e1b303500764656661756c743b696e74726f6475636520796f757273656c6620746f2074686520667269656e646c792063726f7764206f66206c696b656d696e64656420666f6c78\",\"obj\":{\"publicKey\":\"25b272a71555322d40efe449a7f99af8fd364b92d350f1664481b2da340a02d0\",\"signature\":\"bf7578e781caee4ca708281645b291a2100c4f2138f0e0ac98bc2b4a414b4ba8dca08285751114b05f131421a1745b648c43b17b05392593237dfacc8dff5208\",\"links\":[\"5049d089a650aa896cb25ec35258653be4df196b4a5e5b6db7ed024aaa89e1b3\"],\"postType\":3,\"channel\":\"default\",\"timestamp\":80,\"topic\":\"introduce yourself to the friendly crowd of likeminded folx\"}}">>,decode_example_json(D).post_join() ->D = <<"{\"name\":\"post/join\",\"type\":\"post\",\"id\":4,\"binary\":\"25b272a71555322d40efe449a7f99af8fd364b92d350f1664481b2da340a02d064425f10fa34c1e14b6101491772d3c5f15f720a952dd56c27d5ad52f61f695130ce286de73e332612b36242339b61c9e12397f5dcc94c79055c7e1cb1dbfb08015049d089a650aa896cb25ec35258653be4df196b4a5e5b6db7ed024aaa89e1b304500764656661756c74\",\"obj\":{\"publicKey\":\"25b272a71555322d40efe449a7f99af8fd364b92d350f1664481b2da340a02d0\",\"signature\":\"64425f10fa34c1e14b6101491772d3c5f15f720a952dd56c27d5ad52f61f695130ce286de73e332612b36242339b61c9e12397f5dcc94c79055c7e1cb1dbfb08\",\"links\":[\"5049d089a650aa896cb25ec35258653be4df196b4a5e5b6db7ed024aaa89e1b3\"],\"postType\":4,\"channel\":\"default\",\"timestamp\":80}}">>,
post_leave() ->D = <<"{\"name\":\"post/leave\",\"type\":\"post\",\"id\":5,\"binary\":\"25b272a71555322d40efe449a7f99af8fd364b92d350f1664481b2da340a02d0abb083ecdca569f064564942ddf1944fbf550dc27ea36a7074be798d753cb029703de77b1a9532b6ca2ec5706e297dce073d6e508eeb425c32df8431e4677805015049d089a650aa896cb25ec35258653be4df196b4a5e5b6db7ed024aaa89e1b305500764656661756c74\",\"obj\":{\"publicKey\":\"25b272a71555322d40efe449a7f99af8fd364b92d350f1664481b2da340a02d0\",\"signature\":\"abb083ecdca569f064564942ddf1944fbf550dc27ea36a7074be798d753cb029703de77b1a9532b6ca2ec5706e297dce073d6e508eeb425c32df8431e4677805\",\"links\":[\"5049d089a650aa896cb25ec35258653be4df196b4a5e5b6db7ed024aaa89e1b3\"],\"postType\":5,\"channel\":\"default\",\"timestamp\":80}}">>,decode_example_json(D).