Analysis of Ethereum POA consensus algorithm

1. Concepts and definitions in clique

  • EPOCH_LENGTH  : The length of epoch is 30000 blocks. Every time you enter a new epoch, the previous votes are cleared and the recording starts again. The vote here refers to adding or removing the signer.
  • BLOCK_PERIOD  : block generation time, default is 15s
  • UNCLE_HASH  : always Keccak256(RLP([])) , because there is no uncle
  • SIGNER_COUNT  : Each block has a number of signers
  • SIGNER_LIMIT  : equal to (SIGNER_COUNT / 2) + 1. Each singer can only sign 1 of the consecutive SIGNER_LIMIT blocks.
    • For example, there are 5 signers: ABCDE, signing 4 blocks, and the signer is not allowed to be ABAC, because A signed 2 times in 3 consecutive blocks.
  • NONCE_AUTH  : Indicates that the voting type is to add a new signer; value = 0xffffffffffffffff
  • NONCE_DROP  : Indicates that the voting type is to kick out the old signer; value = 0x0000000000000000
  • EXTRA_VANITY  : Represents the reserved field length in the Extra field in the block header: 32 bytes
  • EXTRA_SEAL  : Represents the length of the signature data stored in the Extra field in the block header: 65 bytes
  • IN-TURN/OUT-OF-TURN  : Each block has an in-turn signer, and other signers are out-of-turn. The weight of the in-turn signer is greater, and the block generation time will be faster. In this way It can be guaranteed that a block of this height has a high probability of being mined by an in-turn signer.
  • Extra fields in the genesis block include:
    • 32-byte prefix (extraVanity)
    • The addresses of all signers
    • 65-byte suffix (extraSeal): saves the signer’s signature
  • The Extra fields of other blocks only include extraVanity and extraSeal
  • The Time field indicates the time interval for generating blocks: blockPeriod(15s)
  • The Nonce field indicates a vote: add (nonceAuthVote: 0xffffffffffffffff) or remove (nonceDropVote: 0x00000000000000000) a signer
  • The Coinbase field stores  the voted  address
    • For example: a vote by signerA: join signerB, then Coinbase stores B’s address
  • Difficulty field value: 1-is  a signer of this block  (in turn), 2-  is not a signer of this block  (out of turn)

2. Characteristics of PoA

  • PoA relies on preset authorized nodes (signers) and is responsible for generating blocks. Ordinary nodes cannot mine and do not have the right to generate blocks.
  • New signers can be elected by authorized signers (more than 50% of votes cast).
  • Even if there is a malicious signer, he can only attack at most 1 of the consecutive blocks (the number is (SIGNER_COUNT / 2) + 1). During this period, other signers can vote to kick out the malicious signer.
  • You can specify the time when the block is generated.
  • No mining rewards

3. PoA workflow and interface

  1. Specify a set of initial authorized signers in the genesis block, and all addresses are stored in the Extra field of the genesis block
  2. After starting mining, the group of signers begins to sign and broadcast the generated blocks .
  3. The signature result  is stored in the Extra field of the block header
  4. Update the addresses of all signers currently highly authorized in Extra  because there are newly added or kicked out signers.
  5. There is one signer at each height in the IN-TURN state, and other signers are in the OUT-OF-TURN state. The block signed by the IN-TURN signer will be broadcast immediately, and the block signed by the OUT  -OF-TURN signer will be delayed  a little randomly. The time will be broadcast again to ensure that the IN-TURN signature block has a higher priority and is uploaded to the chain.
  6. If a new signer needs to be added, the signer initiates a proposal through the API interface, and the proposal is broadcast to other nodes by reusing the Coinbase (new signer address) and Nonce ("0xffffffffffffffff")  fields in the block header. All authorized signers will respond to the new signer. Signers vote to "join". If the yes votes exceed 50% of the total number of signers, they agree to join.
  7. If an old signer needs to be kicked out, all authorized signers will vote to "kick out" the old signer. If the yes vote exceeds 50% of the total number of signers, it means that the old signer is kicked out.

signer signs the block header

  1. The length of Extra is at least 65 bytes (the signature result is 65 bytes, that is, R, S, V, and V are 0 or 1)
  2. RLP encode  all fields in blockHeader except the last 65 bytes of Extra
  3. Keccak256 hash the encoded data 
  4. The signed data (65 bytes) is saved to  the last 65 bytes of Extra

clique.go implements all interfaces in consensus to implement the POA algorithm.

Clique.Prepare(chain , header)

Prepare is one of the consensus engine interfaces. This function configures consensus-related parameters in the header (Cionbase, Difficulty, Extra, MixDigest, Time)

  • For non-epoch blocks (number % Epoch != 0):
  1. Get the voting data in Clique.proposals (for example: A joins C, B kicks out D)
  2. Analyze whether the number of votes is valid based on the signers of the snapshot (for example: C was not in the signers originally, and the vote to add is valid; D was originally in the signers, and the vote to remove is valid)
  3. From the voted address list (C, D),  randomly select an address  as the Coinbase of the header, and set the Nonce to join (0xffffffffffffffff) or kick out (0x0000000000000000)
  4. If Clique.signer is the signer of this round (in-turn), set header.Difficulty = diffInTurn(1), otherwise it is diffNoTurn(2)
  5. Configure the data of header.Extra as [extraVanity + all signers in snap + extraSeal]
  6. MixDigest needs to be configured as nil
  7. Configure timestamp: Time is the time of the parent block + 15s
  8. Fill the Coinbase, Nonce, Difficulty, Extra of the header object (reserved suffix, used for Seal() to store the signature of the current block signer)

Initialization of consensus engine clique

In Ethereum.StartMining, if Ethereum.engine is configured as clique.Clique, configure the  signer of clique according to the miner address of the current node (default is accounts[0])  : clique.Authorize(eb, wallet.SignHash), where  the signature The function  is SignHash, which signs the given hash.

Clique.snapshot(chain,number,hash,parents)

Snapshot retrieves authorized snapshots within a specified time, first encapsulates them in memory and disk, and looks for snapshots;

If it is in the genesis block, create a new snapshot;

If there is no snapshot of the block header, the collection block is moved backward. If there is a clear parent, it is forced to the parent. If there is no clear parent, it is found in the database.

After finding the snapshot, move the second half of all headers forward

Then generate a new snapshot through the block header, save the hash of the current block to the latest snapshot, and save the generated snapshot to the disk.

The votes application stores the number of votes

Snapshot.apply(headers)

Create a new snapshot of authorized signers and update the proposals in the block header starting from the previous snapshot to the latest snapshot

  1. Perform integrity check on the input parameter headers: Because multiple block headers may be passed in,  the block numbers must be consecutive.
  2. Traverse all headers. If the block number happens to be at the beginning of epoch (number%Epoch == 0), reset Votes and Tally in the snapshot (  discard all historical data  )
  3. For each header, get  the signer from the signature
  4. If the signer is in snap.Recents, it means that  there has been a signature recently  , and no signature is allowed again, and empty will be returned.
  5. Record  that the signer is the signer of the block: snap.Recents[number] = signer
  6. Count the number of votes for header.Coinbase, if it  exceeds 50% of the total number of signers
  7. Perform add or remove operations
  8. Delete a signer record in snap.Recents: key=number- (uint64(len(snap.Signers)/2 + 1)), which means the signer is released and the block can be signed next time
  9. Clear votes from Coinbase that were removed
  10. Remove all voting records for this Conibase in snap.Votes
  11. Remove all vote records for this Conibase in snap.Tally

 Clique.Seal(chain, block , stop)

Seal is also one of the consensus engine interfaces. This function uses clique.signer to sign the block. To achieve consensus, the engine tries to create a sealed block and generate a signature into the suffix of header.Extra

A block that has called Finalize() can be authorized or sealed. When successful, all members of the block returned are complete and can be regarded as a normal block. It can be broadcast to the entire network or inserted into the blockchain, etc.

  • Sealed genesis blocks are not supported
  • If the signer is not among the signers of the snapshot, the block is not allowed to be signed.
  • If the signer is not the signer of this block, he needs to delay for a random period of time before signing. If he is the signer of this block, he will sign directly.
  • If the current signer is among the 'recent signers', wait for the next epoch
  • Does not support 0-period chains, does not support empty block sealing, no rewards but can be sealed
  • Use copy to replace the signature result and save it in the 65 bytes of extraSeal in the Extra field of the block header.

Clique.VerifySeal(chain, header)

VerifySeal is also one of the consensus engine interfaces.

  1. Recover the account address from the signature of the header. The address change requirement is in the signers of the snapshot.
  2. Check if the calculated difficulty in the block header matches (in turn or out of turn)

Clique.Finalize

Finalize is also one of the consensus engine interfaces. This function generates a block and "finalizes" the new block. There is no uncle block processing and no reward mechanism.

  1. header.Root: the state root remains intact
  2. header.UncleHash: is nil
  3. types.NewBlock(header, txs, nil, receipts): Encapsulate and return the final block

API.Propose(addr, auth)

Add a proposal: the caller's vote for addr, auth indicates whether to join or kick out

verifyUncles(chain,block)

Determine whether the uncle block in the block is greater than zero, since uncle blocks are not allowed in this consensus

ecercover(header,sigcache)

Extract Ethereum account address from signature header

CalcDifficulty(chain,time,parent)

Returns the computational difficulty of the block. The computational difficulty is the difficulty adjustment algorithm.

Difficulty field value: 1-is  a signer of this block  (in turn), 2-  is not a signer of this block  (out of turn)

voting strategy

Because the blockchain may undergo small reorgs, conventional voting mechanisms (cast-and-forget) may not be optimal because the block containing a single vote may not be on the final chain due to The latest block has been discarded.

A simple but effective way is to configure "proposal" for signers. For example, "add 0x...", "drop 0x...", when there are multiple concurrent proposals, the signature code "randomly" selects a proposal. Injected into the block signed by the signer, so that multiple concurrent proposals and reorgs can be saved on the chain.

The list may expire after a certain number of blocks/epochs. Passing the proposal does not mean it will not be recalled, so it should not be discarded immediately when the proposal is passed.

  • Votes for adding and removing new signers are effective immediately and will participate in the next vote count.
  • Both joining and kicking out require more than 50% of the current total number of  signers to vote.
  • You can kick yourself out (it also requires more than 50% of the votes)
  • Parallel voting is possible (A and B cross-vote C and D), as long as the final number of votes is 50%
  • If a signer's vote is not passed before entering a new epoch, the votes of other signers for that signer will also be counted in the judgment.
  • Entering a new epoch, all previous pending votes will be invalidated, and counting of votes will begin again.
  • Repeated and continuous voting is not allowed (due to the restriction of recent signers, within SIGNER_COUNT / 2 + 1 blocks, the signer can only sign one block)

Examples of voting scenarios

  • ABCD and AB first kick out CD, and C kicks out D. The result is that ABC is left
  • ABCD, AB first kicked out CD, C kicked out D, and B voted for C's remaining vote. The result is that ABC is left
  • ABCD, AB first kicked out CD, and C kicked out D. Even if C voted for himself, AB would be left.
  • ABCDE, ABC first join F (success, ABCDEF), BCDE kick out F (success, ABCDE), DE join F (failure, ABCDE), BCD kick out A (success, BCDE), B join F (due to DE joining F It still exists. At this time, BDE joins F and meets more than 50% of the votes), and the result is that BCDEF is left

4. Attack and defense in PoA

  • Malicious signer. A malicious user is added to the signer list, or the signer key/machine is compromised. The solution is a list of N authorized signers, and any signer can only sign for each K 1 of the block signatures. This minimizes the damage and the remaining miners can vote to kick out the malicious user.
  • Censoring signer. If a signer (or a group of signers) attempts to check the proposals of other signers in the block (especially voting to kick them), to solve this problem, we will increase the allowable mining of the signer. The frequency is limited to 1/(N/2). If he doesn't want to be kicked out, he must control more than 50% of the signers.
  • "Spamming" signers. These signers inject a new voting proposal into every block they sign. Since nodes need to count all votes to create a list of authorized signers, over time this will generate a lot of useless spam. voting, causing the system to run slower. Through the epoch mechanism, old votes will be discarded every time a new epoch is entered.
  • Concurrent blocks. If the number of authorized signers is N and we allow each signer to sign 1/K, then at least NK signers can successfully sign a block at any time. In order to avoid these block competitions ( forks  ), each signer will add a random delay when generating a new block. This ensures that split ends are difficult to occur.

Guess you like

Origin blog.csdn.net/m0_55345224/article/details/120881258