HackMD
    • Sharing Link copied
    • /edit
    • View mode
      • Edit mode
      • View mode
      • Book mode
      • Slide mode
      Edit mode View mode Book mode Slide mode
    • Note Permission
    • Read
      • Only me
      • Signed-in users
      • Everyone
      Only me Signed-in users Everyone
    • Write
      • Only me
      • Signed-in users
      • Everyone
      Only me Signed-in users Everyone
    • More (Comment, Invitee)
    • Publishing
    • Commenting Enable
      Disabled Forbidden Owners Signed-in users Everyone
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Invitee
    • No invitee
    • Options
    • Versions and GitHub Sync
    • Transfer ownership
    • Delete this note
    • Template
    • Save as template
    • Insert from template
    • Export
    • Google Drive Export to Google Drive
    • Gist
    • Import
    • Google Drive Import from Google Drive
    • Gist
    • Clipboard
    • Download
    • Markdown
    • HTML
    • Raw HTML
Menu Sharing Help
Menu
Options
Versions and GitHub Sync Transfer ownership Delete this note
Export
Google Drive Export to Google Drive Gist
Import
Google Drive Import from Google Drive Gist Clipboard
Download
Markdown HTML Raw HTML
Back
Sharing
Sharing Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
More (Comment, Invitee)
Publishing
More (Comment, Invitee)
Commenting Enable
Disabled Forbidden Owners Signed-in users Everyone
Permission
Owners
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Invitee
No invitee
   owned this note    owned this note      
Published Linked with GitHub
Like BookmarkBookmarked
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
# Casper Spec & Implementation Guide ## Background In this proposed spec for stage 1 Casper, Ethereum will transition from pure proof of work to hybrid PoW/PoS. In this scheme, all of the proof of work mechanics will continue to exist albeit with a reduced block reward (0.6 ETH), but additional proof of stake mechanisms will be added. In particular, the fork choice rule (ie. the way that a client determines which chain is "the canonical chain") will be modified to take these mechanics into account. A "Casper contract" will be published at some address `CASPER_ADDR`, and this contract will include functionality that allows anyone to deposit their ether, specifying a piece of "validation code" (think of this as being kind of like a public key) that they will use to sign messages, and become a validator. Once a user is inducted into the active validator pool, they will be able to send messages to participate in the PoS consensus process. The "size" of a validator in the active validator pool refers to the amount of ether that they deposited. The purpose of the PoS consensus process is to "finalize" key blocks called "checkpoints". Every 50th block is a checkpoint. To finalize a checkpoint, `2/3rds` of validator deposits must vote on two _sequential_ checkpoints. This finalizes the first of the two checkpoints. For more information regarding the Casper consensus protocol see [the paper here](https://github.com/ethereum/research/blob/master/papers/casper-basics/casper_basics.pdf). ### Casper Resources - [Proof of Stake FAQ](https://github.com/ethereum/wiki/wiki/Proof-of-Stake-FAQ) - [Casper FFG paper](https://arxiv.org/abs/1710.09437) - [Jon Choi’s Casper 101](https://medium.com/@jonchoi/ethereum-casper-101-7a851a4f1eb0) - [Presentation by Karl Floersch](https://www.youtube.com/watch?v=ycF0WFHY5kc) ## Outline This document outlines the different components which make up the Casper implementation. These include the: - Validator workflow overview, - `simple_casper` contract, - Fork choice rule modification, - Epoch initialization logic, - Casper transaction gas refunds, - Block ordering, and - Simple validator voting logic. ## Validator Workflow Overview 1. [Create valcode:](#validation-code) - Deploy a new contract which is used to validate a validator's signature. 2. Submit Deposit: - Call `casper.deposit(validation_addr, withdrawal_addr)` passing in the validation code contract address from step (1) and your withdrawal address. 3. **[Once each Epoch]** [Submit new vote message:](#casper-vote-generation) - Wait to vote until the checkpoint is at least `EPOCH_LENGTH/4` blocks deep in the main chain. This ensures all validators vote on the same block. - Generate unsigned vote message based on your chain's current head. - Broadcast the unsigned vote transaction to the network. 4. Logout: - [Submit logout message.](#logout-message-generation) - Call `casper.logout(logout_msg)` passing in your newly generated logout message. 5. Withdraw: - Call `casper.withdraw(validator_index)` and your funds will be sent to your validator's withdrawal address specified in step (2). ## Validator States Validators are highly stateful. They must handle valcode creation, depositing, voting, and logging out. Each stage also requires waiting for transaction confirmations. Because of this complexity, a mapping of state to handler is used. The validator state mapping [implemented in pyethapp](https://github.com/karlfloersch/pyethapp/blob/dev_env/pyethapp/validator_service.py#L58-L67) is as follows: ``` uninitiated: self.check_status, waiting_for_valcode: self.check_valcode, waiting_for_login: self.check_status, voting: self.vote, waiting_for_log_out: self.vote_then_logout, waiting_for_withdrawable: self.check_withdrawable, waiting_for_withdrawn: self.check_withdrawn, logged_out: self.check_status ``` ### Validator State Transition Diagram ![ffg-validator](https://user-images.githubusercontent.com/706123/34855668-d2f55412-f6f5-11e7-8d83-370ffe65a9b8.png) Arrows are the logic followed upon receiving a new block while in a given state. For example, if the validator is in state `voting` and receives a new block whose height is `in_first_quarter_of_epoch`, then the validator follows the arrow to remain in the state `voting`. ## Validation Code Validators must deploy their own signature validation contract. This will be used to check the signatures attached to their votes. This validation code **must** be a pure function. This means no storage reads/writes, environment variable reads OR external calls (except to other contracts that have already been purity-verified, or to precompiles) allowed. For basic signature verification, [ecdsa signatures are currently being used](https://github.com/karlfloersch/pyethereum/blob/develop/ethereum/hybrid_casper/casper_utils.py#L75). The validation code for these ecdsa signatures can be found [here](https://github.com/ethereum/casper/blob/34503973abceed0f0267fe35e229a40e7a94270a/casper/contracts/sighash.se.py). The validation code contract is currently being deployed as a part of the `induct_validator()` function [found here](https://github.com/karlfloersch/pyethereum/blob/develop/ethereum/hybrid_casper/casper_utils.py#L85-L89): ``` def induct_validator(chain, casper, key, value): sender = utils.privtoaddr(key) valcode_addr = chain.tx(key, "", 0, mk_validation_code(sender)) # Create a new validation code contract based on the validator's Ethereum address assert utils.big_endian_to_int(chain.tx(key, purity_checker_address, 0, purity_translator.encode('submit', [valcode_addr]))) == 1 casper.deposit(valcode_addr, sender, value=value) # Submit deposit specifying the validation code contract address ``` ## Casper Vote Format A Casper vote is an RLP-encoded list with the elements: ``` [ validator_index: number, # Index of the validator sending this vote target_hash: bytes32, # Hash of the target checkpoint block for this vote target_epoch: number, # Epoch number of the target checkpoint source_epoch: number, # Epoch number of the source checkpoint signature # A signed hash of the first four elements in this list, RLP encoded. (ie. RLP([validator_index, target_hash, target_epoch, source_epoch]) ] ``` Casper vote messages are simply included in normal transactions sent to the Casper contract's `casper.vote(vote_msg)` function. ## Casper Vote Generation To generate a Casper vote which votes on your chain's current head, first get the vote message contents. To do this, using the Casper contract call: - `casper.validator_indexes(WITHDRAWAL_ADDRESS)` for the `validator_index` - `casper.recommended_target_hash()` for the `target_hash` - `casper.current_epoch()` for the `target_epoch` - `casper.recommended_source_epoch()` for the `source_epoch` Next, RLP encode all these elements. To compute your signature, compute the `sha3` hash of your vote's RLP encoded list, and sign the hash. Your signature must be valid when checked against your validator's `validation_code` contract. Finally, append your signature to the end of the vote message contents. This is [implemented in Pyethereum](https://github.com/karlfloersch/pyethereum/blob/develop/ethereum/hybrid_casper/casper_utils.py#L73-L77) as follows: ``` def mk_vote(validator_index, target_hash, target_epoch, source_epoch, key): sighash = utils.sha3(rlp.encode([validator_index, target_hash, target_epoch, source_epoch])) v, r, s = utils.ecdsa_raw_sign(sighash, key) sig = utils.encode_int32(v) + utils.encode_int32(r) + utils.encode_int32(s) return rlp.encode([validator_index, target_hash, target_epoch, source_epoch, sig]) ``` ## Logout Message Generation Like the Casper vote messages, a logout message is an RLP encoded list where the last element is the validator's signature. The elements of the unsigned list are the `validator_index` and `epoch` where epoch is the current epoch. A signature is generated in the same way it is done with votes above. This is [implemented in Pyethereum](https://github.com/karlfloersch/pyethereum/blob/develop/ethereum/hybrid_casper/casper_utils.py#L79-L83) as follows: ``` def mk_logout(validator_index, epoch, key): sighash = utils.sha3(rlp.encode([validator_index, epoch])) v, r, s = utils.ecdsa_raw_sign(sighash, key) sig = utils.encode_int32(v) + utils.encode_int32(r) + utils.encode_int32(s) return rlp.encode([validator_index, epoch, sig]) ``` ## Simple Casper Contract High Level Overview The Casper smart contract contains Casper's core logic. It is written in Viper & can be deployed to the blockchain like any other contract to `CASPER_ADDR`. Casper messages are then sent to the contract by calling `vote(vote_msg)` where `vote_msg` is a Casper vote messaged as outlined above. ### [[Contract Source]](https://github.com/ethereum/casper/blob/master/casper/contracts/simple_casper.v.py) #### `def __init__(epoch_length, withdrawal_delay, ...)` The Casper contract constructor takes in key settings. Most notably: - `epoch_length` - `withdrawal_delay` - `dynasty_logout_delay` - `min_deposit_size` - `base_interest_factor` - `base_penalty_factor` These settings cannot be changed after deployment. #### `def initialize_epoch(epoch)` Calculates the interest rate & penalty factor for this epoch based on the time since finality. Once a new epoch begins, this function is immediately called as the first transaction applied to the state. See [Epoch initialization](http://notes.eth.sg/GwTgLAZghgRgHGAtABmARgKaLFNzEIwjZgDsAxsBAMzIwbJpA===?both#epoch-initialization) for more details. #### `def deposit(validation_addr, withdrawal_addr)` Accepts deposits from prospective validators & adds them to the next validator set. #### `def logout(logout_msg)` Initiates validator logout. The validator must continue to validate for `dynasty_logout_delay` dynasties before entering the `withdrawal_delay` waiting period. #### `def withdraw(validator_index)` If the validator has waited for a period greater than `withdrawal_delay` epochs past their `end_dynasty`, then send them ETH equivalent to their deposit. #### `def vote(vote_msg)` Called once by each validator each epoch. The vote message contains the fields presented in [Casper Vote Format](http://notes.eth.sg/GwTgLAZghgRgHGAtABmARgKaLFNzEIwjZgDsAxsBAMzIwbJpA===?both#casper-vote-format). #### `def slash(vote_msg_1, vote_msg_2)` Can be called by anyone who detects a slashing condition violation. Sends 4% of slashed validator's funds to the caller as a finder's fee and burns the remaining 96%. ## Casper Fork Choice Rule The Casper fork choice rule is as follows: 1. Start with last finalized checkpoint. 2. From that point checkpoint, select the casper checkpoint with the highest justified epoch: `casper.last_justified_epoch()` 3. Starting from that checkpoint, choose the block with the highest PoW score as the new head Checkpoints are not considered finalized from a client perspective if the validators' total_deposits is less than `NON_REVERT_MIN_DEPOSIT`. This var is configurable clientside depending on local security requirements. The fork choice is roughly implemented with the following ([pyethereum implementation here](https://github.com/karlfloersch/pyethereum/blob/develop/ethereum/hybrid_casper/chain.py#L201-L249)): ```python def add_block(self, candidate_block): if (self.get_score(self.head) < self.get_score(candidate_block) and not self.switch_reverts_finalized_block(self.head, candidate_block)): self.set_head(candidate_block) def get_score(self, prestate, block): casper = tester.ABIContract(casper_abi, block) return casper.last_justified_epoch() * 10**40 + self.get_pow_difficulty(block) def switch_reverts_finalized_block(self, old_head, new_head): while old_head.number > new_head.number: if b'finalized:'+old_head.hash in self.db: log.info('[WARNING] Attempt to revert failed: checkpoint {} is finalized'.format(encode_hex(old_head.hash))) return True old_head = self.get_parent(old_head) while new_head.number > old_head.number: new_head = self.get_parent(new_head) while new_head.hash != old_head.hash: if b'finalized:'+old_head.hash in self.db: log.info('[WARNING] Attempt to revert failed; checkpoint {} is finalized'.format(encode_hex(old_head.hash))) return True old_head = self.get_parent(old_head) new_head = self.get_parent(new_head) return False ``` ## Epoch initialization At the beginning of each new epoch, the function `initialize_epoch(epoch)` must be called. This function _utilizes no gas_ and is called automatically by the `NULL_SENDER`. The code as [implemented in Pyethereum](https://github.com/karlfloersch/pyethereum/blob/develop/ethereum/hybrid_casper/consensus.py#L20-L26) as follows: ```python # Initalize the next epoch in the Casper contract if state.block_number % state.config['EPOCH_LENGTH'] == 0 and state.block_number != 0: key, account = state.config['SENDER'], privtoaddr(state.config['SENDER']) data = casper_utils.casper_translator.encode('initialize_epoch', [state.block_number // state.config['EPOCH_LENGTH']]) transaction = transactions.Transaction(state.get_nonce(account), 0, 3141592, state.config['CASPER_ADDRESS'], 0, data).sign(key) apply_casper_no_gas_transaction(state, transaction) ``` ## Casper Vote Gas Refunds Casper votes do not pay gas if successful, and should be considered invalid transactions and not be included in the block if failed. This avoids a large gas burden on validators. The code as [implemented in Pyethereum](https://github.com/karlfloersch/pyethereum/blob/a66ab671e0bb19327bb8cd11d69664146451c250/ethereum/messages.py#L184-L256) is as follows: ```python def apply_transaction(state, tx): casper_contract = tx.to == state.env.config['CASPER_ADDRESS'] vote = tx.data[0:4] == b'\xe9\xdc\x06\x14' null_sender = tx.sender == b'\xff' * 20 if casper_contract and vote and null_sender: log_tx.debug("Applying CASPER no gas transaction: {}".format(tx)) return apply_casper_no_gas_transaction(state, tx) else: log_tx.debug("Applying transaction (non-CASPER VOTE): {}".format(tx)) return apply_regular_transaction(state, tx) ... ``` Where `_apply_casper_no_gas_transaction(state, tx)` refunds the sender's gas when the transaction is successful, and is invalid when transaction fails. ## Casper Block Ordering A new block validity rule is added which asserts that all Casper `vote` transactions are included **after** normal transactions in a block. This allows Casper transactions to be processed in parallel with normal transactions. This is [implemented in Pyethereum](https://github.com/karlfloersch/pyethereum/blob/develop/ethereum/common.py#L122-L133) as follows: ```python # Validate that casper transactions come last def validate_casper_vote_transaction_ordering(state, block): reached_casper_vote_transactions = False for tx in block.transactions: casper_contract = tx.to == state.env.config['CASPER_ADDRESS'] vote = tx.data[0:4] == b'\xe9\xdc\x06\x14' null_sender = tx.sender == b'\xff' * 20 if casper_contract and vote and null_sender: reached_casper_vote_transactions = True elif reached_casper_vote_transactions: raise InvalidTransaction("Please put all Casper transactions last") return True ``` ## Simple Validator Voting Logic Once a validator is logged in, they can use the following logic to determine when to send a vote: 1. When a new block is received & replaces our chain's current head, call `validate(block)` 2. Inside `validate(block)` check: 1) The block is at least `EPOCH_LENGTH/4` blocks deep to ensure that the checkpoint hash is safe to vote on. 2) [NO_DBL_VOTE] The block's epoch has not been voted on before. 3) [NO_SURROUND] The block's `target_epoch` >= `self.latest_target_epoch` and `source_epoch` >= `self.latest_source_epoch`. NOTE: This check is very simple, but it excludes a couple cases where it would be safe to vote. 3. If all of the checks pass, generate & send a new vote! NOTE: To check if a validator is logged in, one can use: ``` python return casper.validators__start_dynasty(validator_index) >= casper.dynasty() ``` #### [[See the current validator implementation for more information.]](https://github.com/karlfloersch/pyethapp/blob/dev_env/pyethapp/validator_service.py)

Import from clipboard

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lost their connection.

Create a note from template

Create a note from template

Oops...
This template has been removed or transferred.


Upgrade

All
  • All
  • Team
No template.

Create a template


Upgrade

Delete template

Do you really want to delete this template?

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Sign in via SAML

or

Sign in via GitHub

Help

  • English
  • 中文
  • 日本語

Documents

Tutorials

Book Mode Tutorial

Slide Example

YAML Metadata

Resources

Releases

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions

Versions and GitHub Sync

Sign in to link this note to GitHub Learn more
This note is not linked with GitHub Learn more
 
Add badge Pull Push GitHub Link Settings
Upgrade now

Version named by    

More Less
  • Edit
  • Delete

Note content is identical to the latest version.
Compare with
    Choose a version
    No search result
    Version not found

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub

      Please sign in to GitHub and install the HackMD app on your GitHub repo. Learn more

       Sign in to GitHub

      HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Available push count

      Upgrade

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Upgrade

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully