Skip to content

Commit

Permalink
Hyperchains app and initial db (#3338)
Browse files Browse the repository at this point in the history
* HC App

* Initial HC DB design
  • Loading branch information
gorbak25 committed Sep 4, 2020
1 parent dd24d03 commit 91847c3
Show file tree
Hide file tree
Showing 16 changed files with 426 additions and 3 deletions.
5 changes: 4 additions & 1 deletion Makefile
Expand Up @@ -104,6 +104,9 @@ export AEVM_EXTERNAL_TEST_VERSION=348b0633f4a6ee3c100368bf0f4fca71394b4d01
console: $(SWAGGER_ENDPOINTS_SPEC)
@$(REBAR) as local shell --config config/dev.config --sname aeternity@localhost

hyperchains-console: $(SWAGGER_ENDPOINTS_SPEC)
@$(REBAR) as local shell --config config/hyperchains-dev.config --sname aeternity@localhost

local-build: KIND=local
local-build: internal-build

Expand Down Expand Up @@ -445,7 +448,7 @@ test-arch-os-dependencies:
make ct-latest SUITE=apps/aecontract/test/aecontract GROUP=sophia TEST=sophia_crypto

.PHONY: \
all console \
all console hyperchains-console \
local-build local-start local-stop local-attach \
prod-build prod-start prod-stop prod-attach prod-package prod-compile-deps \
multi-build multi-start multi-stop multi-clean multi-distclean \
Expand Down
4 changes: 4 additions & 0 deletions apps/aecore/src/aec_fork_block_settings.erl
Expand Up @@ -16,6 +16,10 @@
, contracts_file_name/1
]).

-pluggable([genesis_accounts/0]).

-include("aec_plugin.hrl").

-define(GENESIS_DIR, ".genesis").
-define(MINERVA_DIR, ".minerva").
-define(FORTUNA_DIR, ".fortuna").
Expand Down
2 changes: 1 addition & 1 deletion apps/aecore/src/aec_plugin.erl
Expand Up @@ -22,7 +22,7 @@ get_module(_Tag, _Default) ->
-else.

tags() ->
[aec_headers].
[aec_headers, aec_fork_block_settings].

register(Map) when is_map(Map) ->
case valid_registry(Map) of
Expand Down
2 changes: 2 additions & 0 deletions apps/aecore/src/aecore.app.src
Expand Up @@ -45,6 +45,7 @@
aega,
aefate,
aeserialization,
aehyperchains,
ranch
]},
{env, [
Expand Down Expand Up @@ -79,6 +80,7 @@
{110, {aehttp_app, check_env, []}},
{110, {aec_hard_forks, ensure_env, []}},
{110, {aec_mining, check_env, []}},
{110, {aehc_app, check_env, []}},
{200, {aec_db, check_db, []}}
]}
]}
Expand Down
48 changes: 48 additions & 0 deletions apps/aehyperchains/src/aehc_app.erl
@@ -0,0 +1,48 @@
-module(aehc_app).
-behavior(application).

-export([
start/2
, start_phase/3
, prep_stop/1
, stop/1
, check_env/0
]).

start(_StartType, _StartArgs) ->
aehc_sup:start_link().

start_phase(_Phase, _StartType, _PhaseArgs) ->
ok.

prep_stop(_State) ->
ok.

stop(_State) ->
ok.

check_env() ->
check_env([{[<<"chain">>, <<"hyperchains">>, <<"enabled">>], {set_env, enabled}}]),
case aehc_utils:hc_enabled() of
true ->
lager:info("Hyperchains are enabled"),
aehc_utils:hc_install();
false ->
lager:info("Hyperchains are disabled"),
ok
end.

check_env(Spec) ->
lists:foreach(
fun({K, F}) ->
case aeu_env:user_config(K) of
undefined -> ignore;
{ok, V} -> set_env(F, V)
end
end, Spec).

set_env({set_env, K}, V) when is_atom(K) ->
io:fwrite("setenv K=~p, V=~p~n", [K, V]),
application:set_env(aehyperchains, K, V);
set_env(F, V) when is_function(F, 1) ->
F(V).
3 changes: 3 additions & 0 deletions apps/aehyperchains/src/aehc_commitment.erl
@@ -1,2 +1,5 @@
-module(aehc_commitment).

-export([]).

%%-record()
31 changes: 31 additions & 0 deletions apps/aehyperchains/src/aehc_db.erl
@@ -0,0 +1,31 @@
-module(aehc_db).
-export([ create_tables/1 %% (Mode) -> ok
, check_tables/1 %% (Acc) -> Acc1
]).

create_tables(Mode) ->
case aehc_utils:hc_enabled() of
true ->
AllSpecs = all_specs(Mode),
Specs = lists:flatten([proplists:lookup(Table, AllSpecs) || {missing_table, Table} <- check_tables([])]),
[{atomic, ok} = mnesia:create_table(Tab, Spec) || {Tab, Spec} <- Specs];
false ->
[]
end.

check_tables(Acc) ->
case aehc_utils:hc_enabled() of
true ->
lists:foldl(
fun(M, Acc1) ->
M:check_tables(Acc1)
end, Acc, modules());
false ->
Acc
end.

modules() ->
[].

all_specs(Mode) ->
lists:flatten([M:table_specs(Mode) || M <- modules()]).
6 changes: 6 additions & 0 deletions apps/aehyperchains/src/aehc_fork_block_settings.erl
@@ -0,0 +1,6 @@
-module(aehc_fork_block_settings).

-export([genesis_accounts/0]).

-spec genesis_accounts() -> list().
genesis_accounts() -> [].
23 changes: 23 additions & 0 deletions apps/aehyperchains/src/aehc_sup.erl
@@ -0,0 +1,23 @@
-module(aehc_sup).
-behaviour(supervisor).

-export([
start_link/0
, init/1
]).

-define(SERVER, ?MODULE).
-define(CHILD(Mod,N,Type), {Mod,{Mod,start_link,[]},permanent,N,Type,[Mod]}).

start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).

init([]) ->
Spec = case aehc_utils:hc_enabled() of
true ->
lager:info("Starting Hyperchains"),
[];
false ->
[]
end,
{ok, {{one_for_one, 5, 10}, Spec}}.
36 changes: 36 additions & 0 deletions apps/aehyperchains/src/aehc_utils.erl
@@ -0,0 +1,36 @@
-module(aehc_utils).
-export([ hc_enabled/0,
hc_install/0,
hc_uninstall/0
]).

-spec hc_enabled() -> boolean().
hc_enabled() ->
case application:get_env(aehyperchains, enabled) of
{ok, true} ->
true;
_ ->
false
end.

-spec hc_install() -> ok | no_return().
hc_install() ->
ensure_early_boot(),
aec_plugin:register(#{aec_fork_block_settings => aehc_fork_block_settings}),
ok.

-spec hc_uninstall() -> ok | no_return().
hc_uninstall() ->
ensure_early_boot(),
aec_plugin:register(#{}),
ok.

-spec ensure_early_boot() -> ok | no_return().
ensure_early_boot() ->
case proplists:is_defined(aecore, application:which_applications()) of
true ->
lager:error("Enabling/Disabling HC in a running node will cause data corruption!"),
erlang:error(forbidden);
false ->
ok
end.
22 changes: 22 additions & 0 deletions apps/aehyperchains/src/aehyperchains.app.src
@@ -0,0 +1,22 @@
%% -*- mode: erlang; erlang-indent-level: 4; indent-tabs-mode: nil -*-
{application, aehyperchains,
[{description, "Hybrid chain between PoW and PoS based on BitcoinNG"},
{vsn, {cmd, "cat ../../VERSION"}},
{registered, []},
{applications,
[kernel,
stdlib,
lager,
gproc,
sext
]},
{mod,{aehc_app,[]}},
{env,[
{'$aec_db_create_tables', {aehc_db, create_tables}}
, {'$aec_db_check_tables' , {aehc_db, check_tables}}
]},
{modules, []},
{maintainers, []},
{licenses, []},
{links, []}
]}.
Empty file removed apps/aehyperchains/test/.gitkeep
Empty file.
84 changes: 84 additions & 0 deletions apps/aehyperchains/test/aehc_db_tests.erl
@@ -0,0 +1,84 @@
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
%%%-------------------------------------------------------------------
%%% @copyright (C) 2020, Aeternity Anstalt
%%% @doc
%%% Tests for aehc_db
%%% @end
%%%-------------------------------------------------------------------
-module(aehc_db_tests).

-import(aec_test_utils, [running_apps/0, loaded_apps/0, restore_stopped_and_unloaded_apps/2]).

-include_lib("eunit/include/eunit.hrl").

hyperchains_unable_to_use_normal_db_test_() ->
{foreach,
fun() ->
InitialApps = {running_apps(), loaded_apps()},

% Mock the data dir
{ok, RootDir} = file:get_cwd(),
DataDir = filename:absname_join(RootDir, "data/aecore"),
meck:new(aeu_env, [passthrough]),
meck:expect(aeu_env, data_dir, fun(aecore) -> DataDir end),

meck:new(aec_db, [passthrough]),
meck:expect(aec_db, load_database, 0, ok),
meck:new(aecore_sup, [passthrough]),
meck:expect(aecore_sup, start_link, 0, {ok, pid}),
meck:new(aec_jobs_queues, [passthrough]),
meck:expect(aec_jobs_queues, start, 0, ok),
ok = lager:start(),

InitialApps
end,
fun({OldRunningApps, OldLoadedApps}) ->
meck:unload(aec_jobs_queues),
meck:unload(aecore_sup),
meck:unload(aec_db),
meck:unload(aeu_env),
ok = restore_stopped_and_unloaded_apps(OldRunningApps, OldLoadedApps)
end,
[{"HC Genesis block != Mainnet Genesis block",
fun() ->
% Get genesis hashes
{ok, MainnetGenesisHash} = aec_headers:hash_header(aec_block_genesis:genesis_header()),
aehc_utils:hc_install(),
{ok, HyperchainGenesisHash} = aec_headers:hash_header(aec_block_genesis:genesis_header()),
aehc_utils:hc_uninstall(),

% If enabling HC didn't change the genesis hash then something must be broken
?assertNotEqual(MainnetGenesisHash, HyperchainGenesisHash),

% No HC, No DB - starts normally
meck:expect(aec_db, get_genesis_hash, 0, undefined),
?assertEqual({ok, pid}, aecore_app:start(normal, [])),

% No HC, Mainnet DB present - starts normally
meck:expect(aec_db, get_genesis_hash, 0, MainnetGenesisHash),
?assertEqual({ok, pid}, aecore_app:start(normal, [])),

% No HC, Hyperchain DB present - fails to start
meck:expect(aec_db, get_genesis_hash, 0, HyperchainGenesisHash),
?assertEqual({error, inconsistent_database}, aecore_app:start(normal, [])),

% Enable hyperchains
aehc_utils:hc_install(),

% HC Enabled, No DB - starts normally
meck:expect(aec_db, get_genesis_hash, 0, undefined),
?assertEqual({ok, pid}, aecore_app:start(normal, [])),

% HC Enabled, Mainnet DB present - fails to start
meck:expect(aec_db, get_genesis_hash, 0, MainnetGenesisHash),
?assertEqual({error, inconsistent_database}, aecore_app:start(normal, [])),

% HC Enabled, Hyperchain DB present - starts normally
meck:expect(aec_db, get_genesis_hash, 0, HyperchainGenesisHash),
?assertEqual({ok, pid}, aecore_app:start(normal, [])),

% Disable hyperchains
aehc_utils:hc_install(),
ok
end}
]}.
38 changes: 38 additions & 0 deletions apps/aehyperchains/test/aehc_utils_tests.erl
@@ -0,0 +1,38 @@
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
%%%-------------------------------------------------------------------
%%% @copyright (C) 2020, Aeternity Anstalt
%%% @doc
%%% Tests for aehc_utils
%%% @end
%%%-------------------------------------------------------------------
-module(aehc_utils_tests).

-import(aec_test_utils, [running_apps/0, loaded_apps/0, restore_stopped_and_unloaded_apps/2]).

-include_lib("eunit/include/eunit.hrl").

install_uninstall_only_in_early_boot_test_() ->
{foreach,
fun() ->
InitialApps = {running_apps(), loaded_apps()},
ok = lager:start(),
meck:new(application, [unstick, passthrough]),
InitialApps
end,
fun({OldRunningApps, OldLoadedApps}) ->
meck:unload(application),
ok = restore_stopped_and_unloaded_apps(OldRunningApps, OldLoadedApps)
end,
[{"HC installs/uninstalls only in early boot",
fun() ->
meck:expect(application, which_applications, 0, []),
?assertEqual(ok, aehc_utils:hc_install()),
?assertEqual(ok, aehc_utils:hc_uninstall()),

meck:expect(application, which_applications, 0, [{aecore, "Blockchain for aeapps", 20}]),
?assertException(error, forbidden, aehc_utils:hc_install()),
?assertException(error, forbidden, aehc_utils:hc_uninstall()),

ok
end}
]}.

0 comments on commit 91847c3

Please sign in to comment.