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

EIP 7: DELEGATECALL #23

Closed
vbuterin opened this Issue Nov 19, 2015 · 19 comments

Comments

Projects
None yet
@vbuterin
Copy link
Collaborator

vbuterin commented Nov 19, 2015

Overview

Add a new opcode, DELEGATECALL at 0xf4, which is similar in idea to CALLCODE, except that it propagates the sender and value from the parent scope to the child scope, ie. the call created has the same sender and value as the original call.

Specification

DELEGATECALL: 0xf4, takes 6 operands:

  • gas: the amount of gas the code may use in order to execute;
  • to: the destination address whose code is to be executed;
  • in_offset: the offset into memory of the input;
  • in_size: the size of the input in bytes;
  • out_offset: the offset into memory of the output;
  • out_size: the size of the scratch pad for the output.

Notes on Gas

  • The basic stipend is not given; gas is the total amount the callee receives.
  • Like CALLCODE, account creation never happens, so the upfront gas cost is always schedule.callGas + gas.
  • Unused gas is refunded as normal.

Notes on Sender

  • CALLER behaves exactly in the callee's environment as it does in the caller's environment.

Other Notes

  • The depth limit of 1024 is still preserved as normal.

Rationale

Propagating the sender and value from the parent scope to the child scope makes it much easier for a contract to store another address as a mutable source of code and ''pass through'' calls to it, as the child code would execute in essentially the same environment (except for reduced gas and increased callstack depth) as the parent.

Use case 1: split code to get around 3m gas barrier

~calldatacopy(0, 0, ~calldatasize())
if ~calldataload(0) < 2**253:
    ~delegate_call(msg.gas - 10000, $ADDR1, 0, ~calldatasize(), ~calldatasize(), 10000)
    ~return(~calldatasize(), 10000)
elif ~calldataload(0) < 2**253 * 2:
    ~delegate_call(msg.gas - 10000, $ADDR2, 0, ~calldatasize(), ~calldatasize(), 10000)
    ~return(~calldatasize(), 10000)
...

Use case 2: mutable address for storing the code of a contract:

if ~calldataload(0) / 2**224 == 0x12345678 and self.owner == msg.sender:
    self.delegate = ~calldataload(4)
else:
    ~delegate_call(msg.gas - 10000, self.delegate, 0, ~calldatasize(), ~calldatasize(), 10000)
    ~return(~calldatasize(), 10000)

The child functions called by these methods can now freely reference msg.sender and msg.value.

Possible arguments against

  • You can replicate this functionality by just sticking the sender into the first twenty bytes of the call data. However, this would mean that code would need to be specially compiled for delegated contracts, and would not be usable in delegated and raw contexts at the same time.
@alexvandesande

This comment has been minimized.

Copy link
Contributor

alexvandesande commented Nov 19, 2015

This is a very important update for reusable standard libraries

@wanderer

This comment has been minimized.

Copy link
Member

wanderer commented Nov 19, 2015

+1

@nmushegian

This comment has been minimized.

Copy link

nmushegian commented Nov 20, 2015

The library feature was hamstrung because CALLCODE doesn't work like this! +1!

@vbuterin vbuterin changed the title EIP 3: DELEGATE_CALL EIP 4: DELEGATE_CALL Nov 21, 2015

@vbuterin

This comment has been minimized.

Copy link
Collaborator

vbuterin commented Nov 21, 2015

Note that this works well with #8 , as otherwise sub-delegations will have to specify a fairly low return data size limit.

@chriseth

This comment has been minimized.

Copy link
Contributor

chriseth commented Nov 21, 2015

For this to really support call delegation without having to inspect which function is actually called, we also need a way to retrieve the size of the returned data.
In your example - ~return(~calldatasize(), 10000) - we need to pay for returning the full 10000 bytes.

@chriseth

This comment has been minimized.

Copy link
Contributor

chriseth commented Nov 23, 2015

Previous comment is not 100% true, see EIP5 - MSIZE provides a way to retrieve the size of the returned data.

@frozeman frozeman added the EIP label Nov 23, 2015

@gavofyork gavofyork changed the title EIP 4: DELEGATE_CALL EIP 4: DELEGATECALL Nov 30, 2015

@CJentzsch

This comment has been minimized.

Copy link

CJentzsch commented Dec 4, 2015

Should it be symmetric to CALL and CALLCODE?
CALLCODE takes 7 parameters even though value has no meaning, but only for the sake of symmetry. I think we should be consistent, and also take 7 parameters here, or remove one parameter in CALLCODE (but this would break backwards compatibility, and we don't want that)

@wanderer

This comment has been minimized.

Copy link
Member

wanderer commented Dec 4, 2015

@CJentzsch I would rather argue for the remove of value from CALLCODE.

@CJentzsch

This comment has been minimized.

Copy link

CJentzsch commented Dec 4, 2015

@wanderer . I also like to have 6 values instead of 7. But changing CALLCODE is not possible anymore without breaking existing contracts. Therefore I think we have to live with asymmetry ( as a theoretical physicist I hate that ;-) )

@wanderer wanderer changed the title EIP 4: DELEGATECALL EIP 7: DELEGATECALL Dec 4, 2015

@PeterBorah

This comment has been minimized.

Copy link

PeterBorah commented Dec 8, 2015

Just to double-check: Like CALLCODE, this will use the state of the delegator contract, not the delegatee contract, right? If so, this seems like a good idea. If not, it's a pretty big security hole.

@romanman

This comment has been minimized.

Copy link

romanman commented May 16, 2016

@vbuterin that one should be closed right ?

@VoR0220

This comment has been minimized.

Copy link

VoR0220 commented May 19, 2016

can we close this now that this has been done?

@kumavis

This comment has been minimized.

Copy link
Member

kumavis commented Jun 17, 2016

@wanderer @vbuterin @alexvandesande EIP is merged and on mainnet, please close issue

@wanderer wanderer closed this Jun 17, 2016

@kumavis

This comment has been minimized.

Copy link
Member

kumavis commented Jun 17, 2016

👍

@laksh95

This comment has been minimized.

Copy link

laksh95 commented Jan 29, 2018

I have a small doubt here, does delegate call allows the callee contracted to access the msg.sender(ethereum address of the account) of the caller contract?

@VoR0220

This comment has been minimized.

Copy link

VoR0220 commented Jan 29, 2018

yes.

@laksh95

This comment has been minimized.

Copy link

laksh95 commented Jan 30, 2018

@VoR0220 will be available in the msg.sender itself or in some other variable?

@VoR0220

This comment has been minimized.

Copy link

VoR0220 commented Feb 1, 2018

you would have to send it alongside another variable. Also tx.origin could do it as well I suppose. But then you expose your own contract to security faults in there as well.

@ytrezq

This comment has been minimized.

Copy link

ytrezq commented Jun 20, 2018

@CJentzsch actually, when you callcode with value, you really make a transfer to the address containing the code (and not to caller itself).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment