From 722e2011092eec3f9fc90c3581fad39e49a9c86d Mon Sep 17 00:00:00 2001 From: Vitalik Buterin Date: Wed, 23 Jan 2019 00:05:50 -0600 Subject: [PATCH 1/6] Updated deposit contract for gas efficiency --- specs/core/0_beacon-chain.md | 65 +++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 0facc0a85..eeac36683 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -656,54 +656,71 @@ SECONDS_PER_DAY: constant(uint256) = 86400 Deposit: event({previous_deposit_root: bytes32, data: bytes[2064], merkle_tree_index: bytes[8]}) ChainStart: event({deposit_root: bytes32, time: bytes[8]}) -deposit_tree: map(uint256, bytes32) +zerohashes: bytes32[32] +branch: bytes32[32] deposit_count: uint256 full_deposit_count: uint256 +@public +def __init__(): + for i in range(31): + self.zerohashes[i+1] = sha3(concat(self.zerohashes[i], self.zerohashes[i])) + self.branch[i+1] = self.zerohashes[i+1] + +@public +@constant +def get_deposit_root() -> bytes32: + root:bytes32 = 0x0000000000000000000000000000000000000000000000000000000000000000 + size:uint256 = self.deposit_count + for h in range(32): + if size % 2 == 1: + root = sha3(concat(self.branch[h], root)) + else: + root = sha3(concat(root, self.branch[h])) + size /= 2 + return root + @payable @public def deposit(deposit_input: bytes[2048]): assert msg.value >= as_wei_value(MIN_DEPOSIT_AMOUNT, "gwei") assert msg.value <= as_wei_value(MAX_DEPOSIT_AMOUNT, "gwei") - index: uint256 = self.deposit_count + TWO_TO_POWER_OF_TREE_DEPTH + index: uint256 = self.deposit_count deposit_amount: bytes[8] = slice(concat("", convert(msg.value / GWEI_PER_ETH, bytes32)), start=24, len=8) deposit_timestamp: bytes[8] = slice(concat("", convert(block.timestamp, bytes32)), start=24, len=8) deposit_data: bytes[2064] = concat(deposit_amount, deposit_timestamp, deposit_input) merkle_tree_index: bytes[8] = slice(concat("", convert(index, bytes32)), start=24, len=8) - log.Deposit(self.deposit_tree[1], deposit_data, merkle_tree_index) - # add deposit to merkle tree - self.deposit_tree[index] = sha3(deposit_data) - for i in range(DEPOSIT_CONTRACT_TREE_DEPTH): - index /= 2 - self.deposit_tree[index] = sha3(concat(self.deposit_tree[index * 2], self.deposit_tree[index * 2 + 1])) + i: int128 = 0 + power_of_two: uint256 = 2 + for _ in range(32): + if (index+1) % power_of_two != 0: + break + i += 1 + power_of_two *= 2 + value:bytes32 = sha3(deposit_data) + for j in range(32): + if j < i: + value = sha3(concat(self.branch[j], value)) + self.branch[j] = self.zerohashes[j] + self.branch[i] = value self.deposit_count += 1 + + log.Deposit(self.get_deposit_root(), deposit_data, merkle_tree_index) + if msg.value == as_wei_value(MAX_DEPOSIT_AMOUNT, "gwei"): self.full_deposit_count += 1 if self.full_deposit_count == CHAIN_START_FULL_DEPOSIT_THRESHOLD: timestamp_day_boundary: uint256 = as_unitless_number(block.timestamp) - as_unitless_number(block.timestamp) % SECONDS_PER_DAY + SECONDS_PER_DAY chainstart_time: bytes[8] = slice(concat("", convert(timestamp_day_boundary, bytes32)), start=24, len=8) - log.ChainStart(self.deposit_tree[1], chainstart_time) - -@public -@constant -def get_deposit_root() -> bytes32: - return self.deposit_tree[1] - -@public -@constant -def get_branch(leaf: uint256) -> bytes32[32]: # size is DEPOSIT_CONTRACT_TREE_DEPTH (symbolic const not supported) - branch: bytes32[32] # size is DEPOSIT_CONTRACT_TREE_DEPTH - index: uint256 = leaf + TWO_TO_POWER_OF_TREE_DEPTH - for i in range(DEPOSIT_CONTRACT_TREE_DEPTH): - branch[i] = self.deposit_tree[bitwise_xor(index, 1)] - index /= 2 - return branch + log.ChainStart(self.get_deposit_root(), chainstart_time) ``` +Note: to save ~10x on gas this contract uses a somewhat unintuitive progressive Merkle root calculation algo that requires only O(log(n)) storage. See https://github.com/ethereum/research/blob/master/beacon_chain_impl/progressive_merkle_tree.py for an implementation of the same algo in python tested for correctness. + ## Beacon chain processing The beacon chain is the system chain for Ethereum 2.0. The main responsibilities of the beacon chain are: From c8b2755177b054c6291c75dbb3c8a23a8975e342 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Wed, 23 Jan 2019 06:11:55 -0600 Subject: [PATCH 2/6] Added efficiency improvement (two fewer expected SSTOREs per deposit) --- specs/core/0_beacon-chain.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index eeac36683..b19c6724d 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -653,7 +653,7 @@ DEPOSIT_CONTRACT_TREE_DEPTH: constant(uint256) = 32 TWO_TO_POWER_OF_TREE_DEPTH: constant(uint256) = 4294967296 # 2**32 SECONDS_PER_DAY: constant(uint256) = 86400 -Deposit: event({previous_deposit_root: bytes32, data: bytes[2064], merkle_tree_index: bytes[8]}) +Deposit: event({previous_deposit_root: bytes32, data: bytes[2064], merkle_tree_index: bytes[8], branch: bytes32[32]}) ChainStart: event({deposit_root: bytes32, time: bytes[8]}) zerohashes: bytes32[32] @@ -676,7 +676,7 @@ def get_deposit_root() -> bytes32: if size % 2 == 1: root = sha3(concat(self.branch[h], root)) else: - root = sha3(concat(root, self.branch[h])) + root = sha3(concat(root, self.zerohashes[h])) size /= 2 return root @@ -704,12 +704,11 @@ def deposit(deposit_input: bytes[2048]): for j in range(32): if j < i: value = sha3(concat(self.branch[j], value)) - self.branch[j] = self.zerohashes[j] self.branch[i] = value self.deposit_count += 1 - log.Deposit(self.get_deposit_root(), deposit_data, merkle_tree_index) + log.Deposit(self.get_deposit_root(), deposit_data, merkle_tree_index, self.branch) if msg.value == as_wei_value(MAX_DEPOSIT_AMOUNT, "gwei"): self.full_deposit_count += 1 From 1b06e771d827d81e5edb105de42ae37b7b5f408e Mon Sep 17 00:00:00 2001 From: vbuterin Date: Thu, 24 Jan 2019 03:25:13 -0600 Subject: [PATCH 3/6] previous_deposit_root -> deposit_root --- 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 b19c6724d..7abe82b98 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -653,7 +653,7 @@ DEPOSIT_CONTRACT_TREE_DEPTH: constant(uint256) = 32 TWO_TO_POWER_OF_TREE_DEPTH: constant(uint256) = 4294967296 # 2**32 SECONDS_PER_DAY: constant(uint256) = 86400 -Deposit: event({previous_deposit_root: bytes32, data: bytes[2064], merkle_tree_index: bytes[8], branch: bytes32[32]}) +Deposit: event({deposit_root: bytes32, data: bytes[2064], merkle_tree_index: bytes[8], branch: bytes32[32]}) ChainStart: event({deposit_root: bytes32, time: bytes[8]}) zerohashes: bytes32[32] From fab0feeb2936b750092abce1e4d9a554a49e64c7 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sat, 26 Jan 2019 16:47:18 -0600 Subject: [PATCH 4/6] Vyper contract 2048 -> 512 --- specs/core/0_beacon-chain.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 7abe82b98..0ca5a3f21 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -653,7 +653,7 @@ DEPOSIT_CONTRACT_TREE_DEPTH: constant(uint256) = 32 TWO_TO_POWER_OF_TREE_DEPTH: constant(uint256) = 4294967296 # 2**32 SECONDS_PER_DAY: constant(uint256) = 86400 -Deposit: event({deposit_root: bytes32, data: bytes[2064], merkle_tree_index: bytes[8], branch: bytes32[32]}) +Deposit: event({deposit_root: bytes32, data: bytes[528], merkle_tree_index: bytes[8], branch: bytes32[32]}) ChainStart: event({deposit_root: bytes32, time: bytes[8]}) zerohashes: bytes32[32] @@ -682,14 +682,14 @@ def get_deposit_root() -> bytes32: @payable @public -def deposit(deposit_input: bytes[2048]): +def deposit(deposit_input: bytes[512]): assert msg.value >= as_wei_value(MIN_DEPOSIT_AMOUNT, "gwei") assert msg.value <= as_wei_value(MAX_DEPOSIT_AMOUNT, "gwei") index: uint256 = self.deposit_count deposit_amount: bytes[8] = slice(concat("", convert(msg.value / GWEI_PER_ETH, bytes32)), start=24, len=8) deposit_timestamp: bytes[8] = slice(concat("", convert(block.timestamp, bytes32)), start=24, len=8) - deposit_data: bytes[2064] = concat(deposit_amount, deposit_timestamp, deposit_input) + deposit_data: bytes[528] = concat(deposit_amount, deposit_timestamp, deposit_input) merkle_tree_index: bytes[8] = slice(concat("", convert(index, bytes32)), start=24, len=8) # add deposit to merkle tree From 30ca318869fe1f41e39aa2d6dd4c8ad8c25006d3 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Tue, 29 Jan 2019 18:58:56 -0600 Subject: [PATCH 5/6] Rewrite to not trigger temporary Vyper issues --- specs/core/0_beacon-chain.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 0ca5a3f21..6d3a9574a 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -708,7 +708,8 @@ def deposit(deposit_input: bytes[512]): self.deposit_count += 1 - log.Deposit(self.get_deposit_root(), deposit_data, merkle_tree_index, self.branch) + new_deposit_root:bytes32 = self.get_deposit_root() + log.Deposit(new_deposit_root, deposit_data, merkle_tree_index, self.branch) if msg.value == as_wei_value(MAX_DEPOSIT_AMOUNT, "gwei"): self.full_deposit_count += 1 From 260c642e3f52b1795f476d15e650af8767982509 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Tue, 29 Jan 2019 19:00:39 -0600 Subject: [PATCH 6/6] One more new_deposit_root, and chainStarted as public bool --- 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 6d3a9574a..370b2c2bd 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -660,6 +660,7 @@ zerohashes: bytes32[32] branch: bytes32[32] deposit_count: uint256 full_deposit_count: uint256 +chainStarted: public(bool) @public def __init__(): @@ -716,7 +717,8 @@ def deposit(deposit_input: bytes[512]): if self.full_deposit_count == CHAIN_START_FULL_DEPOSIT_THRESHOLD: timestamp_day_boundary: uint256 = as_unitless_number(block.timestamp) - as_unitless_number(block.timestamp) % SECONDS_PER_DAY + SECONDS_PER_DAY chainstart_time: bytes[8] = slice(concat("", convert(timestamp_day_boundary, bytes32)), start=24, len=8) - log.ChainStart(self.get_deposit_root(), chainstart_time) + log.ChainStart(new_deposit_root, chainstart_time) + self.chainStarted = True ``` Note: to save ~10x on gas this contract uses a somewhat unintuitive progressive Merkle root calculation algo that requires only O(log(n)) storage. See https://github.com/ethereum/research/blob/master/beacon_chain_impl/progressive_merkle_tree.py for an implementation of the same algo in python tested for correctness.