Skip to content
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

[0.5] Change account id generation logic #648

Merged
merged 4 commits into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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