Change rewards coin ids 2 (#1342)

Replay protection by encoding genesis challenge into coinbase parent ids. 

Co-authored-by: Yostra <straya@chia.net>
This commit is contained in:
Richard Kiss 2021-03-17 03:26:27 -07:00 committed by Gene Hoffman
parent 8137ea162c
commit da57397087
17 changed files with 58 additions and 41 deletions

View File

@ -14,7 +14,7 @@ jobs:
build:
name: MacOS Blockchain Tests
runs-on: ${{ matrix.os }}
timeout-minutes: 90
timeout-minutes: 120
strategy:
fail-fast: false
max-parallel: 4
@ -58,7 +58,7 @@ jobs:
with:
repository: 'Chia-Network/test-cache'
path: '.chia'
ref: '0.24.0'
ref: '0.26.0'
fetch-depth: 1
- name: Link home directory

View File

@ -58,7 +58,7 @@ jobs:
with:
repository: 'Chia-Network/test-cache'
path: '.chia'
ref: '0.24.0'
ref: '0.26.0'
fetch-depth: 1
- name: Link home directory

View File

@ -58,7 +58,7 @@ jobs:
with:
repository: 'Chia-Network/test-cache'
path: '.chia'
ref: '0.24.0'
ref: '0.26.0'
fetch-depth: 1
- name: Link home directory

View File

@ -58,7 +58,7 @@ jobs:
with:
repository: 'Chia-Network/test-cache'
path: '.chia'
ref: '0.24.0'
ref: '0.26.0'
fetch-depth: 1
- name: Link home directory

View File

@ -65,7 +65,7 @@ jobs:
with:
repository: 'Chia-Network/test-cache'
path: '.chia'
ref: '0.24.0'
ref: '0.26.0'
fetch-depth: 1
- name: Link home directory

View File

@ -65,7 +65,7 @@ jobs:
with:
repository: 'Chia-Network/test-cache'
path: '.chia'
ref: '0.24.0'
ref: '0.26.0'
fetch-depth: 1
- name: Link home directory

View File

@ -65,7 +65,7 @@ jobs:
with:
repository: 'Chia-Network/test-cache'
path: '.chia'
ref: '0.24.0'
ref: '0.26.0'
fetch-depth: 1
- name: Link home directory

View File

@ -65,7 +65,7 @@ jobs:
with:
repository: 'Chia-Network/test-cache'
path: '.chia'
ref: '0.24.0'
ref: '0.26.0'
fetch-depth: 1
- name: Link home directory

View File

@ -98,17 +98,18 @@ async def validate_block_body(
# Add reward claims for all blocks from the prev prev block, until the prev block (including the latter)
prev_transaction_block = blocks.block_record(block.foliage_transaction_block.prev_transaction_block_hash)
prev_transaction_block_height = prev_transaction_block.height
assert prev_transaction_block.fees is not None
pool_coin = create_pool_coin(
prev_transaction_block.height,
prev_transaction_block_height,
prev_transaction_block.pool_puzzle_hash,
calculate_pool_reward(prev_transaction_block.height),
constants.GENESIS_CHALLENGE,
)
farmer_coin = create_farmer_coin(
prev_transaction_block.height,
prev_transaction_block_height,
prev_transaction_block.farmer_puzzle_hash,
uint64(calculate_base_farmer_reward(prev_transaction_block.height) + prev_transaction_block.fees),
constants.GENESIS_CHALLENGE,
)
# Adds the previous block
expected_reward_coins.add(pool_coin)
@ -123,6 +124,7 @@ async def validate_block_body(
curr_b.height,
curr_b.pool_puzzle_hash,
calculate_pool_reward(curr_b.height),
constants.GENESIS_CHALLENGE,
)
)
expected_reward_coins.add(
@ -130,6 +132,7 @@ async def validate_block_body(
curr_b.height,
curr_b.farmer_puzzle_hash,
calculate_base_farmer_reward(curr_b.height),
constants.GENESIS_CHALLENGE,
)
)
curr_b = blocks.block_record(curr_b.prev_hash)

View File

@ -148,15 +148,14 @@ def create_foliage(
assert curr.fees is not None
pool_coin = create_pool_coin(
curr.height,
curr.pool_puzzle_hash,
calculate_pool_reward(curr.height),
curr.height, curr.pool_puzzle_hash, calculate_pool_reward(curr.height), constants.GENESIS_CHALLENGE
)
farmer_coin = create_farmer_coin(
curr.height,
curr.farmer_puzzle_hash,
uint64(calculate_base_farmer_reward(curr.height) + curr.fees),
constants.GENESIS_CHALLENGE,
)
assert curr.header_hash == prev_transaction_block.header_hash
reward_claims_incorporated += [pool_coin, farmer_coin]
@ -169,11 +168,13 @@ def create_foliage(
curr.height,
curr.pool_puzzle_hash,
calculate_pool_reward(curr.height),
constants.GENESIS_CHALLENGE,
)
farmer_coin = create_farmer_coin(
curr.height,
curr.farmer_puzzle_hash,
calculate_base_farmer_reward(curr.height),
constants.GENESIS_CHALLENGE,
)
reward_claims_incorporated += [pool_coin, farmer_coin]
curr = blocks.block_record(curr.prev_hash)

View File

@ -2,7 +2,6 @@ from blspy import AugSchemeMPL, G1Element, G2Element, PrivateKey
from src.types.blockchain_format.coin import Coin
from src.types.blockchain_format.sized_bytes import bytes32
from src.util.hash import std_hash
from src.util.ints import uint32, uint64
from src.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import puzzle_for_pk
@ -22,11 +21,19 @@ def sign_coinbase_coin(coin: Coin, private_key: PrivateKey):
return signature_for_coinbase(coin, private_key)
def create_pool_coin(block_index: uint32, puzzle_hash: bytes32, reward: uint64):
block_index_as_hash = bytes32(block_index.to_bytes(32, "big"))
return Coin(block_index_as_hash, puzzle_hash, reward)
def pool_parent_id(block_height: uint32, genesis_challenge: bytes32) -> uint32:
return bytes32(genesis_challenge[:16] + block_height.to_bytes(16, "big"))
def create_farmer_coin(block_index: uint32, puzzle_hash: bytes32, reward: uint64):
block_index_as_hash = std_hash(std_hash(block_index.to_bytes(4, "big")))
return Coin(block_index_as_hash, puzzle_hash, reward)
def farmer_parent_id(block_height: uint32, genesis_challenge: bytes32) -> uint32:
return bytes32(genesis_challenge[16:] + block_height.to_bytes(16, "big"))
def create_pool_coin(block_height: uint32, puzzle_hash: bytes32, reward: uint64, genesis_challenge: bytes32):
parent_id = pool_parent_id(block_height, genesis_challenge)
return Coin(parent_id, puzzle_hash, reward)
def create_farmer_coin(block_height: uint32, puzzle_hash: bytes32, reward: uint64, genesis_challenge: bytes32):
parent_id = farmer_parent_id(block_height, genesis_challenge)
return Coin(parent_id, puzzle_hash, reward)

View File

@ -1,4 +1,3 @@
from src.types.blockchain_format.sized_bytes import bytes32
from src.util.ints import uint64
from .constants import ConsensusConstants
@ -28,12 +27,13 @@ testnet_kwargs = {
"NUMBER_OF_TIMESTAMPS": 11, # Than the average of the last NUMBER_OF_TIMESTAMPS blocks
# Used as the initial cc rc challenges, as well as first block back pointers, and first SES back pointer
# We override this value based on the chain being run (testnet0, testnet1, mainnet, etc)
"GENESIS_CHALLENGE": bytes32([0x00] * 32),
# Default used for tests is std_hash(b'')
"GENESIS_CHALLENGE": bytes.fromhex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"),
"GENESIS_PRE_FARM_POOL_PUZZLE_HASH": bytes.fromhex(
"bc4fd6c394fe90c6097afbfa6ab5927743e2210ecf689dad477be8eb408745d5"
"d23da14695a188ae5708dd152263c4db883eb27edeb936178d4d988b8f3ce5fc"
),
"GENESIS_PRE_FARM_FARMER_PUZZLE_HASH": bytes.fromhex(
"4ad98e66a019f11b60a8279514d13105b65588865e2a8e794b5b73d5646174f6"
"3d8765d3a597ec1d99663f6c9816d915b9f68613ac94009884c4addaefcce6af"
),
"MAX_VDF_WITNESS_SIZE": 64,
# Target tx count per sec

View File

@ -727,7 +727,7 @@ class WalletRpcApi:
fee_amount = 0
last_height_farmed = 0
for record in tx_records:
height = record.height_farmed()
height = record.height_farmed(self.service.constants.GENESIS_CHALLENGE)
if height > last_height_farmed:
last_height_farmed = height
if record.type == TransactionType.COINBASE_REWARD:

View File

@ -14,8 +14,10 @@ network_overrides: &network_overrides
GENESIS_CHALLENGE: null
testnet0:
MIN_PLOT_SIZE: 18
GENESIS_CHALLENGE: "0x0000000000000000000000000000000000000000000000000000000000000000"
GENESIS_CHALLENGE: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
NETWORK_TYPE: 1
GENESIS_PRE_FARM_POOL_PUZZLE_HASH: "d23da14695a188ae5708dd152263c4db883eb27edeb936178d4d988b8f3ce5fc"
GENESIS_PRE_FARM_FARMER_PUZZLE_HASH: "3d8765d3a597ec1d99663f6c9816d915b9f68613ac94009884c4addaefcce6af"
testnet5:
GENESIS_CHALLENGE: f4c0c9d5b3cd59d6f7c76ecd780e646a2c55064367d0ddbb42b2b6462a4674b2
NETWORK_TYPE: 1

View File

@ -1,11 +1,11 @@
from dataclasses import dataclass
from typing import List, Optional, Tuple
from src.consensus.coinbase import pool_parent_id, farmer_parent_id
from src.types.blockchain_format.coin import Coin
from src.types.blockchain_format.sized_bytes import bytes32
from src.types.mempool_inclusion_status import MempoolInclusionStatus
from src.types.spend_bundle import SpendBundle
from src.util.hash import std_hash
from src.util.ints import uint8, uint32, uint64
from src.util.streamable import Streamable, streamable
from src.wallet.util.transaction_type import TransactionType
@ -45,17 +45,17 @@ class TransactionRecord(Streamable):
# Note, transactions pending inclusion (pending) return false
return False
def height_farmed(self) -> Optional[uint32]:
def height_farmed(self, genesis_challenge) -> Optional[uint32]:
if not self.confirmed:
return None
if self.type == TransactionType.FEE_REWARD or self.type == TransactionType.COINBASE_REWARD:
for block_index in range(self.confirmed_at_height, self.confirmed_at_height - 100, -1):
if block_index < 0:
return None
block_index_as_hash = bytes32(block_index.to_bytes(32, "big"))
block_index_as_hash_2 = std_hash(std_hash(block_index.to_bytes(4, "big")))
if block_index_as_hash == self.additions[0].parent_coin_info:
pool_parent = pool_parent_id(uint32(block_index), genesis_challenge)
farmer_parent = farmer_parent_id(uint32(block_index), genesis_challenge)
if pool_parent == self.additions[0].parent_coin_info:
return uint32(block_index)
if block_index_as_hash_2 == self.additions[0].parent_coin_info:
if farmer_parent == self.additions[0].parent_coin_info:
return uint32(block_index)
return None

View File

@ -14,6 +14,7 @@ from cryptography.fernet import Fernet
from src import __version__
from src.consensus.block_record import BlockRecord
from src.consensus.coinbase import pool_parent_id, farmer_parent_id
from src.consensus.constants import ConsensusConstants
from src.consensus.find_fork_point import find_fork_point_in_chain
from src.full_node.weight_proof import WeightProofHandler
@ -529,14 +530,18 @@ class WalletStateManager:
if prev is not None:
# include last block
pool_rewards.add(bytes32(prev.height.to_bytes(32, "big")))
farmer_rewards.add(std_hash(std_hash(prev.height)))
pool_parent = pool_parent_id(uint32(prev.height), self.constants.GENESIS_CHALLENGE)
farmer_parent = farmer_parent_id(uint32(prev.height), self.constants.GENESIS_CHALLENGE)
pool_rewards.add(pool_parent)
farmer_rewards.add(farmer_parent)
prev = await self.blockchain.get_block_record_from_db(prev.prev_hash)
while prev is not None:
# step 2 traverse from previous block to the block before it
pool_rewards.add(bytes32(prev.height.to_bytes(32, "big")))
farmer_rewards.add(std_hash(std_hash(prev.height)))
pool_parent = pool_parent_id(uint32(prev.height), self.constants.GENESIS_CHALLENGE)
farmer_parent = farmer_parent_id(uint32(prev.height), self.constants.GENESIS_CHALLENGE)
pool_rewards.add(pool_parent)
farmer_rewards.add(farmer_parent)
if prev.is_transaction_block:
break
prev = await self.blockchain.get_block_record_from_db(prev.prev_hash)

View File

@ -36,14 +36,13 @@ def get_future_reward_coins(block: FullBlock) -> Tuple[Coin, Coin]:
assert block.transactions_info is not None
farmer_amount = uint64(farmer_amount + block.transactions_info.fees)
pool_coin: Coin = create_pool_coin(
block.height,
block.foliage.foliage_block_data.pool_target.puzzle_hash,
pool_amount,
block.height, block.foliage.foliage_block_data.pool_target.puzzle_hash, pool_amount, constants.GENESIS_CHALLENGE
)
farmer_coin: Coin = create_farmer_coin(
block.height,
block.foliage.foliage_block_data.farmer_reward_puzzle_hash,
farmer_amount,
constants.GENESIS_CHALLENGE,
)
return pool_coin, farmer_coin