Skip to content

Commit

Permalink
Merge pull request #648 from eosnetworkfoundation/elmato/change-accou…
Browse files Browse the repository at this point in the history
…nt-id-generation

[0.5] Change account id generation logic
  • Loading branch information
elmato authored Sep 5, 2023
2 parents 4da2499 + 07f89bb commit a9df91a
Show file tree
Hide file tree
Showing 11 changed files with 724 additions and 3 deletions.
4 changes: 4 additions & 0 deletions contract/include/evm_runtime/state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,12 @@ struct state : State {
mutable std::map<evmc::address, uint64_t> addr2id;
mutable std::map<bytes32, bytes> addr2code;
mutable db_stats stats;
std::optional<config2> _config2;

explicit state(name self, name ram_payer, bool read_only=false) : _self(self), _ram_payer(ram_payer), _read_only{read_only}{}
virtual ~state() override;

uint64_t get_next_account_id();

std::optional<Account> read_account(const evmc::address& address) const noexcept override;

Expand Down
7 changes: 7 additions & 0 deletions contract/include/evm_runtime/tables.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,11 @@ struct [[eosio::table]] [[eosio::contract("evm_contract")]] allowed_egress_accou

typedef eosio::multi_index<"egresslist"_n, allowed_egress_account> egresslist;

struct [[eosio::table]] [[eosio::contract("evm_contract")]] config2
{
uint64_t next_account_id{0};

EOSLIB_SERIALIZE(config2, (next_account_id));
};

} //namespace evm_runtime
1 change: 1 addition & 0 deletions contract/src/actions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,7 @@ void evm_contract::withdraw(eosio::name owner, eosio::asset quantity, const eosi

bool evm_contract::gc(uint32_t max) {
assert_unfrozen();
require_auth(get_self());

evm_runtime::state state{get_self(), eosio::same_payer};
return state.gc(max);
Expand Down
27 changes: 24 additions & 3 deletions contract/src/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ void state::update_account(const evmc::address& address, std::optional<Account>
++stats.account.read;

auto emplace = [&](auto& row) {
row.id = accounts.available_primary_key();
row.id = get_next_account_id();
row.eth_address = to_bytes(address);
row.nonce = current->nonce;
row.balance = to_bytes(current->balance);
Expand Down Expand Up @@ -214,7 +214,7 @@ void state::update_account_code(const evmc::address& address, uint64_t, const ev
++stats.account.update;
} else {
accounts.emplace(_ram_payer, [&](auto& row){
row.id = accounts.available_primary_key();;
row.id = get_next_account_id();;
row.eth_address = to_bytes(address);
row.nonce = 0;
row.code_id = code_id;
Expand Down Expand Up @@ -245,7 +245,7 @@ void state::update_storage(const evmc::address& address, uint64_t incarnation, c
uint64_t table_id;
if(itr == inx.end()){
accounts.emplace(_ram_payer, [&](auto& row){
table_id = accounts.available_primary_key();
table_id = get_next_account_id();
row.id = table_id;
row.eth_address = to_bytes(address);
row.nonce = 0;
Expand Down Expand Up @@ -329,4 +329,25 @@ evmc::bytes32 state::state_root_hash() const {
return {};
}

uint64_t state::get_next_account_id() {
if(!_config2) {
eosio::singleton<"config2"_n, config2> cfg2{_self, _self.value};
if(cfg2.exists()) {
_config2 = cfg2.get();
} else {
account_table accounts(_self, _self.value);
_config2 = config2{accounts.available_primary_key()};
}
}
auto id = _config2->next_account_id;
_config2->next_account_id++;
return id;
}

state::~state() {
if(!_config2.has_value()) return;
eosio::singleton<"config2"_n, config2> cfg2{_self, _self.value};
cfg2.set(_config2.value(), _self);
}

} // namespace evm_runtime
1 change: 1 addition & 0 deletions contract/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ include_directories(
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations")

add_eosio_test_executable( unit_test
${CMAKE_SOURCE_DIR}/account_id_tests.cpp
${CMAKE_SOURCE_DIR}/basic_evm_tester.cpp
${CMAKE_SOURCE_DIR}/evm_runtime_tests.cpp
${CMAKE_SOURCE_DIR}/init_tests.cpp
Expand Down
254 changes: 254 additions & 0 deletions contract/tests/account_id_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
#include "basic_evm_tester.hpp"
#include <silkworm/execution/address.hpp>

using namespace evm_test;
struct account_id_tester : basic_evm_tester {
account_id_tester() {
create_accounts({"alice"_n});
transfer_token(faucet_account_name, "alice"_n, make_asset(10000'0000));
init();
}

std::optional<uint64_t> get_max_account_id() {
const auto& db = control->db();
const auto* t_id = db.find<chain::table_id_object, chain::by_code_scope_table>( boost::make_tuple( evm_account_name, evm_account_name, "account"_n ) );
if ( !t_id ) return {};

const auto& idx = db.get_index<chain::key_value_index, chain::by_scope_primary>();
if( idx.begin() == idx.end() ) return {};

decltype(t_id->id) next_tid(t_id->id._id + 1);
auto itr = idx.lower_bound(boost::make_tuple(next_tid));
if(itr == idx.end() || itr->t_id == next_tid) --itr;

if(itr->t_id._id != t_id->id._id) return {};
return itr->primary_key;
}

std::string int_str32(uint32_t x) {
std::stringstream hex_ss;
hex_ss << std::hex << x;
int hex_length = hex_ss.str().length();

std::stringstream ss;
ss << std::setfill('0') << std::setw(64 - hex_length) << 0 << std::hex << std::uppercase << x;
return ss.str();
}

// // SPDX-License-Identifier: MIT
// pragma solidity ^0.8.17;
// contract Factory {
// // Returns the address of the newly deployed contract
// function deploy(
// bytes32 _salt
// ) public payable returns (address) {
// return address(new TestContract{salt: _salt}());
// }
// }
// contract TestContract {
// uint foo;
// function set_foo(uint val) public {
// foo = val;
// }
// function killme() public {
// address payable addr = payable(address(0));
// selfdestruct(addr);
// }
// }

const std::string factory_and_test_bytecode = "608060405234801561001057600080fd5b506102c1806100206000396000f3fe60806040526004361061001e5760003560e01c80632b85ba3814610023575b600080fd5b61003d600480360381019061003891906100d2565b610053565b60405161004a9190610140565b60405180910390f35b6000816040516100629061008a565b8190604051809103906000f5905080158015610082573d6000803e3d6000fd5b509050919050565b6101308061015c83390190565b600080fd5b6000819050919050565b6100af8161009c565b81146100ba57600080fd5b50565b6000813590506100cc816100a6565b92915050565b6000602082840312156100e8576100e7610097565b5b60006100f6848285016100bd565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061012a826100ff565b9050919050565b61013a8161011f565b82525050565b60006020820190506101556000830184610131565b9291505056fe608060405234801561001057600080fd5b50610110806100206000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806324d97a4a146037578063e5d5dfbc14603f575b600080fd5b603d6057565b005b605560048036038101906051919060b2565b6072565b005b60008073ffffffffffffffffffffffffffffffffffffffff16ff5b8060008190555050565b600080fd5b6000819050919050565b6092816081565b8114609c57600080fd5b50565b60008135905060ac81608b565b92915050565b60006020828403121560c55760c4607c565b5b600060d184828501609f565b9150509291505056fea2646970667358221220e59ba0023d9a6f99a6734000d8812c1830a2a61597b322310827ec350a4304dd64736f6c63430008120033a26469706673582212205e94c73549f6e1751509ff5704aec0a8ada3a60ab2b8cc1b9df9febc7ed2bd2f64736f6c63430008120033";

size_t get_storage_slots_count(uint64_t account_id) {
size_t total_slots{0};
scan_account_storage(account_id, [&](const storage_slot& slot)->bool{
total_slots++;
return true;
});
return total_slots;
}


};

BOOST_AUTO_TEST_SUITE(account_id_tests)

BOOST_FIXTURE_TEST_CASE(new_account_id_logic, account_id_tester) try {

BOOST_CHECK(!get_max_account_id().has_value());
BOOST_REQUIRE_EXCEPTION(get_config2(), fc::out_of_range_exception, [](const fc::out_of_range_exception& e) {return true;});

// Fund evm1 address with 100 EOS
evm_eoa evm1;
const int64_t to_bridge = 1000000;
transfer_token("alice"_n, evm_account_name, make_asset(to_bridge), evm1.address_0x());
BOOST_CHECK(get_max_account_id().value() == 0);
BOOST_CHECK(get_config2().next_account_id == 1);

// Deploy Factory and Test contracts
auto contract_addr = deploy_contract(evm1, evmc::from_hex(factory_and_test_bytecode).value());
uint64_t contract_account_id = find_account_by_address(contract_addr).value().id;
BOOST_CHECK(get_max_account_id().value() == 1);
BOOST_CHECK(get_config2().next_account_id == 2);

// Call 'Factory::deploy'
auto txn = generate_tx(contract_addr, 0, 1'000'000);

silkworm::Bytes data;
data += evmc::from_hex("2b85ba38").value(); //deploy
data += evmc::from_hex(int_str32(555)).value(); //salt=555
txn.data = data;
evm1.sign(txn);
pushtx(txn);

BOOST_CHECK(get_max_account_id().value() == 2);
BOOST_CHECK(get_config2().next_account_id == 3);

// Recover TestContract address
auto test_contract_address = find_account_by_id(2).value().address;

// Call 'TestContract::set_foo(1234)'
txn = generate_tx(test_contract_address, 0, 1'000'000);
data.clear();
data += evmc::from_hex("e5d5dfbc").value(); //set_foo
data += evmc::from_hex(int_str32(1234)).value(); //val=1234
txn.data = data;
evm1.sign(txn);
pushtx(txn);

BOOST_CHECK(get_storage_slots_count(2) == 1);

// Call 'TestContract::killme'
txn = generate_tx(test_contract_address, 0, 1'000'000);
data.clear();
data += evmc::from_hex("24d97a4a").value(); //killme
txn.data = data;
evm1.sign(txn);
pushtx(txn);

BOOST_CHECK(get_storage_slots_count(2) == 1);

BOOST_CHECK(get_max_account_id().value() == 1);
BOOST_CHECK(get_config2().next_account_id == 3);

// Call 'Factory::deploy' again
txn = generate_tx(contract_addr, 0, 1'000'000);
data.clear();
data += evmc::from_hex("2b85ba38").value(); //deploy
data += evmc::from_hex(int_str32(555)).value(); //salt=555
txn.data = data;
evm1.sign(txn);
pushtx(txn);

BOOST_CHECK(get_max_account_id().value() == 3);
BOOST_CHECK(get_config2().next_account_id == 4);

BOOST_CHECK(get_storage_slots_count(3) == 0);

BOOST_CHECK(test_contract_address == find_account_by_id(3).value().address);

// Call 'TestContract::set_foo(1234)'
txn = generate_tx(test_contract_address, 0, 1'000'000);
data.clear();
data += evmc::from_hex("e5d5dfbc").value(); //set_foo
data += evmc::from_hex(int_str32(1234)).value(); //val=1234
txn.data = data;
evm1.sign(txn);
pushtx(txn);

BOOST_CHECK(get_storage_slots_count(3) == 1);

// Call gc(100)
gc(100);
BOOST_CHECK(get_storage_slots_count(2) == 0);


} FC_LOG_AND_RETHROW()

BOOST_FIXTURE_TEST_CASE(transition_from_0_5_1, account_id_tester) try {

// Set old code
set_code(evm_account_name, testing::contracts::evm_runtime_wasm_0_5_1());
set_abi(evm_account_name, testing::contracts::evm_runtime_abi_0_5_1().data());

BOOST_CHECK(!get_max_account_id().has_value());

// Fund evm1 address with 100 EOS
evm_eoa evm1;
const int64_t to_bridge = 1000000;
transfer_token("alice"_n, evm_account_name, make_asset(to_bridge), evm1.address_0x());
BOOST_CHECK(get_max_account_id().value() == 0);

// Deploy Factory and Test contracts
auto contract_addr = deploy_contract(evm1, evmc::from_hex(factory_and_test_bytecode).value());
uint64_t contract_account_id = find_account_by_address(contract_addr).value().id;
BOOST_CHECK(get_max_account_id().value() == 1);

// Call 'Factory::deploy'
auto txn = generate_tx(contract_addr, 0, 1'000'000);

silkworm::Bytes data;
data += evmc::from_hex("2b85ba38").value(); //deploy
data += evmc::from_hex(int_str32(555)).value(); //salt=555
txn.data = data;
evm1.sign(txn);
pushtx(txn);

BOOST_CHECK(get_max_account_id().value() == 2);

// Recover TestContract address
auto test_contract_address = find_account_by_id(2).value().address;

// Call 'TestContract::set_foo(1234)'
txn = generate_tx(test_contract_address, 0, 1'000'000);
data.clear();
data += evmc::from_hex("e5d5dfbc").value(); //set_foo
data += evmc::from_hex(int_str32(1234)).value(); //val=1234
txn.data = data;
evm1.sign(txn);
pushtx(txn);

BOOST_CHECK(get_storage_slots_count(2) == 1);

// Call 'TestContract::killme'
txn = generate_tx(test_contract_address, 0, 1'000'000);
data.clear();
data += evmc::from_hex("24d97a4a").value(); //killme
txn.data = data;
evm1.sign(txn);
pushtx(txn);

BOOST_CHECK(get_storage_slots_count(2) == 1);
BOOST_CHECK(get_max_account_id().value() == 1);

// Make a transfer to a new address to create a new account
evm_eoa evm2;
transfer_token("alice"_n, evm_account_name, make_asset(to_bridge), evm2.address_0x());
BOOST_CHECK(get_max_account_id().value() == 2);

// Set new code
set_code(evm_account_name, testing::contracts::evm_runtime_wasm());
set_abi(evm_account_name, testing::contracts::evm_runtime_abi().data());

BOOST_REQUIRE_EXCEPTION(get_config2(), fc::out_of_range_exception, [](const fc::out_of_range_exception& e) {return true;});

// Call 'Factory::deploy' again
txn = generate_tx(contract_addr, 0, 1'000'000);
data.clear();
data += evmc::from_hex("2b85ba38").value(); //deploy
data += evmc::from_hex(int_str32(555)).value(); //salt=555
txn.data = data;
evm1.sign(txn);
pushtx(txn);

BOOST_CHECK(get_max_account_id().value() == 3);
BOOST_CHECK(get_config2().next_account_id == 4);

// Make a transfer to a new address to create a new account
evm_eoa evm3;
transfer_token("alice"_n, evm_account_name, make_asset(to_bridge), evm3.address_0x());
BOOST_CHECK(get_max_account_id().value() == 4);
BOOST_CHECK(get_config2().next_account_id == 5);

} FC_LOG_AND_RETHROW()

BOOST_AUTO_TEST_SUITE_END()
26 changes: 26 additions & 0 deletions contract/tests/basic_evm_tester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,14 @@ config_table_row basic_evm_tester::get_config() const
return fc::raw::unpack<config_table_row>(d);
}

config2_table_row basic_evm_tester::get_config2() const
{
static constexpr eosio::chain::name config2_singleton_name = "config2"_n;
const vector<char> d =
get_row_by_account(evm_account_name, evm_account_name, config2_singleton_name, config2_singleton_name);
return fc::raw::unpack<config2_table_row>(d);
}

void basic_evm_tester::setfeeparams(const fee_parameters& fee_params)
{
mvo fee_params_vo;
Expand Down Expand Up @@ -342,6 +350,10 @@ void basic_evm_tester::withdraw(name owner, asset quantity)
push_action(evm_account_name, "withdraw"_n, owner, mvo()("owner", owner)("quantity", quantity));
}

void basic_evm_tester::gc(uint32_t max) {
push_action(evm_account_name, "gc"_n, evm_account_name, mvo()("max", max));
}

balance_and_dust basic_evm_tester::vault_balance(name owner) const
{
const vector<char> d = get_row_by_account(evm_account_name, evm_account_name, "balances"_n, owner);
Expand Down Expand Up @@ -466,6 +478,20 @@ std::optional<account_object> basic_evm_tester::find_account_by_address(const ev
return result;
}

std::optional<account_object> basic_evm_tester::find_account_by_id(uint64_t id) const
{
static constexpr eosio::chain::name account_table_name = "account"_n;
const vector<char> d =
get_row_by_account(evm_account_name, evm_account_name, account_table_name, name{id});
if(d.empty()) return {};

partial_account_table_row row;
fc::datastream<const char*> ds(d.data(), d.size());
fc::raw::unpack(ds, row);

return convert_to_account_object(row);
}

bool basic_evm_tester::scan_account_storage(uint64_t account_id, std::function<bool(storage_slot)> visitor) const
{
static constexpr eosio::chain::name storage_table_name = "storage"_n;
Expand Down
Loading

0 comments on commit a9df91a

Please sign in to comment.