Skip to content
This repository has been archived by the owner on Oct 28, 2021. It is now read-only.

Commit

Permalink
Implement EIP-1702 Generalized Account Versioning Scheme (#5640)
Browse files Browse the repository at this point in the history
Implement EIP-1702 Generalized Account Versioning Scheme
  • Loading branch information
gumb0 authored Jul 10, 2019
2 parents e1f8a1f + 0554648 commit d169557
Show file tree
Hide file tree
Showing 21 changed files with 462 additions and 75 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
- Added: [#5588](https://github.com/ethereum/aleth/pull/5588) Testeth prints similar test suite name suggestions, when the name passed in `-t` argument is not found.
- Added: [#5593](https://github.com/ethereum/aleth/pull/5593) Dynamically updating host ENR.
- Added: [#5624](https://github.com/ethereum/aleth/pull/5624) Remove useless peers from peer list.
- Added: [#5634](https://github.com/ethereum/aleth/pull/5634) Bootnodes for Rinkeby and Goerli
- Added: [#5634](https://github.com/ethereum/aleth/pull/5634) Bootnodes for Rinkeby and Goerli.
- Added: [#5640](https://github.com/ethereum/aleth/pull/5640) Istanbul support: EIP-1702 Generalized Account Versioning Scheme.
- Changed: [#5532](https://github.com/ethereum/aleth/pull/5532) The leveldb is upgraded to 1.22. This is breaking change on Windows and the old databases are not compatible.
- Changed: [#5559](https://github.com/ethereum/aleth/pull/5559) Update peer validation error messages.
- Changed: [#5568](https://github.com/ethereum/aleth/pull/5568) Improve rlpx handshake log messages and create new rlpx log channel.
Expand Down
10 changes: 6 additions & 4 deletions aleth-vm/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,13 +259,18 @@ int main(int argc, char** argv)
} // Ignore decoding errors.
}

unique_ptr<SealEngineFace> se(ChainParams(genesisInfo(networkName)).createSealEngine());
LastBlockHashes lastBlockHashes;
EnvInfo const envInfo(blockHeader, lastBlockHashes, 0);

Transaction t;
Address contractDestination("1122334455667788991011121314151617181920");
if (!code.empty())
{
// Deploy the code on some fake account to be called later.
Account account(0, 0);
account.setCode(bytes{code});
auto const latestVersion = se->evmSchedule(envInfo.number()).accountVersion;
account.setCode(bytes{code}, latestVersion);
std::unordered_map<Address, Account> map;
map[contractDestination] = account;
state.populateFrom(map);
Expand All @@ -278,9 +283,6 @@ int main(int argc, char** argv)

state.addBalance(sender, value);

unique_ptr<SealEngineFace> se(ChainParams(genesisInfo(networkName)).createSealEngine());
LastBlockHashes lastBlockHashes;
EnvInfo const envInfo(blockHeader, lastBlockHashes, 0);
Executive executive(state, envInfo, *se);
ExecutionResult res;
executive.setResultRecipient(res);
Expand Down
2 changes: 2 additions & 0 deletions libethcore/ChainOperationParams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ EVMSchedule const& ChainOperationParams::scheduleForBlockNumber(u256 const& _blo
{
if (_blockNumber >= experimentalForkBlock)
return ExperimentalSchedule;
else if (_blockNumber >= istanbulForkBlock)
return IstanbulSchedule;
else if (_blockNumber >= constantinopleFixForkBlock)
return ConstantinopleFixSchedule;
else if (_blockNumber >= constantinopleForkBlock)
Expand Down
24 changes: 23 additions & 1 deletion libethcore/EVMSchedule.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ struct EVMSchedule
{
EVMSchedule(): tierStepGas(std::array<unsigned, 8>{{0, 2, 3, 5, 8, 10, 20, 0}}) {}
EVMSchedule(bool _efcd, bool _hdc, unsigned const& _txCreateGas): exceptionalFailedCodeDeposit(_efcd), haveDelegateCall(_hdc), tierStepGas(std::array<unsigned, 8>{{0, 2, 3, 5, 8, 10, 20, 0}}), txCreateGas(_txCreateGas) {}
unsigned accountVersion = 0;
bool exceptionalFailedCodeDeposit = true;
bool haveDelegateCall = true;
bool eip150Mode = false;
Expand Down Expand Up @@ -146,10 +147,31 @@ static const EVMSchedule ConstantinopleFixSchedule = [] {
return schedule;
}();

static const EVMSchedule IstanbulSchedule = [] {
EVMSchedule schedule = ConstantinopleFixSchedule;
schedule.accountVersion = 1;
return schedule;
}();

static const EVMSchedule ExperimentalSchedule = [] {
EVMSchedule schedule = ConstantinopleSchedule;
EVMSchedule schedule = IstanbulSchedule;
schedule.blockhashGas = 800;
return schedule;
}();

inline EVMSchedule const& latestScheduleForAccountVersion(u256 const& _version)
{
if (_version == 0)
return ConstantinopleFixSchedule;
else if (_version == IstanbulSchedule.accountVersion)
return IstanbulSchedule;
else
{
// This should not happen, as all existing accounts
// are created either with version 0 or with one of fork's versions
assert(false);
return DefaultSchedule;
}
}
}
}
9 changes: 6 additions & 3 deletions libethereum/Account.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ using namespace dev::eth::validation;

namespace fs = boost::filesystem;

void Account::setCode(bytes&& _code)
void Account::setCode(bytes&& _code, u256 const& _version)
{
auto const newHash = sha3(_code);
if (newHash != m_codeHash)
Expand All @@ -43,13 +43,16 @@ void Account::setCode(bytes&& _code)
m_hasNewCode = true;
m_codeHash = newHash;
}
m_version = _version;
}

void Account::resetCode()
{
m_codeCache.clear();
m_hasNewCode = false;
m_codeHash = EmptySHA3;
// Reset the version, as it was set together with code
m_version = 0;
}

u256 Account::originalStorageValue(u256 const& _key, OverlayDB const& _db) const
Expand Down Expand Up @@ -166,7 +169,7 @@ AccountMap dev::eth::jsonToAccountMap(std::string const& _json, u256 const& _def
cerr << "Error importing code of account " << a
<< "! Code needs to be hex bytecode prefixed by \"0x\".";
else
ret[a].setCode(fromHex(codeStr));
ret[a].setCode(fromHex(codeStr), 0);
}
else
cerr << "Error importing code of account " << a
Expand All @@ -186,7 +189,7 @@ AccountMap dev::eth::jsonToAccountMap(std::string const& _json, u256 const& _def
if (code.empty())
cerr << "Error importing code of account " << a << "! Code file "
<< codePath << " empty or does not exist.\n";
ret[a].setCode(std::move(code));
ret[a].setCode(std::move(code), 0);
}
else
cerr << "Error importing code of account " << a
Expand Down
26 changes: 21 additions & 5 deletions libethereum/Account.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,23 @@ class Account
/// Construct a dead Account.
Account() {}

/// Construct an alive Account, with given endowment, for either a normal (non-contract) account or for a
/// contract account in the
/// conception phase, where the code is not yet known.
/// Construct an alive Account, with given endowment, for either a normal (non-contract) account
/// or for a contract account in the conception phase, where the code is not yet known.
Account(u256 _nonce, u256 _balance, Changedness _c = Changed): m_isAlive(true), m_isUnchanged(_c == Unchanged), m_nonce(_nonce), m_balance(_balance) {}

/// Explicit constructor for wierd cases of construction or a contract account.
Account(u256 _nonce, u256 _balance, h256 _contractRoot, h256 _codeHash, Changedness _c): m_isAlive(true), m_isUnchanged(_c == Unchanged), m_nonce(_nonce), m_balance(_balance), m_storageRoot(_contractRoot), m_codeHash(_codeHash) { assert(_contractRoot); }
Account(u256 const& _nonce, u256 const& _balance, h256 const& _contractRoot,
h256 const& _codeHash, u256 const& _version, Changedness _c)
: m_isAlive(true),
m_isUnchanged(_c == Unchanged),
m_nonce(_nonce),
m_balance(_balance),
m_storageRoot(_contractRoot),
m_codeHash(_codeHash),
m_version(_version)
{
assert(_contractRoot);
}


/// Kill this account. Useful for the suicide opcode. Following this call, isAlive() returns
Expand All @@ -88,6 +98,7 @@ class Account
m_storageRoot = EmptyTrie;
m_balance = 0;
m_nonce = 0;
m_version = 0;
changed();
}

Expand Down Expand Up @@ -171,7 +182,7 @@ class Account
bool hasNewCode() const { return m_hasNewCode; }

/// Sets the code of the account. Used by "create" messages.
void setCode(bytes&& _code);
void setCode(bytes&& _code, u256 const& _version);

/// Reset the code set by previous setCode
void resetCode();
Expand All @@ -183,6 +194,8 @@ class Account
/// @returns the account's code.
bytes const& code() const { return m_codeCache; }

u256 version() const { return m_version; }

private:
/// Note that we've altered the account.
void changed() { m_isUnchanged = false; }
Expand Down Expand Up @@ -214,6 +227,9 @@ class Account
*/
h256 m_codeHash = EmptySHA3;

/// Account's version
u256 m_version = 0;

/// The map with is overlaid onto whatever storage is implied by the m_storageRoot in the trie.
mutable std::unordered_map<u256, u256> m_storageOverlay;

Expand Down
3 changes: 2 additions & 1 deletion libethereum/Block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,8 @@ void Block::updateBlockhashContract()
if (blockNumber == forkBlock)
{
m_state.createContract(c_blockhashContractAddress);
m_state.setCode(c_blockhashContractAddress, bytes(c_blockhashContractCode));
m_state.setCode(c_blockhashContractAddress, bytes(c_blockhashContractCode),
m_sealEngine->evmSchedule(blockNumber).accountVersion);
m_state.commit(State::CommitBehaviour::KeepEmptyAccounts);
}

Expand Down
36 changes: 28 additions & 8 deletions libethereum/Executive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,9 +342,11 @@ bool Executive::call(CallParameters const& _p, u256 const& _gasPrice, Address co
{
bytes const& c = m_s.code(_p.codeAddress);
h256 codeHash = m_s.codeHash(_p.codeAddress);
// Contract will be executed with the version stored in account
auto const version = m_s.version(_p.codeAddress);
m_ext = make_shared<ExtVM>(m_s, m_envInfo, m_sealEngine, _p.receiveAddress,
_p.senderAddress, _origin, _p.apparentValue, _gasPrice, _p.data, &c, codeHash,
m_depth, false, _p.staticCall);
version, m_depth, false, _p.staticCall);
}
}

Expand All @@ -355,24 +357,38 @@ bool Executive::call(CallParameters const& _p, u256 const& _gasPrice, Address co

bool Executive::create(Address const& _txSender, u256 const& _endowment, u256 const& _gasPrice, u256 const& _gas, bytesConstRef _init, Address const& _origin)
{
// Contract creation by an external account is the same as CREATE opcode
return createOpcode(_txSender, _endowment, _gasPrice, _gas, _init, _origin);
// Contract will be created with the version corresponding to latest hard fork
auto const latestVersion = m_sealEngine.evmSchedule(m_envInfo.number()).accountVersion;
return createWithAddressFromNonceAndSender(
_txSender, _endowment, _gasPrice, _gas, _init, _origin, latestVersion);
}

bool Executive::createOpcode(Address const& _sender, u256 const& _endowment, u256 const& _gasPrice, u256 const& _gas, bytesConstRef _init, Address const& _origin)
{
// Contract will be created with the version equal to parent's version
return createWithAddressFromNonceAndSender(
_sender, _endowment, _gasPrice, _gas, _init, _origin, m_s.version(_sender));
}

bool Executive::createWithAddressFromNonceAndSender(Address const& _sender, u256 const& _endowment,
u256 const& _gasPrice, u256 const& _gas, bytesConstRef _init, Address const& _origin,
u256 const& _version)
{
u256 nonce = m_s.getNonce(_sender);
m_newAddress = right160(sha3(rlpList(_sender, nonce)));
return executeCreate(_sender, _endowment, _gasPrice, _gas, _init, _origin);
return executeCreate(_sender, _endowment, _gasPrice, _gas, _init, _origin, _version);
}

bool Executive::create2Opcode(Address const& _sender, u256 const& _endowment, u256 const& _gasPrice, u256 const& _gas, bytesConstRef _init, Address const& _origin, u256 const& _salt)
{
m_newAddress = right160(sha3(bytes{0xff} +_sender.asBytes() + toBigEndian(_salt) + sha3(_init)));
return executeCreate(_sender, _endowment, _gasPrice, _gas, _init, _origin);
// Contract will be created with the version equal to parent's version
return executeCreate(
_sender, _endowment, _gasPrice, _gas, _init, _origin, m_s.version(_sender));
}

bool Executive::executeCreate(Address const& _sender, u256 const& _endowment, u256 const& _gasPrice, u256 const& _gas, bytesConstRef _init, Address const& _origin)
bool Executive::executeCreate(Address const& _sender, u256 const& _endowment, u256 const& _gasPrice,
u256 const& _gas, bytesConstRef _init, Address const& _origin, u256 const& _version)
{
if (_sender != MaxAddress ||
m_envInfo.number() < m_sealEngine.chainParams().experimentalForkBlock) // EIP86
Expand Down Expand Up @@ -411,7 +427,11 @@ bool Executive::executeCreate(Address const& _sender, u256 const& _endowment, u2
// Schedule _init execution if not empty.
if (!_init.empty())
m_ext = make_shared<ExtVM>(m_s, m_envInfo, m_sealEngine, m_newAddress, _sender, _origin,
_endowment, _gasPrice, bytesConstRef(), _init, sha3(_init), m_depth, true, false);
_endowment, _gasPrice, bytesConstRef(), _init, sha3(_init), _version, m_depth, true,
false);
else
// code stays empty, but we set the version
m_s.setCode(m_newAddress, {}, _version);

return !m_ext;
}
Expand Down Expand Up @@ -476,7 +496,7 @@ bool Executive::go(OnOpFunc const& _onOp)
}
if (m_res)
m_res->output = out.toVector(); // copy output to execution result
m_s.setCode(m_ext->myAddress, out.toVector());
m_s.setCode(m_ext->myAddress, out.toVector(), m_ext->version);
}
else
m_output = vm->exec(m_gas, *m_ext, _onOp);
Expand Down
10 changes: 9 additions & 1 deletion libethereum/Executive.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,17 @@ class Executive
/// Revert all changes made to the state by this execution.
void revert();

/// Used only in tests
ExtVM const& extVM() const { return *m_ext; }

private:
/// @returns false iff go() must be called (and thus a VM execution in required).
bool executeCreate(Address const& _txSender, u256 const& _endowment, u256 const& _gasPrice, u256 const& _gas, bytesConstRef _code, Address const& _originAddress);
bool createWithAddressFromNonceAndSender(Address const& _sender, u256 const& _endowment,
u256 const& _gasPrice, u256 const& _gas, bytesConstRef _init, Address const& _origin,
u256 const& _version);
/// @returns false iff go() must be called (and thus a VM execution in required).
bool executeCreate(Address const& _txSender, u256 const& _endowment, u256 const& _gasPrice,
u256 const& _gas, bytesConstRef _code, Address const& _originAddress, u256 const& _version);

State& m_s; ///< The state to which this operation/transaction is applied.
// TODO: consider changign to EnvInfo const& to avoid LastHashes copy at every CALL/CREATE
Expand Down
27 changes: 19 additions & 8 deletions libethereum/ExtVM.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,13 @@ class ExtVM : public ExtVMFace
/// Full constructor.
ExtVM(State& _s, EnvInfo const& _envInfo, SealEngineFace const& _sealEngine, Address _myAddress,
Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data,
bytesConstRef _code, h256 const& _codeHash, unsigned _depth, bool _isCreate,
bool _staticCall)
bytesConstRef _code, h256 const& _codeHash, u256 const& _version, unsigned _depth,
bool _isCreate, bool _staticCall)
: ExtVMFace(_envInfo, _myAddress, _caller, _origin, _value, _gasPrice, _data, _code.toBytes(),
_codeHash, _depth, _isCreate, _staticCall),
_codeHash, _version, _depth, _isCreate, _staticCall),
m_s(_s),
m_sealEngine(_sealEngine)
m_sealEngine(_sealEngine),
m_evmSchedule(initEvmSchedule(envInfo().number(), _version))
{
// Contract: processing account must exist. In case of CALL, the ExtVM
// is created only if an account has code (so exist). In case of CREATE
Expand Down Expand Up @@ -97,19 +98,29 @@ class ExtVM : public ExtVMFace
void suicide(Address _a) final;

/// Return the EVM gas-price schedule for this execution context.
EVMSchedule const& evmSchedule() const final
{
return m_sealEngine.evmSchedule(envInfo().number());
}
EVMSchedule const& evmSchedule() const final { return m_evmSchedule; }

State const& state() const { return m_s; }

/// Hash of a block if within the last 256 blocks, or h256() otherwise.
h256 blockHash(u256 _number) final;

private:
EVMSchedule const& initEvmSchedule(int64_t _blockNumber, u256 const& _version) const
{
// If _version is latest for the block, select corresponding latest schedule.
// Otherwise run with the latest schedule known to correspond to the _version.
EVMSchedule const& currentBlockSchedule = m_sealEngine.evmSchedule(_blockNumber);
if (currentBlockSchedule.accountVersion == _version)
return currentBlockSchedule;
else
return latestScheduleForAccountVersion(_version);
}


State& m_s; ///< A reference to the base state.
SealEngineFace const& m_sealEngine;
EVMSchedule const& m_evmSchedule;
};

}
Expand Down
Loading

0 comments on commit d169557

Please sign in to comment.