New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/evm #1252
Feature/evm #1252
Conversation
runtime/development/src/lib.rs
Outdated
@@ -1696,6 +1704,81 @@ impl pallet_liquidity_rewards::Config for Runtime { | |||
type WeightInfo = (); | |||
} | |||
|
|||
const WEIGHT_PER_GAS: u64 = 20_000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is copied directly from the Frontier template project - TODO is to determine if this is a good value (large enough to be safe without wasting block time)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may be a good idea to check how Moonbeam does this
runtime/development/src/lib.rs
Outdated
} | ||
} | ||
|
||
impl pallet_base_fee::Config for Runtime { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All again copied from the template project
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would move the evm setup into its seperate file, just like XCM stuff.
And are there some notes on what we add here and what is the reasoning?
Like what is the usage of those two:
- node-side frontier tasks
- node-side import queue
Regarding the service:
- Do you think it is possible to create a seperate command, that will only then use an start node impl that includes all the EVM stuff?
Overall, I think it makes sense to let this only life in a feature branch. It seems to be breaking too much stuff on the service that I would not want to add to the production env. WDYTY?
{ | ||
fn execute(&self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> { | ||
// 1025 is chosen for compatibility with Moonbeam | ||
if handle.code_address() == addr(1025) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you reuse this, to avoid duplicating the addr(1025)?
if handle.code_address() == addr(1025) { | |
if self::is_precompile(handle.code_address().into()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be a const
anyway ;)
I'd rather not rely on is_precompile
in execute
since we'll need to rewrite it again to use a match on addresses when we add more precompiles in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, my point is mostly about having to touch in two places (might oversee it) when updating that constant. But I do not know enough of how is the is_precompile
used or will be used.
runtime/development/src/lib.rs
Outdated
type WithdrawOrigin = EnsureAddressTruncated; | ||
} | ||
|
||
impl pallet_evm_chain_id::Config for Runtime {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is the chainId value set for an ongoing running chain? Manual raw storage set?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It comes from genesis. For adding EVM to a running chain we'll need to implement a migration (we'll also need that to define the "EVM Genesis Block" which will be based on the first block which contains EVM support. Lots of fun :) )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it 👍
/// Extrinsic type that has already been checked. | ||
pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, RuntimeCall, SignedExtra>; | ||
pub type CheckedExtrinsic = | ||
fp_self_contained::CheckedExtrinsic<AccountId, RuntimeCall, SignedExtra, H160>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would still be possible to validate calls the non-evm way? I am wondering on changes needed on clients like GSRPC
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fp_self_contained::CheckedExtrinsic
is pretty much a transparent wrapper around generic::CheckedExtrinsic
that just exposes the necessary trait impls for handling EVM calls in the runtime API. I looked into the code to see if it would break SCALE-encoded compatibility with the existing Extrinsic/Call structures, and AFAICT it's safe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, and I assume the extrinsic payload to be signed is the same? Or a client would need to form the extrinsic to be signed differently?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! thanks @branan !
{ | ||
fn execute(&self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> { | ||
// 1025 is chosen for compatibility with Moonbeam | ||
if handle.code_address() == addr(1025) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, my point is mostly about having to touch in two places (might oversee it) when updating that constant. But I do not know enough of how is the is_precompile
used or will be used.
runtime/development/src/lib.rs
Outdated
type WithdrawOrigin = EnsureAddressTruncated; | ||
} | ||
|
||
impl pallet_evm_chain_id::Config for Runtime {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it 👍
/// Extrinsic type that has already been checked. | ||
pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, RuntimeCall, SignedExtra>; | ||
pub type CheckedExtrinsic = | ||
fp_self_contained::CheckedExtrinsic<AccountId, RuntimeCall, SignedExtra, H160>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, and I assume the extrinsic payload to be signed is the same? Or a client would need to form the extrinsic to be signed differently?
I'm working on rebasing this over the 0.9.37 work at the same time as shuffling the code around - there are major conflicts in the changes to services code, and I don't wan to do two refactors. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@branan do you have an ETA when we can merge this into main with a split node implementation?
What is left to do for that?
Node and runtime EVM code has been split into separate modules now, and I've done a few more small cleanups. I'll ticket the remaining things to "productionize" the code, but otherwise this should be good to land as soon as 0.9.37 is merged. Tomorrow, if all the release shenanigans go correctly? For now this is targeted at the 0.9.37 branch so that the diff is readable/manageable. It will rebase cleanly onto |
Nix build failure is the same as 0.9.37 branch |
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The split looks good. Thanks @branan!
* Actually create a genesis block * use Parachain sync strategy
@offerijns @NunoAlexandre @lemunozm This is ready for final review for merge into the dev environment. There's still some work to do for making it production-ready for other runtimes, but it's in a state where things that API consumers will interact with are stable. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A very fast review, but I feel like I need to stay a long time to make a useful review. I'll try to do this tomorrow.
Could you link any related spec documents about this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left many comments but no blockers but no blockers as nothing pops up as wrong. I am, however, a bit nervous to merge this amount of code without any added test coverage, which I would love to see in place, at the very least to test account version between the Substrate and the Evm sides 👀
|
||
pub mod precompile; | ||
|
||
pub struct FindAuthorTruncated<F>(PhantomData<F>); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we document this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, should be documented in a comment here and in the spec. Will do 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose all of the minor open TODOs will be handled in a subsequent PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wischli Yeah. Most of the TODOs are "make things configurable" or "understand this enough to decide for ourselves instead of copying the template". That's all part of what I've been calling the "productionization" work.
} | ||
|
||
#[derive(Encode, Decode, Default)] | ||
struct EthereumAccount(H160); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this module is called evm
I'd suggest we rename this to just Account
or Address
:
struct EthereumAccount(H160); | |
struct Account(H160); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Name is leftover from before it was moved to it own module. Totally agree
|
||
pub struct ExtendedAddressMapping; | ||
|
||
impl AddressMapping<AccountId> for ExtendedAddressMapping { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This converts accounts of two different networks, it would be cool to refer to AccountId
with a full path to make that more obvious. Like fn into_account_id(address: H160) -> cfg_types::AccountId {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's the same network, just two different namespaces within the network - we are expanding an H160 ethereum account into an H256 substrate account. But both are still "centrifuge accounts".
The AddressMapping trait is part of the EVM pallet interface, but I will see what I can do to add comments or more descriptive parameter names to make it clearer what is going on.
} | ||
|
||
fn ideal() -> Permill { | ||
Permill::from_parts(500_000) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where do we get these from?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Totally copy and pasted from the frontier template. These constants are on the list of "things to understand before it lands in production". But I don't think it needs to be locked down to land it in dev
} | ||
} | ||
|
||
pub struct BaseFeeThreshold; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be cool to document all of these actually
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I honestly don't 100% understand the fee pallets, but I'll do my best
@@ -1764,6 +1773,12 @@ construct_runtime!( | |||
Migration: pallet_migration_manager::{Pallet, Call, Storage, Event<T>} = 199, | |||
// admin stuff | |||
Sudo: pallet_sudo::{Pallet, Call, Config<T>, Storage, Event<T>} = 200, | |||
|
|||
// EVM pallets | |||
EVM: pallet_evm::{Pallet, Config, Call, Storage, Event<T>} = 160, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that we are indexing the evm pallets with a lower number than pallets above that; is that intentional? Shouldn't we move them up to be sorted ASC?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I picked a random range that didn't overlap with anything else. I don't really know how we should organize pallet IDs in our runtime - it's only a u8, so the range is very limited.
@wischli do you have thoughts or feelings here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's fine but I would still move it up to match the order
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we should move it up in this case. However, we should always be careful with these things as the order of the pallet in the construct_runtime
macro decide the order of the on_initialize
(in-order) and on_finalize
(reverse of order) executions.
So for the crucial part, the indices are irrelevant. Moreover, indices should not be swapped and most ideally not be reused. The later part will be interesting to see in the future, given we only have u8
supported. But Kusama/Polkadot will hit this limitation much earlier 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been trying a bit to think about how the separation of "API stability" and "ABI stability" that I'm familiar with from the C world map to Substrate - things like indices I think are a big part of this. Let's chat about it some this week if we have time :)
@@ -2014,6 +2112,160 @@ impl_runtime_apis! { | |||
} | |||
} | |||
|
|||
// Frontier APIs | |||
impl fp_rpc::EthereumRuntimeRPCApi<Block> for Runtime { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we move this to evm.rs
or rather not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, I missed this in the move commit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, on second look I'm not sure - these are part of an impl_runtime_apis
macro invocation Can we have more than one of those?
@@ -1084,6 +1088,10 @@ fn development_genesis( | |||
parachain_system: Default::default(), | |||
treasury: Default::default(), | |||
interest_accrual: Default::default(), | |||
base_fee: Default::default(), | |||
evm_chain_id: development_runtime::EVMChainIdConfig { chain_id: 999_999 }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really want this value? Can't we set it to 2031 (Centrifuge chain id) for example?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the development runtime I don't think it matters. For production on Altair and Centrifuge we'll use the 2088/2031 parachain IDs (both of those are still available as evm IDs)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if for testing it would be helpful to have it the same as Centrifuge to reduce the number of things changing between the development and the centrifuge runtimes but if you don't think it's something for us to be concerned with that's fine 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally test networks have different IDs in the Ethereum ecosystem. That would mean even Catalyst should have a unique ID from the production Centrifuge chain. It is a 64-bit ID, so there is lots of room in the namespace.
I think it's better that any code we write (even tests) be chain-id-agnostic, rather than breaking the convention and reusing production chain IDs for local networks
|
||
use std::{collections::BTreeMap, sync::Arc}; | ||
|
||
// Frontier |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Frontier |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leftover from moving the code out of the shared RPC namespace. Will remove.
use fp_rpc::{ConvertTransaction, ConvertTransactionRuntimeApi, EthereumRuntimeRPCApi}; | ||
use fp_storage::EthereumStorageSchema; | ||
use jsonrpsee::RpcModule; | ||
// Substrate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Substrate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as above
Description
EVM-compatible API/RPC enablement
Currently the ethereum chain ID is hardcoded to "999999", if anyone wants to try to poke it with web3.js
Todo for safe deployment publicly:
Changes and Descriptions
TODO
Type of change
How Has This Been Tested?
Manual validation of RPC calls:
ethereum
pallet storage contains block infoevm
pallet extrinsics to deploy and run a smart contracteth
Substrate RPCs can query block dataChecklist:
main
branch