Compare commits

...

23 Commits

Author SHA1 Message Date
Matt Witherspoon 21e6bea309 Merge remote-tracking branch 'origin/release/4.0' into integration_tests_ci 2023-05-24 14:38:59 -04:00
Alexander Molchevsky aa6efda41d Merge pull request #181 from AntelopeIO/crypto_api_update_proposal
Proposal: Update description of the crypto API to make it more user friendly
2023-05-23 17:17:27 +03:00
Alexander Molchevsky 5b06bd3451 Apply suggestions from code review
a few grammatical changes were made

Co-authored-by: Scott B <Scott@TheBaileyFamily.org>
2023-05-23 00:06:53 +03:00
Alexander Molchevsky a7fa7519b6 Updated after review 2023-05-19 21:48:41 +03:00
Alexander Molchevsky 4bae47bf94 Description of the Crypto API was updated 2023-05-19 08:06:44 +03:00
dimas1185 e3ef75c352 Merge pull request #178 from AntelopeIO/fix-abi-singleton
fixing abi for singleton with non-table type
2023-05-18 17:47:25 -04:00
mikelik 8a6bfbde42 Merge pull request #179 from AntelopeIO/mikelik/crud
Add insert, remove and update methods to multi_index (and index)
2023-05-18 16:23:59 +02:00
Michal Lesiak 65f928fcde Code review improvements 2023-05-18 15:04:57 +02:00
Michal Lesiak a1aae5f8d2 Update tests to cover both const and non-const version of remove() 2023-05-18 15:04:57 +02:00
Michal Lesiak 2b0fd5c55b Get rid of const obj for new modify() method. 2023-05-18 15:04:57 +02:00
Michal Lesiak 94ca0508dd Add insert, remove and update methods to multi_index (and index) 2023-05-18 15:04:57 +02:00
Dmytro Sydorchenko 46c13e9e9d fix integration tests 2023-05-17 17:34:17 -04:00
Alexander Molchevsky 5683746983 Merge pull request #180 from AntelopeIO/revert_new_cdt-abidiff
Revert "Added an implementation of a new cdt-abidiff"
2023-05-17 17:44:58 +03:00
Alexander Molchevsky 5931b591c5 Revert "Added an implementation of a new cdt-abidiff"
This reverts commit 20890cd510.
2023-05-17 15:08:49 +03:00
Dmytro Sydorchenko 23510b2d3c fixing abi for singleton with non-table type 2023-05-15 21:57:27 -04:00
dimas1185 aa0904a420 Merge pull request #177 from AntelopeIO/endian-warning-fix
Endian.h warning fix
2023-05-11 10:02:24 -04:00
Dmytro Sydorchenko 9a7e23f8f4 fix antler-proj ubuntu 18 build 2023-05-10 19:55:37 -04:00
mikelik 99e80b1b9a Merge pull request #158 from AntelopeIO/ehp/GH-157-doc-fix-clang-tidy
Doc Fix: Update Readme to install dep clang-tidy
2023-05-09 10:44:10 +02:00
Alexander Molchevsky 23a78365b1 Merge pull request #171 from AntelopeIO/crypto_ext_doc_upd
The crypto extensions documentation updated
2023-05-04 15:56:08 +03:00
Bucky Kittinger bfb1edb40b Merge pull request #174 from AntelopeIO/larryk85/version_bump
version bump
2023-05-04 08:45:47 -04:00
Bucky Kittinger 5fe3166773 Merge branch 'main' into crypto_ext_doc_upd 2023-05-03 22:53:01 -04:00
Alexander Molchevsky 19d33cca18 The crypto extensions documentation was reviewed and updated 2023-05-03 13:47:40 +03:00
Eric Passmore be7b70ccae note to install dep clang-tidy 2023-05-01 07:08:35 -07:00
4 changed files with 401 additions and 163 deletions
+1
View File
@@ -38,6 +38,7 @@ The instructions below assume that you are building on Ubuntu 20.04.
apt-get update && apt-get install \
build-essential \
clang \
clang-tidy \
cmake \
git \
libxml2-dev \
+71 -118
View File
@@ -1,23 +1,22 @@
---
content_title: Crypto Extensions
content_title: Crypto Extensions API
---
As of `v3.0` crypto host functions were extended to include
- `mod_exp`: Big integer modular exponentiation
- `alt_bn128_add`, `alt_bn128_mul`, `alt_bn128_pair`: Add, multiply, and pairing check functions for the `alt_bn128` elliptic curve
- `blake2_f`: `BLAKE2b F` compression function
- `sha3`: sha3` hash function using `SHA3 NIST`
- `Keccak256`: `sha3` hash function using `SHA3 Keccak`
- `k1_recover`: Safe `ECDSA` uncompressed pubkey recover for the `secp256k1` curve
In `v3.0`, `C` format was supported; in `v3.1`, `C++` format was added for better data abstraction.
Antelope blockchain implements cryptographic functions for operations on points on elliptic curves, computing hashes
of big integers, big integer modular exponentiation, and other operations useful for implementing
cryptographic algorithms in your contracts.
## Prerequisites
In order to use the Crypto Extensions API you need to activate a protocol feature `CRYPTO_PRIMITIVES`
in `nodeos`. To do this you should call the following command in your command line:
- In `nodeos`, activate protocol feature `CRYPTO_PRIMITIVES` (`68d6405cb8df3de95bd834ebb408196578500a9f818ff62ccc68f60b932f7d82`)
- In smart contract code, include `crypto_ext.hpp`
`"cleos push action eosio activate ["6bcb40a24e49c26d0a60513b6aeb8551d264e4717f306b81a37a5afb3b47cedc"] -p eosio@active"`
## C Format
And you need to include in the source code of your contract the following header: [crypto_ext.hpp](https://github.com/AntelopeIO/cdt/blob/main/libraries/eosiolib/core/eosio/crypto_ext.hpp)
The header declares following basic plain C functions which implement the core functionality:
## Plain C API
- `int32_t alt_bn128_add( const char* op1, uint32_t op1_len, const char* op2, uint32_t op2_len, char* result, uint32_t result_len )`
Perform addition operation on the elliptic curve `alt_bn128`, store the result in `result`, and return `0` if success otherwise `-1`
@@ -40,154 +39,92 @@ Test if the SHA3 keccak hash generated from data matches the provided digest
- `int32_t k1_recover( const char* sig, uint32_t sig_len, const char* dig, uint32_t dig_len, char* pub, uint32_t pub_len )`
Calculates the uncompressed public key used for a given signature on a given digest. Return `0` if success otherwise `-1`
## C++ Format
C++ types were added to represent `G1` and `G2` points (read and write) and views (read-only), and represent big integers. Their definitions are
Also, the header contains a set of handy C++ wrappers to simplify the contracts code.
### Types
## C++ API
`ec_point` type was added to represent `G1` and `G2` points on the elliptic curve.
`ec_point_view` type was added to provide a read-only interface for access to `G1` and `G2` points data.
### Data Types
```c++
/**
* Abstracts mutable G1 and G2 points
*
*/
// Abstracts mutable G1 and G2 points
template <std::size_t Size = 32>
struct ec_point {
/**
* Bytes of the x coordinate
*/
std::vector<char> x;
/**
* Bytes of the y coordinate
*/
std::vector<char> y;
/**
* Construct a point given x and y
*
* @param x_ - The x coordinate, a vector of chars
* @param y_ - The y coordinate, a vector of chars
*/
std::vector<char> x; // x coordinate
std::vector<char> y; // y coordinate
// Construct from two coordinates represented by big integers
ec_point(std::vector<char>& x_, std::vector<char>& y_);
/**
* Construct a point given a serialized point
*
* @param p - The serialized point
*/
// Construct from a serialized point
ec_point(std::vector<char>& p);
/**
* Return serialzed point containing only x and y
*/
// Return a serialized point containing only x and y
std::vector<char> serialized() const;
};
/**
* Abstracts read-only G1 and G2 points
*/
// Abstracts read-only G1 and G2 points
template <std::size_t Size = 32>
struct ec_point_view {
/**
* Pointer to the x coordinate
*/
const char* x;
/**
* Pointer to the y coordinate
*/
const char* y;
/**
* Number of bytes in each of x and y
*/
// Number of bytes in each of x and y
uint32_t size;
/**
* Construct a point view from x and y
*
* @param x_ - The x coordinate, poiter to chars
* @param x_size - x's size
* @param y_ - The y coordinate, poiter to chars
* @param y_size - y's size
*/
// Construct from two coordinates. Their sizes must be equal
ec_point_view(const char* x_, uint32_t x_size, const char* y_, uint32_t y_size);
/**
* Construct a point view from a serialized point
*
* @param p - The serialized point
*/
// Construct from a serialized point
ec_point_view(const std::vector<char>& p);
/**
* Construct a point view from a point
*
* @param p - The point
*/
// Construct a point view from a point
ec_point_view(const ec_point<Size>& p);
/**
* Return serialzed point containing only x and y
*/
// Return serialized point containing only x and y
std::vector<char> serialized() const;
};
static constexpr size_t g1_coordinate_size = 32;
static constexpr size_t g2_coordinate_size = 64;
using g1_point = ec_point<g1_coordinate_size>;
using g2_point = ec_point<g2_coordinate_size>;
using g1_point_view = ec_point_view<g1_coordinate_size>;
using g2_point_view = ec_point_view<g2_coordinate_size>;
/**
* Big integer.
*
* @ingroup crypto
*/
using bigint = std::vector<char>;
// Big integer.
using bigint = std::vector<char>;
```
### Methods
- `alt_bn128_add`
- Addition operation on the elliptic curve `alt_bn128`
```c++
template <typename T>
g1_point alt_bn128_add( const T& op1, const T& op2 )
```
Take two G1 points or G1 views as input and return a G1 point.
- `alt_bn128_mul`
- Scalar multiplication operation on the elliptic curve `alt_bn128`
```c++
template <typename T>
g1_point alt_bn128_mul( const T& g1, const bigint& scalar)
```
Take a G1 point or view and a bigint as input and return a G1 point
- `alt_bn128_pair`
- Optimal-Ate pairing check elliptic curve `alt_bn128`
```c++
template <typename G1_T, typename G2_T>
int32_t alt_bn128_pair( const std::vector<std::pair<G1_T, G2_T>>& pairs )
```
Take a pair of G1 and G2 as input.
- `mod_exp`
- Big integer modular exponentiation returns `( BASE^EXP ) % MOD`
```c++
int32_t mod_exp( const bigint& base, const bigint& exp, const bigint& mod, bigint& result )
```
Take bigints as input
Please take a look into a [crypto_ext.hpp](https://github.com/AntelopeIO/cdt/blob/main/libraries/eosiolib/core/eosio/crypto_ext.hpp)
header file, it contains more wrappers and helper functions which may be useful in your code.
### Examples
- `alt_bn128_add`
```c++
std::vector<char> x1, y1, x2, y2;
std::vector<char> x1, y1, x2, y2; // Declare coordinates for points on an elliptic curve
// point
// Create the points on the curve
eosio::g1_point point1 {x1, y1};
eosio::g1_point point2 {x2, y2};
// Add two points and get a third point on the curve as result
auto result = eosio::alt_bn128_add(point1, point2);
// view
// Do the same addition but with g1_point_view data types
eosio::g1_point_view point_view1 {x1.data(), x1.size(), y1.data(), y1.size()};
eosio::g1_point_view point_view2 {x2.data(), x2.size(), y2.data(), y2.size()};
result = eosio::alt_bn128_add(point_view1, point_view2);
@@ -195,32 +132,45 @@ Take bigints as input
- `alt_bn128_mul`
```c++
std::vector<char> x, y, scaler;
// Declare coordinates for a point on an elliptic curve and a big integer
std::vector<char> x, y, scalar;
eosio::bigint s {scalar};
// point
// Create the point object with given coordinates
eosio::g1_point g1_point {x, y};
// Multiply the point with a big integer and get a point on the curve as result
auto result = eosio::alt_bn128_mul(g1_point, s);
// view
// Do the same multiplication but with g1_point_view data type
eosio::g1_point_view g1_view {x.data(), x.size(), y.data(), y.size()};
result = eosio::alt_bn128_mul(g1_view, s);
```
- `alt_bn128_pair`
```c++
// Declare coordinates for the pairs of points on an elliptic curve
std::vector<char> g1_a_x, g1_a_y, g2_a_x, g2_a_y, g1_b_x, g1_b_y, g2_b_x, g2_b_y;
// point
// Create the point object by given coordinates
// First pair
eosio::g1_point g1_a {g1_a_x, g1_a_y};
eosio::g2_point g2_a {g2_a_x, g2_a_y};
// Second pair
eosio::g1_point g1_b {g1_b_x, g1_b_y};
eosio::g2_point g2_b {g2_b_x, g2_b_y};
// Create the pairs object
std::vector<std::pair<eosio::g1_point, eosio::g2_point>> pairs { {g1_a, g2_a}, {g1_b, g2_b} };
// Perform the pairing check
// Return:
// -1 if there is an error
// 1 if the points not in a target group
// 0 if the points in a target group
auto result = eosio::alt_bn128_pair(pairs);
// view
// Do the same pairing check but with g1_point_view and g2_point_view data types
eosio::g1_point_view g1_view_a {g1_a_x.data(), g1_a_x.size(), g1_a_y.data(), g1_a_y.size()};
eosio::g2_point_view g2_view_a {g2_a_x.data(), g2_a_x.size(), g2_a_y.data(), g2_a_y.size()};
eosio::g1_point_view g1_view_b {g1_b_x.data(), g1_b_x.size(), g1_b_y.data(), g1_b_y.size()};
@@ -231,11 +181,14 @@ Take bigints as input
- `mod_exp`
```c++
// Declare big integer variables for the base, the exponent and the modulo
std::vector<char> base, exp, modulo;
eosio::bigint base_val {base};
eosio::bigint exp_val {exp};
eosio::bigint modulo_val {modulo};
// Declare and init a big integer variable for the result of exponentiation
eosio::bigint result( modulo.size(), '\0' );
// Perform the exponentiation
// return -1 if there is an error otherwise 0
auto rc = eosio::mod_exp(base_val, exp_val, modulo_val, result);
```
@@ -732,11 +732,21 @@ class multi_index
_multidx->modify( *itr, payer, std::forward<Lambda&&>(updater) );
}
template<typename Lambda>
void update( const_iterator itr, eosio::name payer, Lambda&& updater ) {
modify( itr, payer, std::forward<Lambda>(updater) );
}
template<typename Lambda>
void modify( const T& obj, eosio::name payer, Lambda&& updater ) {
_multidx->modify( obj, payer, std::forward<Lambda&&>(updater) );
}
template<typename Lambda>
void update( T& obj, eosio::name payer, Lambda&& updater ) {
_multidx->update( obj, payer, std::forward<Lambda&&>(updater) );
}
const_iterator erase( const_iterator itr ) {
eosio::check( itr != cend(), "cannot pass end iterator to erase" );
@@ -748,6 +758,10 @@ class multi_index
return itr;
}
const_iterator remove( const_iterator itr ) {
return erase(itr);
}
eosio::name get_code()const { return _multidx->get_code(); }
uint64_t get_scope()const { return _multidx->get_scope(); }
@@ -841,6 +855,56 @@ class multi_index
return *ptr;
} /// load_object_by_primary_iterator
template<typename Lambda>
void modify_object(T& obj, name payer, Lambda&& updater) {
using namespace _multi_index_detail;
auto& objitem = static_cast<item&>(obj);
eosio::check( objitem.__idx == this, "object passed to modify is not in multi_index" );
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_type{}, obj);
uint64_t pk = to_raw_key(obj.primary_key());
updater( obj );
eosio::check( pk == to_raw_key(obj.primary_key()), "updater cannot change primary key when modifying an object" );
size_t size = pack_size( obj );
//using malloc/free here potentially is not exception-safe, although WASM doesn't support exceptions
void* buffer = max_stack_buffer_size < size ? malloc(size) : alloca(size);
datastream<char*> ds( (char*)buffer, size );
ds << obj;
internal_use_do_not_use::db_update_i64( objitem.__primary_itr, payer.value, buffer, size );
if ( max_stack_buffer_size < size ) {
free( buffer );
}
if( pk >= _next_primary_key )
_next_primary_key = (pk >= no_available_primary_key) ? no_available_primary_key : (pk + 1);
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 = objitem.__iters[index_type::number()];
if( indexitr < 0 ) {
typename index_type::secondary_key_type temp_secondary_key;
indexitr = objitem.__iters[index_type::number()]
= secondary_index_db_functions<typename index_type::secondary_key_type>::db_idx_find_primary( _code.value, _scope, index_type::name(), pk, temp_secondary_key );
}
secondary_index_db_functions<typename index_type::secondary_key_type>::db_idx_update( indexitr, payer.value, secondary );
}
} );
}
public:
/**
* Constructs an instance of a Multi-Index table.
@@ -1531,6 +1595,7 @@ class multi_index
eosio::check( objitem.__idx == this, "object passed to iterator_to is not in multi_index" );
return {this, &objitem};
}
/**
* Adds a new object (i.e., row) to the table.
* @ingroup multiindex
@@ -1613,6 +1678,48 @@ class multi_index
return {this, ptr};
}
/**
* Adds a new object (i.e., row) to the table, alias for emplace()
* @ingroup multiindex
*
* @param payer - Account name of the payer for the Storage usage of the new object
* @param constructor - Lambda function that does an in-place initialization of the object to be created in the table
*
* @pre A multi index table has been instantiated
* @post A new object is created in the Multi-Index table, with a unique primary key (as specified in the object). The object is serialized and written to the table. If the table does not exist, it is created.
* @post Secondary indices are updated to refer to the newly added object. If the secondary index tables do not exist, they are created.
* @post The payer is charged for the storage usage of the new object and, if the table (and secondary index tables) must be created, for the overhead of the table creation.
*
* @return A primary key iterator to the newly created object
*
* Exception - The account is not authorized to write to the table.
*
* Example:
*
* @code
* // This assumes the code from the constructor example. Replace myaction() {...}
*
* void myaction() {
* address_index addresses(_self, _self.value); // code, scope
* // add to table, first argument is account to bill for storage
* addresses.insert(_self, [&](auto& address) {
* address.account_name = "dan"_n;
* address.first_name = "Daniel";
* address.last_name = "Larimer";
* address.street = "1 EOS Way";
* address.city = "Blacksburg";
* address.state = "VA";
* });
* }
* }
* EOSIO_DISPATCH( addressbook, (myaction) )
* @endcode
*/
template<typename Lambda>
const_iterator insert( name payer, Lambda&& constructor ) {
return emplace(payer, std::forward<Lambda>(constructor));
}
/**
* Modifies an existing object in a table.
* @ingroup multiindex
@@ -1660,6 +1767,51 @@ class multi_index
modify( *itr, payer, std::forward<Lambda&&>(updater) );
}
/**
* Updates an existing object in a table, alias for modify()
* @ingroup multiindex
*
* @param itr - an iterator pointing to the object to be updated
* @param payer - account name of the payer for the storage usage of the updated row
* @param updater - lambda function that updates the target object
*
* @pre itr points to an existing element
* @pre payer is a valid account that is authorized to execute the action and be billed for storage usage.
*
* @post The updated object is serialized, then replaces the existing object in the table.
* @post Secondary indices are updated; the primary key of the updated object is not changed.
* @post The payer is charged for the storage usage of the updated object.
* @post If payer is the same as the existing payer, payer only pays for the usage difference between existing and updated object (and is refunded if this difference is negative).
* @post If payer is different from the existing payer, the existing payer is refunded for the storage usage of the existing object.
*
* Exceptions:
* If called with an invalid precondition, execution is aborted.
*
* Example:
*
* @code
* // This assumes the code from the constructor example. Replace myaction() {...}
*
* void myaction() {
* // create reference to address_index - see emplace example
* // add dan account to table - see emplace example
*
* auto itr = addresses.find("dan"_n);
* eosio::check(itr != addresses.end(), "Address for account not found");
* addresses.update( itr, account payer, [&]( auto& address ) {
* address.city = "San Luis Obispo";
* address.state = "CA";
* });
* }
* }
* EOSIO_DISPATCH( addressbook, (myaction) )
* @endcode
*/
template<typename Lambda>
void update( const_iterator itr, name payer, Lambda&& updater ) {
modify( itr, payer, std::forward<Lambda>(updater) );
}
/**
* Modifies an existing object in a table.
* @ingroup multiindex
@@ -1703,53 +1855,55 @@ class multi_index
*/
template<typename Lambda>
void modify( const T& obj, name payer, Lambda&& updater ) {
using namespace _multi_index_detail;
T& mutable_obj = const_cast<T&>(obj);
const auto& objitem = static_cast<const item&>(obj);
eosio::check( objitem.__idx == this, "object passed to modify is not in 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.
modify_object(mutable_obj, payer, std::forward<Lambda>(updater));
}
auto secondary_keys = make_extractor_tuple::get_extractor_tuple(indices_type{}, obj);
uint64_t pk = _multi_index_detail::to_raw_key(obj.primary_key());
auto& mutableobj = const_cast<T&>(obj); // Do not forget the auto& otherwise it would make a copy and thus not update at all.
updater( mutableobj );
eosio::check( pk == _multi_index_detail::to_raw_key(obj.primary_key()), "updater cannot change primary key when modifying an object" );
size_t size = pack_size( obj );
//using malloc/free here potentially is not exception-safe, although WASM doesn't support exceptions
void* buffer = max_stack_buffer_size < size ? malloc(size) : alloca(size);
datastream<char*> ds( (char*)buffer, size );
ds << obj;
internal_use_do_not_use::db_update_i64( objitem.__primary_itr, payer.value, buffer, size );
if ( max_stack_buffer_size < size ) {
free( buffer );
}
if( pk >= _next_primary_key )
_next_primary_key = (pk >= no_available_primary_key) ? no_available_primary_key : (pk + 1);
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()];
if( indexitr < 0 ) {
typename index_type::secondary_key_type temp_secondary_key;
indexitr = mutableitem.__iters[index_type::number()]
= secondary_index_db_functions<typename index_type::secondary_key_type>::db_idx_find_primary( _code.value, _scope, index_type::name(), pk, temp_secondary_key );
}
secondary_index_db_functions<typename index_type::secondary_key_type>::db_idx_update( indexitr, payer.value, secondary );
}
} );
/**
* Updates an existing object in a table, alias for modify()
* @ingroup multiindex
*
* @param obj - a reference to the object to be updated
* @param payer - account name of the payer for the storage usage of the updated row
* @param updater - lambda function that updates the target object
*
* @pre obj is an existing object in the table
* @pre payer is a valid account that is authorized to execute the action and be billed for storage usage.
*
* @post The modified object is serialized, then replaces the existing object in the table.
* @post Secondary indices are updated; the primary key of the updated object is not changed.
* @post The payer is charged for the storage usage of the updated object.
* @post If payer is the same as the existing payer, payer only pays for the usage difference between existing and updated object (and is refunded if this difference is negative).
* @post If payer is different from the existing payer, the existing payer is refunded for the storage usage of the existing object.
*
* Exceptions:
* If called with an invalid precondition, execution is aborted.
*
* Example:
*
* @code
* // This assumes the code from the constructor example. Replace myaction() {...}
*
* void myaction() {
* // create reference to address_index - see emplace example
* // add dan account to table - see emplace example
*
* auto itr = addresses.find("dan"_n);
* eosio::check(itr != addresses.end(), "Address for account not found");
* addresses.update( *itr, payer, [&]( auto& address ) {
* address.city = "San Luis Obispo";
* address.state = "CA";
* });
* eosio::check(itr->city == "San Luis Obispo", "Lock arf, Address not modified");
* }
* }
* EOSIO_DISPATCH( addressbook, (myaction) )
* @endcode
*/
template<typename Lambda>
void update( T& obj, name payer, Lambda&& updater ) {
modify_object(obj, payer, std::forward<Lambda>(updater));
}
/**
@@ -1902,6 +2056,46 @@ class multi_index
return itr;
}
/**
* Deletes an existing object from a table using its primary key, alias for erase()
* @ingroup multiindex
*
* @param itr - An iterator pointing to the object to be removed
*
* @pre itr points to an existing element
* @post The object is removed from the table and all associated storage is reclaimed.
* @post Secondary indices associated with the table are updated.
* @post The existing payer for storage usage of the object is refunded for the table and secondary indices usage of the removed object, and if the table and indices are removed, for the associated overhead.
*
* @return For the signature with `const_iterator`, returns a pointer to the object following the removed object.
*
* Exceptions:
* The object to be removed is not in the table.
* The action is not authorized to modify the table.
* The given iterator is invalid.
*
* Example:
*
* @code
* // This assumes the code from the constructor example. Replace myaction() {...}
*
* void myaction() {
* // create reference to address_index - see emplace example
* // add dan account to table - see emplace example
*
* auto itr = addresses.find("dan"_n);
* eosio::check(itr != addresses.end(), "Address for account not found");
* addresses.delete( itr );
* eosio::check(itr != addresses.end(), "Everting lock arf, Address not erased properly");
* }
* }
* EOSIO_ABI( addressbook, (myaction) )
* @endcode
*/
const_iterator remove( const_iterator itr ) {
return erase(itr);
}
/**
* Remove an existing object from a table using its primary key.
* @ingroup multiindex
@@ -1965,5 +2159,41 @@ class multi_index
_items_vector.erase(--(itr2.base()));
}
/**
* Remove an existing object from a table using its primary key, alias for erase()
* @ingroup multiindex
*
* @param obj - Object to be removed
*
* @pre obj is an existing object in the table
* @post The object is removed from the table and all associated storage is reclaimed.
* @post Secondary indices associated with the table are updated.
* @post The existing payer for storage usage of the object is refunded for the table and secondary indices usage of the removed object, and if the table and indices are removed, for the associated overhead.
*
* Exceptions:
* The object to be removed is not in the table.
* The action is not authorized to modify the table.
* The given iterator is invalid.
*
* Example:
*
* @code
* // This assumes the code from the constructor example. Replace myaction() {...}
*
* void myaction() {
* auto itr = addresses.find("dan"_n);
* eosio::check(itr != addresses.end(), "Record is not found");
* addresses.remove(*itr);
* itr = addresses.find("dan"_n);
* eosio::check(itr == addresses.end(), "Record is not deleted");
* }
* }
* EOSIO_DISPATCH( addressbook, (myaction) )
* @endcode
*/
void remove( const T& obj ) {
erase(obj);
}
};
} /// eosio
@@ -204,6 +204,60 @@ namespace _test_multi_index
auto itr2 = table.find(ssn);
eosio::check(itr2 == table.end(), "idx64_general - table.erase()");
}
// insert, update and remove by iterator
{
const uint64_t ssn = 421;
auto new_person = table.insert(payer, [&](auto &r)
{
r.id = ssn;
r.sec = "bob"_n.value; });
table.update(new_person, payer, [&](auto &r)
{ r.sec = "billy"_n.value; });
auto itr1 = table.find(ssn);
eosio::check(itr1 != table.end() && itr1->sec == "billy"_n.value, "idx64_general - table.update()");
table.remove(itr1);
auto itr2 = table.find(ssn);
eosio::check(itr2 == table.end(), "idx64_general - table.remove()");
}
// insert, update and remove by object
{
const uint64_t ssn = 421;
auto new_person = table.insert(payer, [&](auto &r)
{
r.id = ssn;
r.sec = "bob"_n.value; });
table.update(new_person, payer, [&](auto &r)
{ r.sec = "billy"_n.value; });
const auto& person_object = table.get(ssn);
auto& mutable_person = const_cast<record&>(person_object);
table.remove(mutable_person);
auto itr2 = table.find(ssn);
eosio::check(itr2 == table.end(), "idx64_general - table.remove()");
}
// insert, update and remove by const object
{
const uint64_t ssn = 421;
auto new_person = table.insert(payer, [&](auto &r)
{
r.id = ssn;
r.sec = "bob"_n.value; });
table.update(new_person, payer, [&](auto &r)
{ r.sec = "billy"_n.value; });
const auto& person_object = table.get(ssn);
table.remove(person_object);
auto itr2 = table.find(ssn);
eosio::check(itr2 == table.end(), "idx64_general - table.remove()");
}
}
template <uint64_t TableName>