-
Notifications
You must be signed in to change notification settings - Fork 21.5k
Closed
Description
It seems delegatecall is broken between 1.9.1 and 1.9.2. Attached code works in 1.9.1, but does not in 1.9.2.
System information
Version: 1.9.2-stable
Git Commit: e76047e9f5499b58064bddde514dd3119a090adf
Architecture: amd64
Protocol Versions: [63]
Network Id: 1
Go Version: go1.12
Operating System: linux
Expected behaviour
delegatecall should work, of course.
Actual behaviour
delegatecall fails with "out of gas" error.
Steps to reproduce the behaviour
With private geth node and the o_o.js below, in geth console,
personal.unlockAccount(eth.accounts[0], '<password here>', 360000)
loadScript('o_o.js')
inner = Inner_new()
outer = Outer_new()
miner.start(1); admin.sleepBlocks(1); miner.stop()
outer.setImplementation(inner.address, {from:eth.accounts[0]})
tmp = web3.eth.contract(Inner_contract.abi).at(outer.address)
miner.start(1); admin.sleepBlocks(1); miner.stop()
tmp.value()
tx = tmp.set(100, {from:eth.accounts[0], gas:100000})
miner.start(1); admin.sleepBlocks(1); miner.stop()
tmp.value()
debug.traceTransaction(tx)
Backtrace
debug.traceTransaction(<transaction hash>) in 1.9.2 shows that "DELEGATECALL" mishandles gasCost as shown below.
... {
depth: 1,
error: {},
gas: 78152,
gasCost: 78852,
memory: ["0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000080", "0000000000000000000000000000000000000000000000000000000000000000", "60fe47b100000000000000000000000000000000000000000000000000000000", "0000006400000000000000000000000000000000000000000000000000000000"],
op: "DELEGATECALL",
pc: 128,
stack: ["0000000000000000000000000000000000000000000000000000000060fe47b1", "0000000000000000000000003bc5d3aa07701b08bd11bdc1d41fb44cefb355f9", "0000000000000000000000000000000000000000000000000000000000000080", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000024", "0000000000000000000000000000000000000000000000000000000000000080", "0000000000000000000000003bc5d3aa07701b08bd11bdc1d41fb44cefb355f9", "0000000000000000000000000000000000000000000000000000000000013148"],
storage: {}
}]
In 1.9.1, it gets handled properly.
{
depth: 1,
gas: 78152,
gasCost: 76942,
memory: ["0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000080", "0000000000000000000000000000000000000000000000000000000000000000", "60fe47b100000000000000000000000000000000000000000000000000000000", "0000006400000000000000000000000000000000000000000000000000000000"],
op: "DELEGATECALL",
pc: 128,
stack: ["0000000000000000000000000000000000000000000000000000000060fe47b1", "0000000000000000000000003bc5d3aa07701b08bd11bdc1d41fb44cefb355f9", "0000000000000000000000000000000000000000000000000000000000000080", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000024", "0000000000000000000000000000000000000000000000000000000000000080", "0000000000000000000000003bc5d3aa07701b08bd11bdc1d41fb44cefb355f9", "0000000000000000000000000000000000000000000000000000000000013148"], storage: {}
}
Code in solidity and .js format
o_o.sol
pragma solidity ^0.4.24;
// modified from "github.com:/OpenZeppelin/openzeppelin-labs/upgradeability_using_eternal_storage/contracts/Proxy.sol"
contract Proxy {
address _impl;
constructor() public {}
function () public payable {
address addr = _impl;
require(_impl != address(0));
assembly {
let ptr := mload(0x40)
calldatacopy(ptr, 0, calldatasize)
let result := delegatecall(gas, addr, ptr, calldatasize, 0, 0)
let size := returndatasize
returndatacopy(ptr, 0, size)
switch result
case 0 { revert(ptr, size) }
default { return(ptr, size) }
}
}
function setImplementation(address newImpl) public {
require(newImpl != address(0), "newImplementation should be non-zero");
_impl = newImpl;
}
function implementation() public view returns (address) {
return _impl;
}
}
contract Outer is Proxy {
constructor() public {}
}
contract Inner is Proxy{
uint public value;
function set(uint v) public {
value = v;
}
}
o_o.js
var Inner_data = "0x6080604052610290806100136000396000f3006080604052600436106100615763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416633fa4f24581146100ab5780635c60da1b146100d257806360fe47b114610110578063d784d4261461012a575b60005473ffffffffffffffffffffffffffffffffffffffff1680151561008657600080fd5b60405136600082376000803683855af43d806000843e8180156100a7578184f35b8184fd5b3480156100b757600080fd5b506100c0610158565b60408051918252519081900360200190f35b3480156100de57600080fd5b506100e761015e565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b34801561011c57600080fd5b5061012860043561017a565b005b34801561013657600080fd5b5061012873ffffffffffffffffffffffffffffffffffffffff6004351661017f565b60015481565b60005473ffffffffffffffffffffffffffffffffffffffff1690565b600155565b73ffffffffffffffffffffffffffffffffffffffff8116151561022857604080517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f6e6577496d706c656d656e746174696f6e2073686f756c64206265206e6f6e2d60448201527f7a65726f00000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b6000805473ffffffffffffffffffffffffffffffffffffffff191673ffffffffffffffffffffffffffffffffffffffff929092169190911790555600a165627a7a72305820e2312556d728efd4b50ba1f9a883053dbe2302e1c03909adae101584810704bd0029";
var Inner_contract = web3.eth.contract([{"constant":true,"inputs":[],"name":"value","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"v","type":"uint256"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newImpl","type":"address"}],"name":"setImplementation","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"}]);
function Inner_new() {
return Inner_contract.new(
{
from: web3.eth.accounts[0],
data: Inner_data,
gas: "0x10000000"
}, function (e, contract) {
if (typeof contract.address !== "undefined") {
console.log("Contract mined! address: " + contract.address + " transactionHash: " + contract.transactionHash);
}
});
}
function Inner_load(addr) {
return Inner_contract.at(addr);
}
var Outer_data = "0x608060405234801561001057600080fd5b50610230806100206000396000f30060806040526004361061004b5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416635c60da1b8114610095578063d784d426146100d3575b60005473ffffffffffffffffffffffffffffffffffffffff1680151561007057600080fd5b60405136600082376000803683855af43d806000843e818015610091578184f35b8184fd5b3480156100a157600080fd5b506100aa610103565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b3480156100df57600080fd5b5061010173ffffffffffffffffffffffffffffffffffffffff6004351661011f565b005b60005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff811615156101c857604080517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f6e6577496d706c656d656e746174696f6e2073686f756c64206265206e6f6e2d60448201527f7a65726f00000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b6000805473ffffffffffffffffffffffffffffffffffffffff191673ffffffffffffffffffffffffffffffffffffffff929092169190911790555600a165627a7a7230582083dd011c230446c2cc9925ebe5be1ab6101ff5452dd31dc4449bcc8012668d700029";
var Outer_contract = web3.eth.contract([{"constant":true,"inputs":[],"name":"implementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newImpl","type":"address"}],"name":"setImplementation","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"}]);
function Outer_new() {
return Outer_contract.new(
{
from: web3.eth.accounts[0],
data: Outer_data,
gas: "0x10000000"
}, function (e, contract) {
if (typeof contract.address !== "undefined") {
console.log("Contract mined! address: " + contract.address + " transactionHash: " + contract.transactionHash);
}
});
}
function Outer_load(addr) {
return Outer_contract.at(addr);
}
var Proxy_data = "0x608060405234801561001057600080fd5b50610230806100206000396000f30060806040526004361061004b5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416635c60da1b8114610095578063d784d426146100d3575b60005473ffffffffffffffffffffffffffffffffffffffff1680151561007057600080fd5b60405136600082376000803683855af43d806000843e818015610091578184f35b8184fd5b3480156100a157600080fd5b506100aa610103565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b3480156100df57600080fd5b5061010173ffffffffffffffffffffffffffffffffffffffff6004351661011f565b005b60005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff811615156101c857604080517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f6e6577496d706c656d656e746174696f6e2073686f756c64206265206e6f6e2d60448201527f7a65726f00000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b6000805473ffffffffffffffffffffffffffffffffffffffff191673ffffffffffffffffffffffffffffffffffffffff929092169190911790555600a165627a7a72305820388ccb391aae6d68a74b734b2810658d77ebed9bc231214a8912a652ea64ae700029";
var Proxy_contract = web3.eth.contract([{"constant":true,"inputs":[],"name":"implementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newImpl","type":"address"}],"name":"setImplementation","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"}]);
function Proxy_new() {
return Proxy_contract.new(
{
from: web3.eth.accounts[0],
data: Proxy_data,
gas: "0x10000000"
}, function (e, contract) {
if (typeof contract.address !== "undefined") {
console.log("Contract mined! address: " + contract.address + " transactionHash: " + contract.transactionHash);
}
});
}
function Proxy_load(addr) {
return Proxy_contract.at(addr);
}
Metadata
Metadata
Assignees
Labels
No labels