Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

enforce checkpoints #4471

Merged
merged 2 commits into from Jul 5, 2018
Merged

enforce checkpoints #4471

merged 2 commits into from Jul 5, 2018

Conversation

arhag
Copy link
Contributor

@arhag arhag commented Jul 3, 2018

Resolves #3337.

Added a new signal pre_accepted_block (as well as the corresponding channel which is not currently used) which is emitted from controller_impl::push_block prior to any stateful changes occurring in that function.

Added a new class of exceptions that inherit from controller_emit_signal_exception. This class of exceptions is allowed to bubble out of the emit call in controller. Anytime an exception which derives from controller_emit_signal_exception is used, we need to be careful that it being thrown does not cause the originating emit call to leave controller in an inconsistent state.

The new checkpoint_exception which does inherit from controller_emit_signal_exception is only potentially thrown from the synchronous signal handler of pre_accepted_block within chain_plugin. Therefore, it can only currently bubble up from the emit call of the pre_accepted_block signal in controller_impl::push_block which is safe since it does not leave things in an inconsistent state. All it does if thrown is reject the block that was trying to be pushed.

This new exception is thrown if there is a mismatch between the block ID of the pushed block and the checkpoint block ID that may be set for that block number. The code to load this checkpoint information from the config or CLI options was already implemented prior to this PR.

@arhag arhag added this to the Version 1.1 milestone Jul 3, 2018
@arhag arhag requested a review from wanderingbort July 3, 2018 18:54
auto itr = my->loaded_checkpoints.find( blk->block_num() );
if( itr != my->loaded_checkpoints.end() ) {
auto id = blk->id();
EOS_ASSERT( itr->second == id, checkpoint_exception,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future it would probably be convenient to allow for short-ids however, since we encode the block number in the ID we would need a canonical short ID that is reasonably collision resistant first.

@wanderingbort wanderingbort merged commit 40ad609 into release/1.1 Jul 5, 2018
@wanderingbort wanderingbort deleted the 3337-checkpoints branch July 5, 2018 19:39
@arhag arhag mentioned this pull request Jul 5, 2018
@arhag
Copy link
Contributor Author

arhag commented Jul 16, 2018

Checkpoints

The DPOS consensus mechanism is designed to prevent two incompatible blocks from becoming final (irreversible) under the assumption of non-byzantine behavior by more than two-thirds of active block producers. Producers are economically disincentivized from byzantine behavior due to the fact that it is trivial to cryptographically prove byzantine behavior (double signing incompatible blocks) and because the likely consequences to a maliciously byzantine block producer is, at a start, loss of future block producer income.

However, in the unlikely case that sufficient byzantine behavior occurs anyway, it is possible for there to a be fork in the blockchain that tricks newly synchronizing nodes to follow the blockchain down the "wrong branch" and get stuck there. In such scenarios, it is necessary for the node operators to manually intervene using trusted checkpoints they obtain from their social network in order to get back into consensus with the rest of the network. These checkpoints should be specified by the node operators and enforced by nodeos without any source code changes necessary.

Specifying checkpoints

Checkpoints can be specified in the config.ini file or via the command line in the form of [BLOCK_NUM, BLOCK_ID] where BLOCK_NUM is a positive integer specifying the block number and BLOCK_ID is a string containing the hex encoding of the expected block ID.

Here is an example of two checkpoints on blocks 8 and 20 specified in the config.ini file:

# Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints. (eosio::chain_plugin)
checkpoint = [8, "00000008932161ed022c329eaec93a1922e43afb175c458db88776c0718240ea"]
checkpoint = [20, "00000014bd0765ff8c828abb91a021a04525ab344bffdc6165c65000e7bc7823"]

Checkpoint enforcement

Checkpoints are enforced during the operation of pushing a block onto the chain. This operation occurs as new blocks are considered during synchronization and normal operation of nodeos in which the blocks are discovered from the p2p network. The operation also occurs in the scenario in which blocks are explicitly pushed to nodeos via the chain API and the scenario in which the blocks are reapplied during replay. If a checkpoint that must be considered for a particular pushed block does not match the expected block ID, the block will be rejected by the push block operation by throwing a checkpoint_exception which should log a message that looks something like:

2018-07-12T14:24:19.308 thread-0   controller.cpp:902            push_block           ] 3140001 checkpoint_exception: Block does not match checkpoint
Checkpoint does not match for block number 9: expected: 00000009bc70d1043c9f16c09738a51f440a029e24d0c80ef2a355e302fef169 actual: 00000009ddf25ea9e864af55519961a021e4560d06df8c62456c34fa04d2d012
    {"num":9,"expected":"00000009bc70d1043c9f16c09738a51f440a029e24d0c80ef2a355e302fef169","actual":"00000009ddf25ea9e864af55519961a021e4560d06df8c62456c34fa04d2d012"}
    thread-0  chain_plugin.cpp:516 operator()

@arhag arhag mentioned this pull request Jul 16, 2018
14 tasks
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants