Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Add block reward contract config to ethash and allow off-chain contracts #9312

Merged
merged 11 commits into from
Aug 29, 2018

Conversation

sorpaas
Copy link
Collaborator

@sorpaas sorpaas commented Aug 8, 2018

This adds block reward contract config to ethash. A new config blockRewardContractCode is also added to both Aura and ethash. When specified, it will execute the code directly and overrides any blockRewardContractAddress config. Having this blockRewardContractCode config allows chains to deploy hard fork by simply replacing the current config value, without the need from us to support any multi block reward scheme.

One interface issue I faced was RewardKind::Uncle. Most ethash uncles reward are calculated according to its depth, but we didn't have that information. So I added RewardKind::UncleWithDepth(u8) for this purpose. To keep backward compatibility, removing RewardKind::Uncle might not be an option. UncleWithDepth uses the reward kind index from 100 to 355, where 101 represents uncle with depth 1, and 102 represents uncle with depth 2, etc. Technically, we only use 101-106 right now, and all others are invalid. cc @andresilva. Not sure whether there're better ways to do this.

cc @eosclassicteam (#9163) @yograterol (#9283). If you could rewrite your PR on_close_block using a reward contract, that would certainly speed up merging it! Examples of block reward contracts can be found here https://github.com/parity-contracts/block-reward/tree/master/contracts

@sorpaas sorpaas added A0-pleasereview 🤓 Pull request needs code review. M4-core ⛓ Core client code / Rust. labels Aug 8, 2018
@sorpaas sorpaas added this to the 2.1 milestone Aug 8, 2018
@sorpaas sorpaas requested a review from andresilva August 8, 2018 12:02
@phahulin
Copy link
Contributor

Hi @sorpaas !
could you please explain in a bit more details how will blockRewardContractCode work, especially in AuRa chains.
As I understand, new parameter blockRewardContractCode will be part of genesis.json chain spec file, so it will go somewhere in "engine" > "authorityRound" > "params" and contain the bytecode to be executed every round, correct? Will it support the same ABI as contract at blockRewardContractAddress?

I don't understand, how will it help to avoid use of "mutil" option. If blockRewardContractCode is part of genesis and we issue a fork, replacing bytecode in genesis with a new version, won't it make all previous blocks invalid?

Thanks!

@sorpaas
Copy link
Collaborator Author

sorpaas commented Aug 21, 2018

@phahulin So when deploying a hard fork, you just replace blockRewardContractCode with the new one. Previously you may have logic like:

Set block reward to 5 ether.

For the new one, you add if statement to check the block number:

If block number is less than FORK_BLOCK,
  Set block reward to 5 ether.
Otherwise,
  Set block reward to 3 ether.

Note that this parameter is entirely optional, and blockRewardContractCode is never written to state (nor it will appear anywhere in genesis block).

Copy link
Collaborator

@dvdplm dvdplm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code LGTM, logic also (but I'm not familiar so don't take my word for it).
Added a few questions and suggestions.

result.map_err(|e| format!("{}", e))
};

let rewards = c.reward(&benefactors, &mut call)?;
let rewards = c.reward(&beneficiaries, &mut call)?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 at this.

I think the wiki also uses "benefactors" instead of "beneficiaries" which was very confusing to me when I read it.

let result = self.machine.execute_as_system(
block,
to,
U256::max_value(), // unbounded gas? maybe make configurable.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment not relevant anymore?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the original comment didn't make sense in this case (I copied it from some other usage of execute_code_as_system 😛). It probably makes sense to execute the block reward contract with unbounded gas. Otherwise, if we cap it and it goes out of gas what do we default to? No rewards for that block? The block reward contract is a critical part of consensus so I think the framework should assume it is correct, in this case we should assume that it will terminate, if it doesn't your chain is broken.

@@ -139,7 +163,7 @@ pub fn apply_block_rewards<M: Machine + WithBalances + WithRewards>(
}

let rewards: Vec<_> = rewards.into_iter().map(|&(a, k, r)| (a, k.into(), r)).collect();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is a little awkward. It would be nice to do this work only if tracing is enabled. I'm not sure what the reason is for having both RewardKind and RewardType – is it possible to unify them? That way this line could go away entirely I think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably possible to unify them, although now they're a bit different since the tracing type doesn't distinguish between uncle depth. Although maybe we can change note_rewards to take RewardKind and handle the conversion itself (only performing it if tracing is enabled)?

Copy link
Collaborator Author

@sorpaas sorpaas Aug 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created a new issue on this #9399

(I would hope that we get the above done after #9360, otherwise it'll be a lot of duplicate interface changes.)

@@ -531,6 +586,8 @@ mod tests {
eip649_reward: None,
expip2_transition: u64::max_value(),
expip2_duration_limit: 30,
block_reward_contract: None,
block_reward_contract_transition: 0,
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a pretty big function. Perhaps it's time to split it up a little?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how we can split it though. I would love some ideas. The issue is just that ethash engine is full of configs/patches and all kinds of different hard forks.

@@ -16,8 +16,9 @@

//! Authority params deserialization.

use ethereum_types::Address;
use hash::Address;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this rename?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously it was not using the right type -- in json, we should use the json serialization/serialization type ($crate::hash::Address) rather than from ethereum_types.

Copy link
Contributor

@andresilva andresilva left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall LGTM, some minor comments. We should also update the wiki on this.

let result = self.machine.execute_as_system(
block,
to,
U256::max_value(), // unbounded gas? maybe make configurable.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the original comment didn't make sense in this case (I copied it from some other usage of execute_code_as_system 😛). It probably makes sense to execute the block reward contract with unbounded gas. Otherwise, if we cap it and it goes out of gas what do we default to? No rewards for that block? The block reward contract is a critical part of consensus so I think the framework should assume it is correct, in this case we should assume that it will terminate, if it doesn't your chain is broken.

@@ -139,7 +163,7 @@ pub fn apply_block_rewards<M: Machine + WithBalances + WithRewards>(
}

let rewards: Vec<_> = rewards.into_iter().map(|&(a, k, r)| (a, k.into(), r)).collect();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably possible to unify them, although now they're a bit different since the tracing type doesn't distinguish between uncle depth. Although maybe we can change note_rewards to take RewardKind and handle the conversion itself (only performing it if tracing is enabled)?

if let Err(e) = ex.call(params, &mut substate, BytesRef::Flexible(&mut output), &mut NoopTracer, &mut NoopVMTracer) {
warn!("Encountered error on making system call: {}", e);
}
ex.call(params, &mut substate, BytesRef::Flexible(&mut output), &mut NoopTracer, &mut NoopVMTracer).map_err(|e| ::engines::EngineError::FailedSystemCall(format!("{}", e)))?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we still keep the warning? Code that's executed as system is usually consensus critical so it's probably helpful to have failures logged (helps with debugging issues like borked contracts).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After #9085, we can make this call error directly throw, and miner/validator would handle emitting the warnings. :)

@@ -133,6 +133,18 @@ pub enum Seal {
/// A system-calling closure. Enacts calls on a block's state from the system address.
pub type SystemCall<'a> = FnMut(Address, Vec<u8>) -> Result<Vec<u8>, String> + 'a;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it makes sense to remove this type? It seems like everywhere SystemCall is supported SystemOrCodeCall should also be? (even if it's not exposed by any parameter).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we are still using SystemCall in several other cases, but I do agree that we may eventually want to clean up this.

rewards.push((author, RewardKind::Author, result_block_reward));
rewards.push((ubi_contract, RewardKind::External, ubi_reward));
rewards.push((dev_contract, RewardKind::External, dev_reward));
let mut call = |to, data| {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can probably refactor this to share the code with AuRa's on_close_block.

External,

/// Reward attributed to the block uncle(s) without any reference of block difference. This is used by all except Ethash engine's reward contract.
Uncle,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's safe to remove this since the block reward contract was only supported in Aura and it doesn't produce uncles, so I don't think anyone is relying on this. Even if their contract code did handle the uncle type, it would never be reached.

@andresilva
Copy link
Contributor

After this is merged it would also be nice to be able to extract the musicoin block reward logic to a contract (which maybe can be used as an example for other people).

@ghost
Copy link

ghost commented Aug 22, 2018

@andresilva @sorpaas ACK on this. we will move our logic if there is an example for musicoin

@varasev
Copy link
Contributor

varasev commented Aug 22, 2018

Hi @sorpaas,

Please clarify the next thing: will it work fine if we use blockRewardContractAddress and blockRewardContractTransition and then we switch to blockRewardContractCode after some time (and after blockRewardContractTransition block is created)?

@sorpaas sorpaas added A3-inprogress ⏳ Pull request is in progress. No review needed at this stage. and removed A0-pleasereview 🤓 Pull request needs code review. labels Aug 22, 2018
@sorpaas
Copy link
Collaborator Author

sorpaas commented Aug 22, 2018

@varasev Yes it will work fine. In that case, you would want to keep blockRewardContractTransition to be the original value, and in the new contract defined by blockRewardContractCode, keep the behavior of reward contracts for old blocks unchanged.

@sorpaas sorpaas added A0-pleasereview 🤓 Pull request needs code review. and removed A3-inprogress ⏳ Pull request is in progress. No review needed at this stage. labels Aug 22, 2018
@varasev
Copy link
Contributor

varasev commented Aug 22, 2018

@sorpaas Thank you for the clarification. What if the new contract code in blockRewardContractCode differs from that old contract defined in blockRewardContractAddress? Would it be possible?

@sorpaas
Copy link
Collaborator Author

sorpaas commented Aug 22, 2018

@varasev Yes! As long as it evaluates to the same reward results for pre-fork blocks.

Copy link
Contributor

@andresilva andresilva left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Wiki https://wiki.parity.io/Block-Reward-Contract posting it here so we don't forget to update it before we release this feature.

@andresilva andresilva merged commit 74ce0f7 into master Aug 29, 2018
@andresilva andresilva added A8-looksgood 🦄 Pull request is reviewed well. and removed A0-pleasereview 🤓 Pull request needs code review. labels Aug 29, 2018
dvdplm added a commit that referenced this pull request Aug 30, 2018
* master:
  evmbin: Fix gas_used issue in state root mismatch and handle output better (#9418)
  Update hardcoded sync (#9421)
  Add block reward contract config to ethash and allow off-chain contracts (#9312)
  Private packets verification and queue refactoring (#8715)
  Update tobalaba.json (#9419)
  docs: add parity ethereum logo to readme (#9415)
  build: update rocksdb crate (#9414)
  Updating the CI system  (#8765)
  Better support for eth_getLogs in light mode (#9186)
  Add update docs script to CI (#9219)
  `gasleft` extern implemented for WASM runtime (kip-6) (#9357)
  block view! removal in progress (#9397)
  Prevent sync restart if import queue full (#9381)
  nonroot CentOS Docker image (#9280)
  ethcore: kovan: delay activation of strict score validation (#9406)
@5chdn 5chdn deleted the sp-block-reward branch August 30, 2018 19:02
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A8-looksgood 🦄 Pull request is reviewed well. M4-core ⛓ Core client code / Rust.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants