Skip to content

Commit

Permalink
Add support for solution rebasing
Browse files Browse the repository at this point in the history
  • Loading branch information
Lev Berman committed Apr 25, 2024
1 parent 18b3d33 commit 0f08048
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 50 deletions.
65 changes: 50 additions & 15 deletions apps/arweave/src/ar_block_cache.erl
Expand Up @@ -7,7 +7,8 @@
mark_tip/2, get/2, get_earliest_not_validated_from_longest_chain/1,
get_longest_chain_cache/1,
get_block_and_status/2, remove/2, get_checkpoint_block/1, prune/2,
get_by_solution_hash/5, is_known_solution_hash/2]).
get_by_solution_hash/5, is_known_solution_hash/2,
get_siblings/2, get_fork_blocks/2]).

-include_lib("arweave/include/ar.hrl").
-include_lib("eunit/include/eunit.hrl").
Expand All @@ -21,10 +22,6 @@
%% validated: block is validated but does not belong to the tip fork
%% not_validated: block is not validated yet
%% none: null status
-type validation_status() :: on_chain | validated | not_validated | none.
-type nonce_limiter_validation_status() :: nonce_limiter_validated |
nonce_limiter_validation_scheduled | awaiting_nonce_limiter_validation.
-type block_status() :: validation_status() | {not_validated, nonce_limiter_validation_status()}.

%% @doc ETS table: block_cache
%% {block, BlockHash} => {#block{}, block_status(), Timestamp, set(Children)}
Expand Down Expand Up @@ -323,8 +320,8 @@ get_block_and_status(Tab, H) ->
case ets:lookup(Tab, {block, H}) of
[] ->
not_found;
[{_, {B, Status, _Timestamp, _Children}}] ->
{B, Status}
[{_, {B, Status, Timestamp, _Children}}] ->
{B, {Status, Timestamp}}
end.

%% @doc Mark the given block as the tip block. Mark the previous blocks as on-chain.
Expand Down Expand Up @@ -449,6 +446,33 @@ get_by_solution_hash(Tab, SolutionH, H, CDiff, PrevCDiff, [H2 | L], _B) ->
get_by_solution_hash(Tab, SolutionH, H, CDiff, PrevCDiff, L, B2)
end.

%% @doc Return the list of siblings of the given block, if any.
get_siblings(Tab, B) ->
H = B#block.indep_hash,
PrevH = B#block.previous_block,
case ets:lookup(Tab, {block, PrevH}) of
[] ->
[];
[{_, {_B, _Status, _CurrentTimestamp, Children}}] ->
sets:fold(
fun(SibH, Acc) ->
case SibH of
H ->
Acc;
_ ->
case ets:lookup(Tab, {block, SibH}) of
[] ->
Acc;
[{_, {Sib, _, _, _}}] ->
[Sib | Acc]
end
end
end,
[],
Children
)
end.

%%%===================================================================
%%% Private functions.
%%%===================================================================
Expand Down Expand Up @@ -1030,6 +1054,7 @@ block_cache_test() ->
assert_tip(block_id(B1)),
assert_max_cdiff({0, block_id(B1)}),
assert_is_valid_fork(true, on_chain, B1),
?assertEqual([], get_siblings(bcache_test, B1)),

%% Re-adding B1 shouldn't change anything - i.e. nothing should be updated because the
%% block is already on chain
Expand Down Expand Up @@ -1073,6 +1098,7 @@ block_cache_test() ->
assert_max_cdiff({1, block_id(B2)}),
assert_is_valid_fork(true, on_chain, B1),
assert_is_valid_fork(true, not_validated, B2),
?assertEqual([], get_siblings(bcache_test, B2)),

%% Add a TXID to B2, but still don't mark as validated
%%
Expand Down Expand Up @@ -1130,6 +1156,8 @@ block_cache_test() ->
assert_is_valid_fork(true, on_chain, B1),
assert_is_valid_fork(true, not_validated, B2),
assert_is_valid_fork(true, not_validated, B1_2),
?assertEqual([B2], get_siblings(bcache_test, B1_2)),
?assertEqual([B1_2], get_siblings(bcache_test, B2)),

%% Even though B2 is marked as a tip, it is still lower difficulty than B1_2 so will
%% not be included in the longest chain
Expand Down Expand Up @@ -1260,7 +1288,8 @@ block_cache_test() ->
%% \ /
%% 0 B1/on_chain
add_validated(bcache_test, B2_2),
?assertEqual({B2_2, validated}, get_block_and_status(bcache_test, B2_2#block.indep_hash)),
?assertMatch({B2_2, {validated, _}},
get_block_and_status(bcache_test, B2_2#block.indep_hash)),
?assertMatch({B2_3, [B2_2, B2], {{not_validated, ExpectedStatus}, _}},
get_earliest_not_validated_from_longest_chain(bcache_test)),
assert_longest_chain([B2_2, B2, B1], 1),
Expand Down Expand Up @@ -1298,6 +1327,9 @@ block_cache_test() ->
assert_is_valid_fork(true, validated, B2_2),
assert_is_valid_fork(true, not_validated, B2_3),
assert_is_valid_fork(true, on_chain, B3),
?assertEqual([B2_2], get_siblings(bcache_test, B3)),
?assertEqual([B3], get_siblings(bcache_test, B2_2)),
?assertEqual([B2], get_siblings(bcache_test, B1_2)),

%% B3->B2->B1 fork is still heaviest
%%
Expand Down Expand Up @@ -1363,6 +1395,8 @@ block_cache_test() ->
assert_is_valid_fork(true, not_validated, B2_3),
assert_is_valid_fork(true, validated, B3),
assert_is_valid_fork(true, not_validated, B4),
?assertEqual([], get_siblings(bcache_test, B2_3)),
?assertEqual([], get_siblings(bcache_test, B4)),

%% Height Block/Status
%%
Expand Down Expand Up @@ -1513,8 +1547,9 @@ block_cache_test() ->
mark_nonce_limiter_validated(bcache_test, crypto:strong_rand_bytes(48)),
mark_nonce_limiter_validation_scheduled(bcache_test, block_id(B13)),
mark_nonce_limiter_validated(bcache_test, block_id(B13)),
?assertEqual({B13, on_chain}, get_block_and_status(bcache_test, block_id(B13))),
?assertMatch({B14, {not_validated, awaiting_nonce_limiter_validation}},
?assertMatch({B13, {on_chain, _}},
get_block_and_status(bcache_test, block_id(B13))),
?assertMatch({B14, {{not_validated, awaiting_nonce_limiter_validation}, _}},
get_block_and_status(bcache_test, block_id(B14))),
assert_longest_chain([B13, B11], 0),
assert_tip(block_id(B13)),
Expand All @@ -1532,7 +1567,7 @@ block_cache_test() ->
%% \ /
%% 0 B11/on_chain
mark_nonce_limiter_validation_scheduled(bcache_test, block_id(B14)),
?assertMatch({B14, {not_validated, nonce_limiter_validation_scheduled}},
?assertMatch({B14, {{not_validated, nonce_limiter_validation_scheduled}, _}},
get_block_and_status(bcache_test, block_id(B14))),
?assertMatch({B14, [B13], {{not_validated, nonce_limiter_validation_scheduled}, _}},
get_earliest_not_validated_from_longest_chain(bcache_test)),
Expand All @@ -1552,7 +1587,7 @@ block_cache_test() ->
%% \ /
%% 0 B11/on_chain
mark_nonce_limiter_validated(bcache_test, block_id(B14)),
?assertMatch({B14, {not_validated, nonce_limiter_validated}},
?assertMatch({B14, {{not_validated, nonce_limiter_validated}, _}},
get_block_and_status(bcache_test, block_id(B14))),
?assertMatch({B14, [B13], {{not_validated, nonce_limiter_validated}, _}},
get_earliest_not_validated_from_longest_chain(bcache_test)),
Expand Down Expand Up @@ -1598,7 +1633,7 @@ block_cache_test() ->
add_validated(bcache_test, B14),
?assertMatch({B15, [B14, B13], {{not_validated, awaiting_nonce_limiter_validation}, _}},
get_earliest_not_validated_from_longest_chain(bcache_test)),
?assertMatch({B14, validated}, get_block_and_status(bcache_test, block_id(B14))),
?assertMatch({B14, {validated, _}}, get_block_and_status(bcache_test, block_id(B14))),
assert_longest_chain([B14, B13, B11], 1),
assert_tip(block_id(B13)),
assert_max_cdiff({3, block_id(B15)}),
Expand Down Expand Up @@ -1647,7 +1682,7 @@ block_cache_test() ->
mark_nonce_limiter_validated(bcache_test, block_id(B16)),
?assertMatch({B15, [B14, B13], {{not_validated, awaiting_nonce_limiter_validation}, _}},
get_earliest_not_validated_from_longest_chain(bcache_test)),
?assertMatch({B16, {not_validated, nonce_limiter_validated}},
?assertMatch({B16, {{not_validated, nonce_limiter_validated}, _}},
get_block_and_status(bcache_test, block_id(B16))),
assert_longest_chain([B14, B13, B11], 1),
assert_tip(block_id(B13)),
Expand All @@ -1671,7 +1706,7 @@ block_cache_test() ->
%% \ /
%% 0 B11/on_chain
mark_tip(bcache_test, block_id(B14)),
?assertEqual({B14, on_chain}, get_block_and_status(bcache_test, block_id(B14))),
?assertMatch({B14, {on_chain, _}}, get_block_and_status(bcache_test, block_id(B14))),
?assertMatch({B15, [B14], {{not_validated, awaiting_nonce_limiter_validation}, _}},
get_earliest_not_validated_from_longest_chain(bcache_test)),
assert_longest_chain([B14, B13, B11], 0),
Expand Down
4 changes: 2 additions & 2 deletions apps/arweave/src/ar_http_iface_middleware.erl
Expand Up @@ -838,7 +838,7 @@ handle(<<"GET">>, [<<"reward_history">>, EncodedBH], Req, _Pid) ->
{ok, BH} ->
Fork_2_6 = ar_fork:height_2_6(),
case ar_block_cache:get_block_and_status(block_cache, BH) of
{#block{ height = Height, reward_history = RewardHistory }, Status}
{#block{ height = Height, reward_history = RewardHistory }, {Status, _}}
when (Status == on_chain orelse Status == validated),
Height >= Fork_2_6 ->
{200, #{}, ar_serialize:reward_history_to_binary(RewardHistory),
Expand All @@ -861,7 +861,7 @@ handle(<<"GET">>, [<<"block_time_history">>, EncodedBH], Req, _Pid) ->
Fork_2_7 = ar_fork:height_2_7(),
case ar_block_cache:get_block_and_status(block_cache, BH) of
{#block{ height = Height,
block_time_history = BlockTimeHistory }, Status}
block_time_history = BlockTimeHistory }, {Status, _}}
when (Status == on_chain orelse Status == validated),
Height >= Fork_2_7 ->
{200, #{}, ar_serialize:block_time_history_to_binary(
Expand Down
2 changes: 1 addition & 1 deletion apps/arweave/src/ar_node.erl
Expand Up @@ -240,7 +240,7 @@ get_partition_upper_bound(BI) ->

get_recent_partition_upper_bound_by_prev_h(H, Diff) ->
case ar_block_cache:get_block_and_status(block_cache, H) of
{_B, on_chain} ->
{_B, {on_chain, _}} ->
[{_, BI}] = ets:lookup(node_state, recent_block_index),
Genesis = length(BI) =< ?SEARCH_SPACE_UPPER_BOUND_DEPTH,
get_recent_partition_upper_bound_by_prev_h(H, Diff, BI, Genesis);
Expand Down

0 comments on commit 0f08048

Please sign in to comment.