Don’t treat LastCommitInfo like current commit info

parent ecdecddc
Pipeline #37449199 passed with stages
in 20 minutes and 13 seconds
......@@ -202,7 +202,8 @@ The following Merkle tree is formed when calculating an application hash:
* Accounts’ root hash.
* Other data hash:
* Protocol number.
* Timestamp of last block.
* Timestamp of previous block.
* Timestamp of current block.
* Timestamp of the last block in previous epoch.
* Epoch stage number (1-indexed).
* Short fee deposit.
......
......@@ -96,7 +96,10 @@
epoch_length=604800 :: epoch_length(),
epoch_stage=beginning :: ercoin_epoch:stage(),
height=0 :: block_height() | 0,
%% While we use one function to provide a default for the fields below, it doesn’t guarantee us to obtain the same value,
%% so it is better to ensure this in a constructor than to rely on default field values.
last_epoch_end=ercoin_timestamp:now() :: ercoin_timestamp:timestamp(),
previous_timestamp=ercoin_timestamp:now() :: ercoin_timestamp:timestamp(),
timestamp=ercoin_timestamp:now() :: ercoin_timestamp:timestamp(),
%% We provide the field below to allow deterministic time progress in tests.
now_fun=fun ercoin_timestamp:now/1 :: fun((data()) -> ercoin_timestamp:timestamp()),
......@@ -107,7 +110,6 @@
fresh_txs_hash= <<0:256>> :: hash(),
accounts=gb_merkle_trees:empty() :: gb_merkle_trees:tree(),
entropy_fun=fun ercoin_entropy:simple_entropy/1 :: fun ((data()) -> binary()),
%% After BeginBlock, these are validators for the next block.
validators=gb_merkle_trees:empty() :: gb_merkle_trees:tree(),
future_validators :: undefined | {promise, docile_rpc:key(), hash()} | gb_merkle_trees:tree(),
%% A copy of data used to maintain state for the purpose of checking transactions. It is important that checked transactions modify this state,
......
......@@ -109,7 +109,7 @@ handle_event(
last_commit_info=#'abci.LastCommitInfo'{votes=Votes},
header=#'abci.Header'{time=NewTimestampProto}},
gossiping,
Data=#data{timestamp=OldTimestamp}) ->
Data=#data{timestamp=OldTimestamp, previous_timestamp=OldPreviousTimestamp}) ->
NewTimestamp = ercoin_timestamp:from_protobuf(NewTimestampProto),
ValidatorsWithPresence =
[{ercoin_validators:key_value_by_tendermint_address(TendermintAddress, Data), Signed}
......@@ -117,7 +117,7 @@ handle_event(
NewData =
ercoin_epoch:progress(
ercoin_data:shift_to_timestamp(NewTimestamp, Data),
NewTimestamp - OldTimestamp,
OldTimestamp - OldPreviousTimestamp,
ValidatorsWithPresence),
{next_state, committing, NewData, {reply, From, #'abci.ResponseBeginBlock'{}}};
handle_event({call, From}, Query, gossiping, Data) when is_record(Query, 'abci.RequestQuery') ->
......
......@@ -27,21 +27,10 @@
-include_lib("include/ercoin.hrl").
-spec app_hash(data()) -> binary().
app_hash(
#data{
protocol=Protocol,
accounts=Accounts,
validators=Validators,
future_validators=FutureValidators,
fresh_txs_hash=FreshTxsHash,
fee_deposit_short=FeeDepositShort,
fee_deposit_long=FeeDepositLong,
epoch_stage=EpochStage,
last_epoch_end=LastEpochEnd,
timestamp=Timestamp}) ->
ValidatorsHash = gb_merkle_trees:root_hash(Validators),
app_hash(Data) ->
ValidatorsHash = gb_merkle_trees:root_hash(Data#data.validators),
AccountsHash =
case gb_merkle_trees:root_hash(Accounts) of
case gb_merkle_trees:root_hash(Data#data.accounts) of
undefined ->
%% This means that there are no accounts anymore, which is an uninteresting case, but implementing this eases testing.
<<0:256>>;
......@@ -49,25 +38,26 @@ app_hash(
AccountsHash2
end,
FutureValidatorsHash =
case FutureValidators of
case Data#data.future_validators of
undefined ->
<<0:256>>;
{promise, _, FutureValidatorsHash2} ->
FutureValidatorsHash2;
_ ->
FutureValidators ->
gb_merkle_trees:root_hash(FutureValidators)
end,
OtherDataHash =
?HASH(
<<Protocol,
Timestamp:4/unit:8,
LastEpochEnd:4/unit:8,
(ercoin_epoch:stage_no(EpochStage)),
FeeDepositShort:8/unit:8,
FeeDepositLong:8/unit:8,
<<(Data#data.protocol),
(Data#data.previous_timestamp):4/unit:8,
(Data#data.timestamp):4/unit:8,
(Data#data.last_epoch_end):4/unit:8,
(ercoin_epoch:stage_no(Data#data.epoch_stage)),
(Data#data.fee_deposit_short):8/unit:8,
(Data#data.fee_deposit_long):8/unit:8,
ValidatorsHash/binary,
FutureValidatorsHash/binary,
FreshTxsHash/binary>>),
(Data#data.fresh_txs_hash)/binary>>),
?HASH(<<AccountsHash/binary, OtherDataHash/binary>>).
-spec grant_fee_deposits(data()) -> data().
......@@ -106,9 +96,9 @@ grant_fee_deposits(
Data1#data{fee_deposit_short=0, fee_deposit_long=DepositLong - DisposedDepositLong}.
-spec shift_to_timestamp(ercoin_timestamp:timestamp(), data()) -> data().
shift_to_timestamp(Timestamp, Data=#data{epoch_length=EpochLength, fresh_txs=FreshTxs}) ->
NewFreshTxs = lists:dropwhile(fun ({TxTimestamp, _}) -> TxTimestamp < Timestamp - EpochLength end, FreshTxs),
remove_old_and_unlock_accounts(Data#data{timestamp=Timestamp, fresh_txs=NewFreshTxs}).
shift_to_timestamp(NewTimestamp, Data=#data{epoch_length=EpochLength, fresh_txs=FreshTxs, timestamp=OldTimestamp}) ->
NewFreshTxs = lists:dropwhile(fun ({TxTimestamp, _}) -> TxTimestamp < NewTimestamp - EpochLength end, FreshTxs),
remove_old_and_unlock_accounts(Data#data{timestamp=NewTimestamp, previous_timestamp=OldTimestamp, fresh_txs=NewFreshTxs}).
-spec now(data()) -> ercoin_timestamp:timestamp().
now(Data=#data{now_fun=F}) ->
......@@ -155,9 +145,12 @@ money_supply(#data{accounts=Accounts, fee_deposit_short=FeeDepositShort, fee_dep
-spec construct(map()) -> data().
%% @doc Construct data from provided option map.
%% Options are not mandatory and generally correspond to data fields, with special case of <code>accounts_opts</code> which needs to be a list of account construction options.
%% Options are not mandatory and generally correspond to data fields, with special case of <code>accounts_opts</code> which needs to be a list of account construction options. If timestamp fields are absent, they are set to current timestamp.
%% @see ercoin_account:construct/1
construct(Opts) ->
Now = ercoin_timestamp:now(),
TimestampFields =
maps:from_list([{Field, maps:get(Field, Opts, Now)} || Field <- [timestamp, previous_timestamp, last_epoch_end]]),
Data1 =
maps:fold(
fun (Key, Value, DataAcc) ->
......@@ -170,7 +163,7 @@ construct(Opts) ->
end
end,
#data{},
Opts),
maps:merge(Opts, TimestampFields)),
case gb_merkle_trees:size(Data1#data.validators) of
0 ->
Data1#data{validators=ercoin_validators:draw(Data1)};
......
......@@ -93,8 +93,8 @@ execute_actions([Action|ActionsTail], Data) ->
execute_actions(ActionsTail, apply(Action, [Data])).
-spec progress(data(), non_neg_integer(), list({{address(), binary()}, boolean()})) -> data().
progress(Data=#data{epoch_stage=Stage}, TimeSinceLastBlock, ValidatorsWithPresence) ->
Data1 = increase_absencies(Data, TimeSinceLastBlock, ValidatorsWithPresence),
progress(Data=#data{epoch_stage=Stage}, LastBlockDuration, ValidatorsWithPresence) ->
Data1 = increase_absencies(Data, LastBlockDuration, ValidatorsWithPresence),
case should_we_step(Data1) of
true ->
NewStage = next_stage(Stage),
......@@ -119,12 +119,14 @@ next_stage(_, []) ->
beginning.
-spec update_last_epoch_end(data()) -> data().
update_last_epoch_end(Data=#data{timestamp=Timestamp}) ->
Data#data{last_epoch_end=Timestamp}.
update_last_epoch_end(Data=#data{previous_timestamp=PreviousTimestamp}) ->
Data#data{last_epoch_end=PreviousTimestamp}.
-spec stage_actions(stage()) -> list(action()).
stage_actions(beginning) ->
[];
[fun ercoin_data:grant_fee_deposits/1,
fun update_last_epoch_end/1,
fun ercoin_validators:set_future_as_current/1];
stage_actions(drawing_frozen) ->
[fun ercoin_validators:begin_drawing/1];
stage_actions(drawing_yielded) ->
......@@ -132,10 +134,7 @@ stage_actions(drawing_yielded) ->
stage_actions(drawing_to_be_announced) ->
[];
stage_actions(drawing_announced) ->
[fun ercoin_data:grant_fee_deposits/1,
%% While we update last_epoch_end before the new epoch has already begun, we’ll use this field only later.
fun update_last_epoch_end/1,
fun ercoin_validators:set_future_as_current/1].
[].
-spec increase_absencies(data(), non_neg_integer(), list({{address(), binary()}, boolean()})) -> data().
increase_absencies(Data=#data{validators=Validators}, TimestampDiff, ValidatorsWithPresence) ->
......
......@@ -283,6 +283,7 @@ initial_data_without_votes(Accounts, BurningEnd, Beginning) ->
epoch_length=3600*24*7,
epoch_stage=beginning,
last_epoch_end=Beginning,
previous_timestamp=Beginning,
timestamp=Beginning,
accounts=Accounts,
entropy_fun=fun ercoin_entropy:reliable_entropy/1},
......
......@@ -203,12 +203,15 @@ prop_commit_responds_with_app_hash() ->
ResponseData =:= ercoin_data:app_hash(NewData)
end).
prop_begin_block_updates_timestamp() ->
prop_begin_block_updates_timestamps() ->
?FORALL(
{Data, BeginBlock},
ercoin_gen:data_with_begin_block(),
(apply_begin_block(BeginBlock, Data))#data.timestamp =:=
ercoin_timestamp:from_protobuf(BeginBlock#'abci.RequestBeginBlock'.header#'abci.Header'.time)).
begin
#data{timestamp=NewTimestamp, previous_timestamp=NewPreviousTimestamp} = apply_begin_block(BeginBlock, Data),
NewTimestamp =:= ercoin_timestamp:from_protobuf(BeginBlock#'abci.RequestBeginBlock'.header#'abci.Header'.time) andalso
NewPreviousTimestamp =:= Data#data.timestamp
end).
prop_query() ->
?FORALL(
......
......@@ -186,24 +186,27 @@ data_sks_1() ->
EpochLength,
EpochStage,
LastEpochEnd,
EpochDuration,
PreviousBlockTimeSinceEpoch,
CurrentBlockLength,
FreshTxsHash,
FeeDepositShort,
FeeDepositLong,
FreshTxsStub,
KeyPool},
{block_height(),
{oneof([block_height(), 0]),
epoch_length(),
epoch_stage(),
timestamp(),
pos_integer(),
non_neg_integer(),
non_neg_integer(),
hash(),
fee(),
fee(),
list({timestamp(), hash()}),
keypool()},
begin
Timestamp = LastEpochEnd + EpochDuration,
PreviousTimestamp = LastEpochEnd + PreviousBlockTimeSinceEpoch,
Timestamp = PreviousTimestamp + CurrentBlockLength,
?LET(
{AccountsList,
Validators,
......@@ -215,6 +218,7 @@ data_sks_1() ->
height=Height,
last_epoch_end=LastEpochEnd,
epoch_stage=EpochStage,
previous_timestamp=PreviousTimestamp,
timestamp=Timestamp,
epoch_length=EpochLength,
now_fun=fun dummy_now/1,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment