-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create cid #21
Create cid #21
Changes from 11 commits
1fb3965
1e4e2aa
74f3cf4
b17d84b
0e4f639
011b284
597b817
3b96a0b
2412717
d2a7671
1ae6128
8a21996
ce987d2
3369a12
1c142e2
74c58d2
ec620ec
aa4f2c6
d15e914
3ec0d4d
53ccfaf
58e1c09
89f3806
8f97c9a
89dbdc1
217d684
fc2409c
b287876
0325986
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
language: elixir | ||
elixir: | ||
- 1.8 | ||
env: | ||
- MIX_ENV=test | ||
script: | ||
- mix test |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,38 +1,87 @@ | ||
defmodule Cid do | ||
@moduledoc """ | ||
Returns a SHA512 transformed to Base64, remove ambiguous chars then sub-string | ||
Provides a way for a user to turn a String, Map or Struct into a CID that | ||
is identical to one what will be returned from IPFS if the same data is | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A link to an explanation of what IPFS is might be good here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please link to it in the |
||
added. | ||
|
||
Currently only produces a default v1 CID. | ||
Currently only uses the "raw" codec | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe add a little more info on what a v1 CID or "raw" codec is. Just a link would probably be fine, even if it's to elsewhere in this repo. |
||
Data provided must be under 256Kb in order for the CID to match the one | ||
returned by IPFS | ||
""" | ||
|
||
@doc """ | ||
make/2 create a SHA512 hash from the given input and return the require length | ||
note: we remove "ambiguous" characters so _humans_ can type the hash without | ||
getting "confused" this might not be required, but is to match the original | ||
"Hits" implementation. | ||
Returns a CID that identical to one returned by IPFS if given the same data. | ||
Can take a String, Map or Struct as an argument. | ||
|
||
## Parameters | ||
## Examples | ||
|
||
- input: String the string to be hashed. | ||
- length: Number the length of string required | ||
iex> Cid.cid("hello") | ||
"zb2rhcc1wJn2GHDLT2YkmPq5b69cXc2xfRZZmyufbjFUfBkxr" | ||
|
||
Returns String hash of desired length. | ||
iex> Cid.cid(%{key: "value"}) | ||
"zb2rhkN6szWhAmBFjjP8RSczv2YVNLnG1tz1Q7FyfEp8LssNZ" | ||
""" | ||
def make(input) when is_map(input) do | ||
input |> stringify_map_values |> make | ||
def cid(value) do | ||
value | ||
|> create_multihash() | ||
|> create_cid() | ||
end | ||
|
||
# if create_multihash is called with a struct, the struct is converted into a | ||
# map and then create_multihash is called again | ||
defp create_multihash(%_{} = struct) do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A brief explanation of how this pattern matching works would be useful for beginners |
||
struct | ||
|> Map.from_struct() | ||
|> create_multihash() | ||
end | ||
|
||
def make(input, length \\ 32) do | ||
hash1 = :crypto.hash(:sha512, input) | ||
{:ok, <<_multihash_code, _length, hash2::binary>>} = Multihash.encode(:sha2_512, hash1) | ||
# if create_multihash is called with a map the map is converted into a JSON | ||
# string and then create_multihash is called again | ||
defp create_multihash(map) when is_map(map) do | ||
map | ||
|> Jason.encode!() | ||
|> create_multihash() | ||
end | ||
|
||
# if create_multihash is called with a string, the string has a new line added | ||
# to the end (as that's what IPFS appears to be doing based on tests), then | ||
# the string is converted into a multihash | ||
defp create_multihash(str) when is_binary(str) do | ||
str = add_new_line(str) | ||
digest = :crypto.hash(:sha256, str) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Explaining in the comments that this is an erlang function would be good for beginners |
||
{:ok, multihash} = Multihash.encode(:sha2_256, digest) | ||
|
||
hash2 | ||
|> Base.encode64() | ||
|> String.replace(~r/[Il0oO=\/\+]/, "", global: true) | ||
|> String.slice(0..(length - 1)) | ||
multihash | ||
end | ||
|
||
def stringify_map_values(input_map) do | ||
Enum.sort(Map.keys(input_map)) # sort map keys for consistent ordering | ||
|> Enum.map(fn (x) -> Map.get(input_map, x) end) | ||
|> Enum.join("") | ||
# if create_multihash is called something that is not a string, map or struct | ||
# then it returns an error. | ||
defp create_multihash(_), do: {:error, "invalid data type"} | ||
|
||
# if an error is passed in return error message | ||
defp create_cid({:error, msg}), do: msg | ||
|
||
# takes a multihash and retuns a CID | ||
defp create_cid(multihash) when is_binary(multihash) do | ||
multihash | ||
|> create_cid_suffix() | ||
|> B58.encode58() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could do with a brief explanation of B58 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just added this in one of the latest commits. Thanks for pointing that out |
||
|> add_multibase_prefix() | ||
end | ||
|
||
# takes a multihash and returns the suffix | ||
# currently version is hardcoded to 1 | ||
# and multicodec-packed-content-type is hardcoded to "raw" ("U" == <<85>>) | ||
# <version><multicodec-packed-content-type><multihash> | ||
defp create_cid_suffix(multihash), do: <<1>> <> "U" <> multihash | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A small comment on the binary syntax might be useful to people unfamiliar with it |
||
|
||
# adds the multibase prefix to the suffix (<version><mc><mh>) | ||
defp add_multibase_prefix(suffix), do: "z" <> suffix | ||
|
||
# adds new line to the end of string. (exists because all tests with ipfs | ||
# appeared to do the same thing.) | ||
defp add_new_line(str) do | ||
str <> "\n" | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
%{ | ||
"basefiftyeight": {:hex, :basefiftyeight, "0.1.0", "3d48544743bf9aab7ab02aed803ac42af77acf268c7d8c71d4f39e7fa85ee8d3", [:mix], [], "hexpm"}, | ||
"ex_multihash": {:hex, :ex_multihash, "2.0.0", "7fb36f842a2ec1c6bbba550f28fcd16d3c62981781b9466c9c1975c43d7db43c", [:mix], [], "hexpm"}, | ||
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 👍 👍 👍 👍 👍 👍 👍 👍 👍 👍 👍 👍 👍 👍 👍 👍 👍 👍 👍 👍 👍 👍 👍
Good addition @nelsonic