[EOSIO/eos] Bug: _items_vector is not updated when multi_index is moved to new instance (#5317)

https://github.com/EOSIO/eos/blob/develop/contracts/eosiolib/multi_index.hpp#L270
The elements of `_items_vector` are not not updated to the address of new instance when `multi_index` is moved causing certain operations to fail. Affected member functions are [`iterator_to`](https://github.com/EOSIO/eos/blob/develop/contracts/eosiolib/multi_index.hpp#L478), [`erase`](https://github.com/EOSIO/eos/blob/develop/contracts/eosiolib/multi_index.hpp#L2154) and [`modify`](https://github.com/EOSIO/eos/blob/develop/contracts/eosiolib/multi_index.hpp#L1838). All three functions fill throw an assert exception with message `object passed to is not in multi_index` on cached items when index was moved to new instance.

The core of this problem is that all successful query operations carried against `multi_index`, cache loaded items. Cached Items store the address of multi_index instance in `__idx` member variable for identification purposes. The `__idx` variable is never updated when index is moved to the new instance.

Items are cached here:
https://github.com/EOSIO/eos/blob/develop/contracts/eosiolib/multi_index.hpp#L590
and here:
https://github.com/EOSIO/eos/blob/develop/contracts/eosiolib/multi_index.hpp#L1696

Example of a bug:

«`

multi_index_t get_multi_index_of(uint64_t key)
{
multi_index_1 mi1;
if(mi1.find(key) != mi1.end()) {
return mi1;
}

multi_index_2 mi2;
……
}

///////////////////////////////////////////////////////

auto mi = get_multi_index_of(some_key);
auto it = mi.find(some_key);
mi.modify(it, payer, [](auto& obj){…}) // Error
«`

Possible solutions:
— Make custom move constructor / move assignment operator and do not move `_items_vector` to new instance. The problem with this solution is, that new instance looses all cached items and has to fetch them again from db.
— Make custom move constructor / move assignment operator and update items of `_items_vector` to the new instance before move. This solution is very expensive in terms of CPU cycles so I wouldn’t consider it.
— Delete move constructor / move assignment operator. I would also not recommend this solution because there are cases where you want to move `multi_index` from one object to another.
— Replace member variable `__idx` with unique identifier which is not tied to the address of index instance. Since `__idx` is not used for other than identifying parent index, it’s safe to replace it with short UID.

Добавить комментарий