summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Nordberg <linus@nordu.net>2014-04-25 13:25:04 +0200
committerLinus Nordberg <linus@nordu.net>2014-04-25 13:25:04 +0200
commit5904738b98e9756e362ed9a81f2d831621493311 (patch)
tree00bdc6fa713d1cc0db126f46f062a4ca803d15bb
parent80ae7bd6d3e139078ee58d9861f159b555d28b38 (diff)
Allow for empty hash trees.
-rw-r--r--src/ht.erl39
1 files changed, 30 insertions, 9 deletions
diff --git a/src/ht.erl b/src/ht.erl
index 68c3c67..5f41991 100644
--- a/src/ht.erl
+++ b/src/ht.erl
@@ -9,23 +9,24 @@
tree :: tree()}). % the tree
-type head() :: #head{}.
--type tree() :: inner() | leaf().
+-type tree() :: inner() | leaf() | undefined.
-type leaf() :: #leaf{}.
-type inner() :: #inner{}.
-export_type([head/0, tree/0, inner/0, leaf/0]).
--export([create/1, append/2, tree_hash/1, tree_version/1,
+-export([create/0, append/2, tree_hash/1, tree_version/1,
audit_path/2]).
%% Public interface.
--spec create(iolist() | binary()) -> head().
-create(D) ->
- mkhead(1, mkleaf(D)).
+-spec create() -> head().
+create() ->
+ mkhead(0, undefined).
-spec tree_hash(head()) -> binary();
(tree()) -> binary().
tree_hash(#head{tree=T}) ->
case T of
+ undefined -> hashfun(<<>>);
#inner{hash=H} -> H;
#leaf{hash=H} -> H
end.
@@ -55,14 +56,18 @@ tree_version(#head{version=Ver}) ->
%% Example: N=4 (100) => l=2, the root (soon not to be root).
%% Example: N=5 (101) => l=0, the rightmost leaf.
%% Example: N=6 (110) => l=1, the last and rightmost inner node.
--spec append(head(), leaf()) -> head().
+-spec append(head(), leaf() | iolist() | binary()) -> head().
+append(#head{version = 0}, Leaf) when is_record(Leaf, leaf) ->
+ mkhead(0, Leaf);
append(Head, Leaf) when is_record(Leaf, leaf) ->
N = Head#head.version,
%Depth = depth(Head),
Level = fls(N),
RBD = rightbranchdepth(Head#head.tree),
%io:format("N=~p, Depth=~p, Level=~p, RBD=~p~n", [N, Depth, Level, RBD]),
- #head{version = N + 1, tree = append(Head#head.tree, Leaf, RBD-Level-1)}.
+ #head{version = N + 1, tree = append(Head#head.tree, Leaf, RBD-Level-1)};
+append(Head, Data) ->
+ append(Head, mkleaf(Data)).
-spec append(tree(), tree(), pos_integer()) -> tree().
append(Dest, Newtree, _) when is_record(Dest, leaf) ->
@@ -158,6 +163,18 @@ rightbranchdepth(Tree, Acc) ->
%%%%%%%%%%%%%%%%%%%%
%% Internal tests.
+
+-define(TEST_VECTOR_TREES,
+ [<<148,242,40,0,3,172,180,106,111,230,146,161,32,40,128,38,103,8,194,
+ 102,72,68, 126,70,108,47,8,216,208,146,178,107>>]).
+basic_tree_test_() ->
+ TestVectorTree = #leaf{hash = hd(?TEST_VECTOR_TREES)},
+ [?_assertEqual(#head{version = 0, tree = undefined},
+ create()),
+ ?_assertEqual(#head{version = 1, tree = TestVectorTree},
+ create("a foo is a bar"))].
+
+%% Test vectors from certificate-transparency/src/python/ct/crypto/merkle_test.py.
-define(EMPTY_HASH, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855").
-define(TEST_VECTOR_LEAVES, ["", "\x00", "\x10", " !", "01", "@ABC", "PQRSTUVW", "`abcdefghijklmno"]).
-define(TEST_VECTOR_HASHES, ["6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d",
@@ -169,8 +186,8 @@ rightbranchdepth(Tree, Acc) ->
"ddb89be403809e325750d3d263cd78929c2942b7942a34b77e122c9594a74c8c",
"5dc9da79a70659a9ad559cb701ded9a2ab9d823aad2f4960cfe370eff4604328"]).
-empty_hash_test() ->
- ?assertEqual(hex:hexstr_to_bin(?EMPTY_HASH), mth([])).
+empty_hash_test_() ->
+ [?_assertEqual(hex:hexstr_to_bin(?EMPTY_HASH), mth([]))].
mth_test() ->
lists:foreach(
@@ -226,6 +243,10 @@ mth(L) ->
hashfun([<<"\x01">>, mth(L1), mth(L2)])
end.
+-spec create(iolist() | binary()) -> head().
+create(D) ->
+ mkhead(1, mkleaf(D)).
+
-spec mkhead(list()) -> head().
mkhead([]) ->
mkhead(1, mkleaf([]));