Skip to content
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

Implement delegate_call #80

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

ermalkaleci
Copy link
Contributor

@ermalkaleci ermalkaleci commented Oct 12, 2024

Closes #67

TODO:

  • Update pallet-revive to take address instead of code hash

address public sender;
uint256 public value;

event DidSetVars();
Copy link
Member

Choose a reason for hiding this comment

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

Testing this is irrelevant here? Keeping the fixtures as concise as possible eases debugging them :)

@@ -43,6 +43,7 @@ test_spec!(call, "Caller", "Call.sol");
test_spec!(transfer, "Transfer", "Transfer.sol");
test_spec!(return_data_oob, "ReturnDataOob", "ReturnDataOob.sol");
test_spec!(immutables, "Immutables", "Immutables.sol");
test_spec!(delegate, "Delegate", "Delegate.sol");
Copy link
Member

Choose a reason for hiding this comment

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

This could use some better test coverage, i.e. calling into non-existent accounts, plain accounts, recursively into self (from runtime code and the constructor).

let extcodehash_pointer =
context.build_alloca_at_entry(context.word_type(), "extcodehash_pointer");

context.build_runtime_call(
Copy link
Member

Choose a reason for hiding this comment

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

I think with the current runtime this isn’t necessarily correct (might never be written to, so reading from it is potentially UB).

The reason delegate_call accepts a code hash instead of an address is probably so that people can upload code that they can delegate call into without having to instantiate first. But this isn’t expressible in Solidity (if it was it would be problematic because of the immutables - we assume to be instantiated first). Ideally for Solidity, the runtime API would take care of the code hash image lookup. Moving it into the runtime reduces contract code size and closes the door for related bugs in contracts. But it would need either a change or a new API (to preserve to use case of not requiring to instantiate new code hashes first).

If we decide to stick with the current version of delegate_call, I’d like this PR to wait for #77 to avoid too much churn.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would prefer we change pallet-revive to accept address rather than code hash. I was trying to work with what we have

Copy link
Member

Choose a reason for hiding this comment

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

Yes the pallet doing the address lookup would definitively be preferred here.

FYI the pallet is under construction too, it isn't deployed anywhere. At that stage we are free to make breaking changes however we wish, as long as they are justified.

Copy link
Member

@xermicus xermicus Oct 14, 2024

Choose a reason for hiding this comment

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

Maybe I should have stated that earlier. Both the compiler and the pallet are new, in the works and not yet deployed anywhere. But they are not entirely new code bases. We generally try to adapt the EVM behavior, as to introduce not too many differences from what people expect on EVM :)

So the rule of thumb is, anything that differs should be investigated and changed. It doesn't mean that you have to do it all (even though we can use any helping hands) - if you feel like it is getting too tiring I can open corresponding polkadot-sdk issues and add them to the tracking issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought pallet-revive will work with ink! and rust contracts and thought we need to somehow make revive work around that. If that's not that case then I can open PR on pallet-revive

Copy link
Member

@xermicus xermicus Oct 14, 2024

Choose a reason for hiding this comment

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

ink! will work fine on the revive-runtime since all changes can be reflected in a new ink! version. We need the runtime to align with the incumbent platform and not vice-versa.

Copy link
Member

Choose a reason for hiding this comment

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

I can open PR on pallet-revive

Great, thanks! Just link this discussion there for context. I'm un-opinionated how this should exactly look like - having distinct delegate_call_by_hash and delegate_call_by_address syscalls seems reasonable. Or just remove the ability to delegate call by hashes altogether if it is not required anymore. But I don't know.

Copy link
Member

Choose a reason for hiding this comment

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

CC @athei

Copy link
Member

Choose a reason for hiding this comment

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

With immutables being a thing now does it even make sense to support delegate calling by code hash? We probably just want to remove it and just allow by contract address.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah the nature of immutables turns delegate calling by hash into a potential footgun. I'm in favor of changing it to take an address instead, it's not required for Solidity. Should it for some reason turn out super viable for other languages we can still add it later.

let output_offset = context.safe_truncate_int_to_xlen(output_offset)?;
let output_length = context.safe_truncate_int_to_xlen(output_length)?;

// TODO: What to supply here? Is there a weight to gas?
Copy link
Member

Choose a reason for hiding this comment

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

We need some further investigation on this. Why is gas not a supported parameter in the pallet? Can it be supported? If so, that needs either an issue or PR for the pallet. Otherwise the gas argument should be removed from this function altogether and the reason documented.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah no specific reason. Probably an oversight because we thought it doesn't make sense still you are calling in your own contracts storage anyways which is quite sensitive. We probably want to just make delegate call another call flag. Since we are also only supplying an address instead of a code hash.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we should replace weight (ref_time, proof_size) with just gas on revive. Solidity has no concept of PoV and all weight (substrate) related operations should be handled by pallet-revive (gas to weight and so on)

Copy link
Member

Choose a reason for hiding this comment

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

It cannot really be handled by pallet-revive alone. Since Solidity has no concept of it we have to assume some default limits for proof size and storage deposit limit. As of right now the default is just "use all of the remaining". This is the most compatible but doesn't allow proper limiting. The plan is to expose the extended interface via pre-compile to allow Solidity contracts to limit all dimensions.

That being said, we decided to leave all parameters on the host function and revive just fills them out with 0. This way other languages can at least use them.

Copy link
Member

@xermicus xermicus Oct 16, 2024

Choose a reason for hiding this comment

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

Yes you are right, for Solidity, ideally we just pass through what we get and let the pallet handle it. For other languages however this might be undesirable since they can just decide to not ignore it. Mapping everything it into the 1D (ref_time) space implies wasting block-space. Which is especially undesired for other languages like Rust/ink! because we expect people to use those for implementing more compute heavy logic. So to me it seems that ideally we'd have both. Or, since this is a pointer anyways, it could point to something like enum Weights { Gas(U256), Native { ref_time: u64, proof_size: u64 } }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

True but I don't see how we can match 1 unit of gas with 1 unit of time (we may come close though). Anyway renaming to gas_limit and storage_limit makes more sense to me.

Copy link
Member

Choose a reason for hiding this comment

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

True but I don't see how we can match 1 unit of gas with 1 unit of time (we may come close though)

We are doing this: 1 gas = 1 ref_time = 1 picosecond of execution on the reference machine

Copy link
Member

@xermicus xermicus Oct 17, 2024

Choose a reason for hiding this comment

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

Why renaming it? I don't see how just renaming anything would help in any way?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why renaming it?

To make it less confusing. Just an idea, I am fine either way

Copy link
Member

Choose a reason for hiding this comment

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

Ok I see but gas_limit and storage_limit are terms that neither exist in EVM nor in Substrate :) I think that this would only increase the confusion.

let output_length_pointer = context.build_alloca_at_entry(context.xlen_type(), "output_length");
context.build_store(output_length_pointer, output_length)?;

let flags = context.xlen_type().const_int(0u64, false);
Copy link
Member

Choose a reason for hiding this comment

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

Delegate calls can be reentrant.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Member

@xermicus xermicus Oct 14, 2024

Choose a reason for hiding this comment

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

Ah okay. Would be good to know the reason for this and document it :) I mean ideally this would be changed in the pallet to better match the EVM semantics.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

At first I thought pallet-revive was updated to fully support revive but now it seems more just a copy of pallet-contracts

Copy link
Member

@xermicus xermicus Oct 14, 2024

Choose a reason for hiding this comment

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

Yeah, pallet-revive was started as a fork of pallet-contracts :) Since the contracts pallet is already close to what we need and heavily audited, we decided to fork it instead of starting from scratch.

],
);

context.build_byte_swap(context.build_load(extcodehash_pointer, "extcodehash_value")?)?;
Copy link
Member

Choose a reason for hiding this comment

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

Why is this needed?

_gas: inkwell::values::IntValue<'ctx>,
_address: inkwell::values::IntValue<'ctx>,
address: inkwell::values::IntValue<'ctx>,
_value: Option<inkwell::values::IntValue<'ctx>>,
Copy link
Member

Choose a reason for hiding this comment

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

Should be removed.

@ermalkaleci ermalkaleci marked this pull request as draft October 17, 2024 14:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support the delegatecall opcode
3 participants