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

multi_index bugfix after migration from boost + tests #146

Merged
merged 1 commit into from
Apr 20, 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
58 changes: 30 additions & 28 deletions libraries/eosiolib/contracts/eosio/multi_index.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,8 @@ class multi_index

static auto extract_secondary_key(const T& obj) { return secondary_extractor_type()(obj); }

// used only for type deduction
constexpr index() : _multidx(nullptr) { }
private:
friend class multi_index;

Expand All @@ -764,17 +766,19 @@ class multi_index

template<uint64_t I>
struct intc { enum e{ value = I }; operator uint64_t()const{ return I; } };
enum index_cv { const_index = 0, mutable_index = 1 };

template<size_t Num, typename... Values>
template<std::size_t Num, typename... Values>
class make_index_tuple {
template <uint64_t... Seq>

template <std::size_t... Seq>
static constexpr auto get_type(std::index_sequence<Seq...>) {
return std::make_tuple(std::make_tuple(index<eosio::name::raw(static_cast<uint64_t>(Values::index_name)),
typename Values::secondary_extractor_type,
intc<Seq>::e::value, false>{},
intc<Seq>::e::value, const_index>{},
index<eosio::name::raw(static_cast<uint64_t>(Values::index_name)),
typename Values::secondary_extractor_type,
intc<Seq>::e::value, true>{})...);
intc<Seq>::e::value, mutable_index>{})...);
}
public:
using type = decltype( get_type(std::make_index_sequence<Num>{}) );
Expand All @@ -783,19 +787,17 @@ class multi_index
using indices_type = typename make_index_tuple<sizeof... (Indices), Indices...>::type;

class make_extractor_tuple {
template <typename Obj, typename IndicesType, uint64_t... Seq>
static constexpr auto get_type(const IndicesType& indices, const Obj& obj, std::index_sequence<Seq...>) {
return std::make_tuple(decltype(std::get<0>(std::get<Seq>(indices)))::extract_secondary_key(obj)...);
template <typename Obj, typename IndicesType, std::size_t... Seq>
static constexpr auto extractor_tuple(IndicesType, const Obj& obj, std::index_sequence<Seq...>) {
return std::make_tuple(std::tuple_element_t<const_index, std::tuple_element_t<Seq, IndicesType>>::extract_secondary_key(obj)...);
}
public:
template <typename Obj, typename IndicesType>
static auto get_extractor_tuple(const IndicesType& indices, const Obj& obj) {
return get_type(indices, obj, std::make_index_sequence<sizeof...(Indices)>{});
static constexpr auto get_extractor_tuple(IndicesType, const Obj& obj) {
return extractor_tuple(IndicesType{}, obj, std::make_index_sequence<std::tuple_size_v<IndicesType>>{});
}
};

indices_type _indices;

const item& load_object_by_primary_iterator( int32_t itr )const {
using namespace _multi_index_detail;

Expand All @@ -820,8 +822,8 @@ class multi_index
ds >> val;

i.__primary_itr = itr;
bluegrass::meta::for_each(_indices, [&](auto& idx){
typedef decltype(std::get<1>(idx)) index_type;
bluegrass::meta::for_each(indices_type{}, [&](auto idx){
typedef std::tuple_element_t<const_index, decltype(idx)> index_type;
i.__iters[ index_type::number() ] = -1;
});
});
Expand Down Expand Up @@ -1425,13 +1427,13 @@ class multi_index
auto get_index() {
using namespace _multi_index_detail;

constexpr uint64_t index_num = bluegrass::meta::for_each(_indices, [&](auto& idx){
return decltype(std::get<0>(idx))::index_name == static_cast<uint64_t>(IndexName);
}, _indices);
constexpr uint64_t index_num = bluegrass::meta::for_each(indices_type{}, [](auto idx){
return std::tuple_element_t<const_index, decltype(idx)>::index_name == static_cast<uint64_t>(IndexName);
});

static_assert( index_num < sizeof...(Indices), "name provided is not the name of any secondary index within multi_index" );

return decltype(std::get<0>(std::get<index_num>(_indices)))(this);
return std::tuple_element_t<const_index, std::tuple_element_t<index_num, indices_type>>(this);
}

/**
Expand Down Expand Up @@ -1476,13 +1478,13 @@ class multi_index
auto get_index()const {
using namespace _multi_index_detail;

constexpr uint64_t index_num = bluegrass::meta::for_each(_indices, [&](auto& idx){
return decltype(std::get<1>(idx))::index_name == static_cast<uint64_t>(IndexName);
}, _indices);
constexpr uint64_t index_num = bluegrass::meta::for_each(indices_type{}, [](auto idx){
return std::tuple_element_t<mutable_index, decltype(idx)>::index_name == static_cast<uint64_t>(IndexName);
});

static_assert( index_num < sizeof...(Indices), "name provided is not the name of any secondary index within multi_index" );

return decltype(std::get<1>(std::get<index_num>(_indices)))(this);
return std::tuple_element_t<mutable_index, std::tuple_element_t<index_num, indices_type>>(this);
}

/**
Expand Down Expand Up @@ -1595,8 +1597,8 @@ class multi_index
if( pk >= _next_primary_key )
_next_primary_key = (pk >= no_available_primary_key) ? no_available_primary_key : (pk + 1);

bluegrass::meta::for_each(_indices, [&](auto& idx){
typedef decltype(std::get<0>(idx)) index_type;
bluegrass::meta::for_each(indices_type{}, [&](auto idx){
typedef std::tuple_element_t<const_index, decltype(idx)> index_type;

i.__iters[index_type::number()] = secondary_index_db_functions<typename index_type::secondary_key_type>::db_idx_store( _scope, index_type::name(), payer.value, obj.primary_key(), index_type::extract_secondary_key(obj) );
});
Expand Down Expand Up @@ -1708,7 +1710,7 @@ class multi_index
auto& mutableitem = const_cast<item&>(objitem);
eosio::check( _code == current_receiver(), "cannot modify objects in table of another contract" ); // Quick fix for mutating db using multi_index that shouldn't allow mutation. Real fix can come in RC2.

auto secondary_keys = make_extractor_tuple::get_extractor_tuple(_indices, obj);
auto secondary_keys = make_extractor_tuple::get_extractor_tuple(indices_type{}, obj);

uint64_t pk = _multi_index_detail::to_raw_key(obj.primary_key());

Expand All @@ -1733,8 +1735,8 @@ class multi_index
if( pk >= _next_primary_key )
_next_primary_key = (pk >= no_available_primary_key) ? no_available_primary_key : (pk + 1);

bluegrass::meta::for_each(_indices, [&](auto& idx){
typedef decltype(std::get<0>(idx)) index_type;
bluegrass::meta::for_each(indices_type{}, [&](auto idx){
typedef std::tuple_element_t<const_index, decltype(idx)> index_type;
auto secondary = index_type::extract_secondary_key( obj );
if( memcmp( &std::get<index_type::index_number>(secondary_keys), &secondary, sizeof(secondary) ) != 0 ) {
auto indexitr = mutableitem.__iters[index_type::number()];
Expand Down Expand Up @@ -1948,8 +1950,8 @@ class multi_index

internal_use_do_not_use::db_remove_i64( objitem.__primary_itr );

bluegrass::meta::for_each(_indices, [&](auto& idx){
typedef decltype(std::get<0>(idx)) index_type;
bluegrass::meta::for_each(indices_type{}, [&](auto idx){
typedef std::tuple_element_t<const_index, decltype(idx)> index_type;

auto i = objitem.__iters[index_type::number()];
if( i < 0 ) {
Expand Down
2 changes: 1 addition & 1 deletion libraries/meta_refl/include/bluegrass/meta/for_each.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -876,7 +876,7 @@ void for_each_field(T&& value, F&& func) {
template <size_t I = 0, template<typename...> typename Tuple, typename Fn, typename... Ts>
constexpr size_t for_each_impl(Tuple<Ts...>& tup, Fn fn) {
if constexpr(I < sizeof...(Ts)) {
using result_t = std::decay_t<decltype(fn(std::get<I>(tup)))>;
using result_t = decltype(fn(std::get<I>(tup)));
if constexpr (std::is_same_v<void, result_t>) {
fn(std::get<I>(tup));
}
Expand Down
3 changes: 3 additions & 0 deletions tests/integration/contracts.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,8 @@ namespace eosio::testing {

static std::vector<uint8_t> name_pk_tests_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../unit/test_contracts/name_pk_tests.wasm"); }
static std::vector<char> name_pk_tests_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/name_pk_tests.abi"); }

static std::vector<uint8_t> test_multi_index_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../unit/test_contracts/test_multi_index.wasm"); }
static std::vector<char> test_multi_index_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/test_multi_index.abi"); }
};
} //ns eosio::testing
75 changes: 75 additions & 0 deletions tests/integration/multi_index_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-compare"
#include <boost/test/unit_test.hpp>
#pragma GCC diagnostic pop

#include <eosio/testing/tester.hpp>

#include <contracts.hpp>

using namespace eosio;
using namespace eosio::testing;

#ifdef NON_VALIDATING_TEST
#define TESTER tester
#else
#define TESTER validating_tester
#endif

BOOST_AUTO_TEST_SUITE(multi_index_tests)

// this test is copy from leap test_api_multi_index
BOOST_FIXTURE_TEST_CASE(main_multi_index_tests, TESTER) { try {
produce_blocks(1);
create_account( "testapi"_n );
produce_blocks(1);
set_code( "testapi"_n, contracts::test_multi_index_wasm() );
set_abi( "testapi"_n, contracts::test_multi_index_abi().data() );
produce_blocks(1);

auto check_failure = [this]( action_name a, const char* expected_error_msg ) {
BOOST_CHECK_EXCEPTION( push_action( "testapi"_n, a, "testapi"_n, {} ),
eosio_assert_message_exception,
eosio_assert_message_is( expected_error_msg )
);
};

push_action( "testapi"_n, "s1g"_n, "testapi"_n, {} ); // idx64_general
push_action( "testapi"_n, "s1store"_n, "testapi"_n, {} ); // idx64_store_only
push_action( "testapi"_n, "s1check"_n, "testapi"_n, {} ); // idx64_check_without_storing
push_action( "testapi"_n, "s2g"_n, "testapi"_n, {} ); // idx128_general
push_action( "testapi"_n, "s2store"_n, "testapi"_n, {} ); // idx128_store_only
push_action( "testapi"_n, "s2check"_n, "testapi"_n, {} ); // idx128_check_without_storing
push_action( "testapi"_n, "s2autoinc"_n, "testapi"_n, {} ); // idx128_autoincrement_test
push_action( "testapi"_n, "s2autoinc1"_n, "testapi"_n, {} ); // idx128_autoincrement_test_part1
push_action( "testapi"_n, "s2autoinc2"_n, "testapi"_n, {} ); // idx128_autoincrement_test_part2
push_action( "testapi"_n, "s3g"_n, "testapi"_n, {} ); // idx256_general
push_action( "testapi"_n, "sdg"_n, "testapi"_n, {} ); // idx_double_general
push_action( "testapi"_n, "sldg"_n, "testapi"_n, {} ); // idx_long_double_general

check_failure( "s1pkend"_n, "cannot increment end iterator" ); // idx64_pk_iterator_exceed_end
check_failure( "s1skend"_n, "cannot increment end iterator" ); // idx64_sk_iterator_exceed_end
check_failure( "s1pkbegin"_n, "cannot decrement iterator at beginning of table" ); // idx64_pk_iterator_exceed_begin
check_failure( "s1skbegin"_n, "cannot decrement iterator at beginning of index" ); // idx64_sk_iterator_exceed_begin
check_failure( "s1pkref"_n, "object passed to iterator_to is not in multi_index" ); // idx64_pass_pk_ref_to_other_table
check_failure( "s1skref"_n, "object passed to iterator_to is not in multi_index" ); // idx64_pass_sk_ref_to_other_table
check_failure( "s1pkitrto"_n, "object passed to iterator_to is not in multi_index" ); // idx64_pass_pk_end_itr_to_iterator_to
check_failure( "s1pkmodify"_n, "cannot pass end iterator to modify" ); // idx64_pass_pk_end_itr_to_modify
check_failure( "s1pkerase"_n, "cannot pass end iterator to erase" ); // idx64_pass_pk_end_itr_to_erase
check_failure( "s1skitrto"_n, "object passed to iterator_to is not in multi_index" ); // idx64_pass_sk_end_itr_to_iterator_to
check_failure( "s1skmodify"_n, "cannot pass end iterator to modify" ); // idx64_pass_sk_end_itr_to_modify
check_failure( "s1skerase"_n, "cannot pass end iterator to erase" ); // idx64_pass_sk_end_itr_to_erase
check_failure( "s1modpk"_n, "updater cannot change primary key when modifying an object" ); // idx64_modify_primary_key
check_failure( "s1exhaustpk"_n, "next primary key in table is at autoincrement limit" ); // idx64_run_out_of_avl_pk
check_failure( "s1findfail1"_n, "unable to find key" ); // idx64_require_find_fail
check_failure( "s1findfail2"_n, "unable to find primary key in require_find" );// idx64_require_find_fail_with_msg
check_failure( "s1findfail3"_n, "unable to find secondary key" ); // idx64_require_find_sk_fail
check_failure( "s1findfail4"_n, "unable to find sec key" ); // idx64_require_find_sk_fail_with_msg

push_action( "testapi"_n, "s1skcache"_n, "testapi"_n, {} ); // idx64_sk_cache_pk_lookup
push_action( "testapi"_n, "s1pkcache"_n, "testapi"_n, {} ); // idx64_pk_cache_sk_lookup

BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW() }

BOOST_AUTO_TEST_SUITE_END()
1 change: 1 addition & 0 deletions tests/unit/test_contracts/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ add_contract(crypto_primitives_tests crypto_primitives_tests crypto_primitives_t
add_contract(get_code_hash_tests get_code_hash_write get_code_hash_write.cpp)
add_contract(get_code_hash_tests get_code_hash_read get_code_hash_read.cpp)
add_contract(name_pk_tests name_pk_tests name_pk_tests.cpp)
add_contract(test_multi_index test_multi_index multi_index_tests.cpp)
add_contract(capi_tests capi_tests capi/capi.c capi/action.c capi/chain.c capi/crypto.c capi/db.c capi/permission.c
capi/print.c capi/privileged.c capi/system.c capi/transaction.c)

Expand Down
Loading