From d597798f9304491c9d301a8aadb1ba383f7e3130 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Mon, 11 Feb 2019 08:17:34 -0600 Subject: [PATCH 01/10] Add support for transfers between withdrawn accounts --- specs/core/0_beacon-chain.md | 41 ++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 3c89cf1a91..b726f0d4f5 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -38,6 +38,8 @@ - [`DepositInput`](#depositinput) - [Exits](#exits) - [`Exit`](#exit) + - [Transfers](#transfers) + - [`Transfer`](#transfer) - [Beacon chain blocks](#beacon-chain-blocks) - [`BeaconBlock`](#beaconblock) - [`BeaconBlockBody`](#beaconblockbody) @@ -269,6 +271,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be | `MAX_ATTESTATIONS` | `2**7` (= 128) | | `MAX_DEPOSITS` | `2**4` (= 16) | | `MAX_EXITS` | `2**4` (= 16) | +| `MAX_TRANSFERS` | `2**4` (= 16) | ### Signature domains @@ -279,6 +282,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be | `DOMAIN_PROPOSAL` | `2` | | `DOMAIN_EXIT` | `3` | | `DOMAIN_RANDAO` | `4` | +| `DOMAIN_TRANSFER` | `5` | ## Data structures @@ -440,6 +444,25 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git } ``` +##### `Transfer` + +```python +{ + # Sending from + 'sender': 'uint64', + # Sending to + 'to': 'uint64', + # Amount to send + 'value': 'uint64', + # Fee + 'fee': 'uint64', + # Must be included in this slot + 'expected_slot': 'uint64', + # Sender signature + 'signature' +} +``` + ### Beacon chain blocks #### `BeaconBlock` @@ -468,6 +491,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git 'attestations': [Attestation], 'deposits': [Deposit], 'exits': [Exit], + 'transfers': [Transfer], } ``` @@ -1766,6 +1790,23 @@ For each `exit` in `block.body.exits`: * Verify that `bls_verify(pubkey=validator.pubkey, message_hash=exit_message, signature=exit.signature, domain=get_domain(state.fork, exit.epoch, DOMAIN_EXIT))`. * Run `initiate_validator_exit(state, exit.validator_index)`. +##### Transfers + +Verify that `len(block.body.transfers) <= MAX_TRANSFERS`. + +For each `transfer` in `block.body.transfers`: + +* Verify that either (i) `state.validator_balances[transfer.sender] == transfer.amount + transfer.fee` or (ii) `state.validator_balances[transfer.sender] >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT`. +* Verify that `transfer.expected_slot == state.slot`. +* Verify that `transfer.sender` does not match the `sender` of any other transfer in `block.body.transfers` +* Let `transfer_message = hash_tree_root(Transfer(sender=transfer.sender, to=transfer.to, value=transfer.value, fee=transfer.fee, expected_slot=transfer.expected_slot, signature=EMPTY_SIGNATURE))`. +* Verify that `bls_verify(pubkey=state.validator_registry[transfer.sender].pubkey, message_hash=transfer_message, signature=transfer.signature, domain=get_domain(state.fork, slot_to_epoch(transfer.expected_slot)), DOMAIN_TRANSFER))`. +* Verify that `state.validator_registry[transfer.sender].withdrawal_epoch <= get_current_epoch(state)` +* Verify that `state.validator_registry[transfer.to].withdrawal_epoch <= get_current_epoch(state)` +* Set `state.validator_balances[transfer.sender] -= transfer.amount + transfer.fee`. +* Set `state.validator_balances[transfer.to] += transfer.amount`. +* Set `state.validator_balances[get_beacon_proposer_index(state, state.slot)] += transfer.fee`. + ### Per-epoch processing The steps below happen when `(state.slot + 1) % EPOCH_LENGTH == 0`. From 8fe0f6b1e591f0c857f2dd810c83df56c683004c Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 11 Feb 2019 14:41:37 +0000 Subject: [PATCH 02/10] Update 0_beacon-chain.md Bug fixes: * Flip the `withdrawal_epoch` comparisons * Extraneous braket Misc cleanups. --- specs/core/0_beacon-chain.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index b726f0d4f5..15e8a1047b 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -449,7 +449,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git ```python { # Sending from - 'sender': 'uint64', + 'from': 'uint64', # Sending to 'to': 'uint64', # Amount to send @@ -457,7 +457,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git # Fee 'fee': 'uint64', # Must be included in this slot - 'expected_slot': 'uint64', + 'slot': 'uint64', # Sender signature 'signature' } @@ -1796,14 +1796,14 @@ Verify that `len(block.body.transfers) <= MAX_TRANSFERS`. For each `transfer` in `block.body.transfers`: -* Verify that either (i) `state.validator_balances[transfer.sender] == transfer.amount + transfer.fee` or (ii) `state.validator_balances[transfer.sender] >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT`. -* Verify that `transfer.expected_slot == state.slot`. -* Verify that `transfer.sender` does not match the `sender` of any other transfer in `block.body.transfers` -* Let `transfer_message = hash_tree_root(Transfer(sender=transfer.sender, to=transfer.to, value=transfer.value, fee=transfer.fee, expected_slot=transfer.expected_slot, signature=EMPTY_SIGNATURE))`. -* Verify that `bls_verify(pubkey=state.validator_registry[transfer.sender].pubkey, message_hash=transfer_message, signature=transfer.signature, domain=get_domain(state.fork, slot_to_epoch(transfer.expected_slot)), DOMAIN_TRANSFER))`. -* Verify that `state.validator_registry[transfer.sender].withdrawal_epoch <= get_current_epoch(state)` -* Verify that `state.validator_registry[transfer.to].withdrawal_epoch <= get_current_epoch(state)` -* Set `state.validator_balances[transfer.sender] -= transfer.amount + transfer.fee`. +* Verify that `state.validator_balances[transfer.from] == transfer.amount + transfer.fee` or `state.validator_balances[transfer.from] >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT`. +* Verify that `transfer.slot == state.slot`. +* Verify that `transfer.from` does not match the `from` field of another transfer in `block.body.transfers`. +* Verify that `get_current_epoch(state) <= state.validator_registry[transfer.from].withdrawal_epoch`. +* Verify that `get_current_epoch(state) <= state.validator_registry[transfer.to].withdrawal_epoch`. +* Let `transfer_message = hash_tree_root(Transfer(from=transfer.from, to=transfer.to, value=transfer.value, fee=transfer.fee, slot=transfer.slot, signature=EMPTY_SIGNATURE))`. +* Verify that `bls_verify(pubkey=state.validator_registry[transfer.from].pubkey, message_hash=transfer_message, signature=transfer.signature, domain=get_domain(state.fork, slot_to_epoch(transfer.slot), DOMAIN_TRANSFER))`. +* Set `state.validator_balances[transfer.from] -= transfer.amount + transfer.fee`. * Set `state.validator_balances[transfer.to] += transfer.amount`. * Set `state.validator_balances[get_beacon_proposer_index(state, state.slot)] += transfer.fee`. From 8424ebb8a6fa16de95e6cf09a5fd8dbfc3f0f69e Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 11 Feb 2019 14:47:13 +0000 Subject: [PATCH 03/10] Update 0_beacon-chain.md Bug fixes: * Add missing type declaration for `signature`. * Prevent overflow of `transfer.amount + transfer.fee` by checking that each term is no greater than sender balance. --- specs/core/0_beacon-chain.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 15e8a1047b..ee73b1459e 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -459,7 +459,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git # Must be included in this slot 'slot': 'uint64', # Sender signature - 'signature' + 'signature': 'bytes96', } ``` @@ -1796,6 +1796,8 @@ Verify that `len(block.body.transfers) <= MAX_TRANSFERS`. For each `transfer` in `block.body.transfers`: +* Verify that `state.validator_balances[transfer.from] >= transfer.amount`. +* Verify that `state.validator_balances[transfer.from] >= transfer.fee`. * Verify that `state.validator_balances[transfer.from] == transfer.amount + transfer.fee` or `state.validator_balances[transfer.from] >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT`. * Verify that `transfer.slot == state.slot`. * Verify that `transfer.from` does not match the `from` field of another transfer in `block.body.transfers`. From f4ccbd41483900f954d86064c970fbd0d4a10920 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 11 Feb 2019 15:38:49 +0000 Subject: [PATCH 04/10] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index ee73b1459e..34a7d9e0fd 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1792,6 +1792,8 @@ For each `exit` in `block.body.exits`: ##### Transfers +Note: Transfers are a temporary functionality for phases 0 and 1, to be removed in phase 2. + Verify that `len(block.body.transfers) <= MAX_TRANSFERS`. For each `transfer` in `block.body.transfers`: From 8a3163485c380f36940f4408df4a92680d9a803f Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 11 Feb 2019 07:43:14 -0800 Subject: [PATCH 05/10] clarify amount in gwei --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 34a7d9e0fd..28049e5b16 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -452,7 +452,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git 'from': 'uint64', # Sending to 'to': 'uint64', - # Amount to send + # Amount in GWEI to send 'value': 'uint64', # Fee 'fee': 'uint64', From a75022b5da8865e5e238a422ba57af70ba413e13 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 11 Feb 2019 07:45:22 -0800 Subject: [PATCH 06/10] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 28049e5b16..726ba3d4cf 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -454,7 +454,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git 'to': 'uint64', # Amount in GWEI to send 'value': 'uint64', - # Fee + # Fee in GWEI to block proposer 'fee': 'uint64', # Must be included in this slot 'slot': 'uint64', From 114a2b051abc2711969577fd1fb76f3af7d79c30 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Mon, 11 Feb 2019 22:09:48 -0600 Subject: [PATCH 07/10] Withdrawal key, not signing key, can transfer --- specs/core/0_beacon-chain.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 726ba3d4cf..84c75db9b7 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -458,6 +458,8 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git 'fee': 'uint64', # Must be included in this slot 'slot': 'uint64', + # Sender's public key + 'pubkey': 'bytes48', # Sender signature 'signature': 'bytes96', } @@ -1805,8 +1807,9 @@ For each `transfer` in `block.body.transfers`: * Verify that `transfer.from` does not match the `from` field of another transfer in `block.body.transfers`. * Verify that `get_current_epoch(state) <= state.validator_registry[transfer.from].withdrawal_epoch`. * Verify that `get_current_epoch(state) <= state.validator_registry[transfer.to].withdrawal_epoch`. +* Verify that `hash(transfer.pubkey) == b'\x00' + state.validator_registry[transfer.from].withdrawal_credentials[1:]` * Let `transfer_message = hash_tree_root(Transfer(from=transfer.from, to=transfer.to, value=transfer.value, fee=transfer.fee, slot=transfer.slot, signature=EMPTY_SIGNATURE))`. -* Verify that `bls_verify(pubkey=state.validator_registry[transfer.from].pubkey, message_hash=transfer_message, signature=transfer.signature, domain=get_domain(state.fork, slot_to_epoch(transfer.slot), DOMAIN_TRANSFER))`. +* Verify that `bls_verify(pubkey=transfer.pubkey, message_hash=transfer_message, signature=transfer.signature, domain=get_domain(state.fork, slot_to_epoch(transfer.slot), DOMAIN_TRANSFER))`. * Set `state.validator_balances[transfer.from] -= transfer.amount + transfer.fee`. * Set `state.validator_balances[transfer.to] += transfer.amount`. * Set `state.validator_balances[get_beacon_proposer_index(state, state.slot)] += transfer.fee`. From 910101ae7899a1a2351c57fb2e8946c16433f912 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 12 Feb 2019 11:25:28 +0000 Subject: [PATCH 08/10] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 84c75db9b7..ec122ba7ba 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -233,6 +233,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be | `ENTRY_EXIT_DELAY` | `2**2` (= 4) | epochs | 25.6 minutes | | `ETH1_DATA_VOTING_PERIOD` | `2**4` (= 16) | epochs | ~1.7 hours | | `MIN_VALIDATOR_WITHDRAWAL_EPOCHS` | `2**8` (= 256) | epochs | ~27 hours | +| `MIN_EXIT_EPOCHS_BEFORE_TRANSFER` | `2**13` (= 8,192) | epochs | ~36 days | ### State list lengths @@ -448,17 +449,17 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git ```python { - # Sending from + # Sender index 'from': 'uint64', - # Sending to + # Recipient index 'to': 'uint64', - # Amount in GWEI to send - 'value': 'uint64', - # Fee in GWEI to block proposer + # Amount in Gwei + 'amount': 'uint64', + # Fee in Gwei for block proposer 'fee': 'uint64', - # Must be included in this slot + # Inclusion slot 'slot': 'uint64', - # Sender's public key + # Sender withdrawal pubkey 'pubkey': 'bytes48', # Sender signature 'signature': 'bytes96', @@ -1805,10 +1806,9 @@ For each `transfer` in `block.body.transfers`: * Verify that `state.validator_balances[transfer.from] == transfer.amount + transfer.fee` or `state.validator_balances[transfer.from] >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT`. * Verify that `transfer.slot == state.slot`. * Verify that `transfer.from` does not match the `from` field of another transfer in `block.body.transfers`. -* Verify that `get_current_epoch(state) <= state.validator_registry[transfer.from].withdrawal_epoch`. -* Verify that `get_current_epoch(state) <= state.validator_registry[transfer.to].withdrawal_epoch`. -* Verify that `hash(transfer.pubkey) == b'\x00' + state.validator_registry[transfer.from].withdrawal_credentials[1:]` -* Let `transfer_message = hash_tree_root(Transfer(from=transfer.from, to=transfer.to, value=transfer.value, fee=transfer.fee, slot=transfer.slot, signature=EMPTY_SIGNATURE))`. +* Verify that `get_current_epoch(state) >= state.validator_registry[transfer.from].exit_epoch + MIN_EXIT_EPOCHS_BEFORE_TRANSFER`. +* Verify that `hash(transfer.pubkey) == BLS_WITHDRAWAL_PREFIX_BYTE + state.validator_registry[transfer.from].withdrawal_credentials[1:]` +* Let `transfer_message = hash_tree_root(Transfer(from=transfer.from, to=transfer.to, amount=transfer.amount, fee=transfer.fee, slot=transfer.slot, signature=EMPTY_SIGNATURE))`. * Verify that `bls_verify(pubkey=transfer.pubkey, message_hash=transfer_message, signature=transfer.signature, domain=get_domain(state.fork, slot_to_epoch(transfer.slot), DOMAIN_TRANSFER))`. * Set `state.validator_balances[transfer.from] -= transfer.amount + transfer.fee`. * Set `state.validator_balances[transfer.to] += transfer.amount`. From 90340d5040000c22f3f0b3d2b378b037ea4035c2 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 12 Feb 2019 19:47:06 +0000 Subject: [PATCH 09/10] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index ec122ba7ba..bf6172c84c 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1807,7 +1807,7 @@ For each `transfer` in `block.body.transfers`: * Verify that `transfer.slot == state.slot`. * Verify that `transfer.from` does not match the `from` field of another transfer in `block.body.transfers`. * Verify that `get_current_epoch(state) >= state.validator_registry[transfer.from].exit_epoch + MIN_EXIT_EPOCHS_BEFORE_TRANSFER`. -* Verify that `hash(transfer.pubkey) == BLS_WITHDRAWAL_PREFIX_BYTE + state.validator_registry[transfer.from].withdrawal_credentials[1:]` +* Verify that `state.validator_registry[transfer.from].withdrawal_credentials == BLS_WITHDRAWAL_PREFIX_BYTE + hash(transfer.pubkey)[1:]`. * Let `transfer_message = hash_tree_root(Transfer(from=transfer.from, to=transfer.to, amount=transfer.amount, fee=transfer.fee, slot=transfer.slot, signature=EMPTY_SIGNATURE))`. * Verify that `bls_verify(pubkey=transfer.pubkey, message_hash=transfer_message, signature=transfer.signature, domain=get_domain(state.fork, slot_to_epoch(transfer.slot), DOMAIN_TRANSFER))`. * Set `state.validator_balances[transfer.from] -= transfer.amount + transfer.fee`. From 0f899f78358edfcab6175ca62814f870dbde548e Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 12 Feb 2019 21:56:29 +0000 Subject: [PATCH 10/10] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index bf6172c84c..73f1b492b7 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1797,7 +1797,7 @@ For each `exit` in `block.body.exits`: Note: Transfers are a temporary functionality for phases 0 and 1, to be removed in phase 2. -Verify that `len(block.body.transfers) <= MAX_TRANSFERS`. +Verify that `len(block.body.transfers) <= MAX_TRANSFERS` and that all transfers are distinct. For each `transfer` in `block.body.transfers`: @@ -1805,7 +1805,6 @@ For each `transfer` in `block.body.transfers`: * Verify that `state.validator_balances[transfer.from] >= transfer.fee`. * Verify that `state.validator_balances[transfer.from] == transfer.amount + transfer.fee` or `state.validator_balances[transfer.from] >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT`. * Verify that `transfer.slot == state.slot`. -* Verify that `transfer.from` does not match the `from` field of another transfer in `block.body.transfers`. * Verify that `get_current_epoch(state) >= state.validator_registry[transfer.from].exit_epoch + MIN_EXIT_EPOCHS_BEFORE_TRANSFER`. * Verify that `state.validator_registry[transfer.from].withdrawal_credentials == BLS_WITHDRAWAL_PREFIX_BYTE + hash(transfer.pubkey)[1:]`. * Let `transfer_message = hash_tree_root(Transfer(from=transfer.from, to=transfer.to, amount=transfer.amount, fee=transfer.fee, slot=transfer.slot, signature=EMPTY_SIGNATURE))`.