提交 3f94e2a5 编写于 作者: B Bart Wyatt

restored db table objects, extracted scope,code,table triplet from tables into...

restored db table objects, extracted scope,code,table triplet from tables into a table id, moved EOSIO tokens to use the same data model as wasm token contracts
上级 eb8387ca
......@@ -181,4 +181,25 @@ void apply_context::deferred_transaction_send( uint32_t id ) {
// _pending_deferred_transactions.erase(itr);
}
const contracts::table_id_object* apply_context::find_table( name scope, name code, name table ) {
require_read_scope(scope);
return db.find<table_id_object, contracts::by_scope_code_table>(boost::make_tuple(scope, code, table));
}
const contracts::table_id_object& apply_context::find_or_create_table( name scope, name code, name table ) {
require_read_scope(scope);
const auto* existing_tid = db.find<contracts::table_id_object, contracts::by_scope_code_table>(boost::make_tuple(scope, code, table));
if (existing_tid != nullptr) {
return *existing_tid;
}
require_write_scope(scope);
return mutable_db.create<contracts::table_id_object>([&](contracts::table_id_object &t_id){
t_id.scope = scope;
t_id.code = code;
t_id.table = table;
});
}
} } /// eosio::chain
......@@ -8,7 +8,7 @@
#include <eosio/chain/block_summary_object.hpp>
#include <eosio/chain/global_property_object.hpp>
#include <eosio/chain/key_value_object.hpp>
#include <eosio/chain/contracts/contract_table_objects.hpp>
#include <eosio/chain/action_objects.hpp>
#include <eosio/chain/generated_transaction_object.hpp>
#include <eosio/chain/transaction_object.hpp>
......@@ -951,10 +951,11 @@ void chain_controller::_initialize_indexes() {
_db.add_index<permission_index>();
_db.add_index<permission_link_index>();
_db.add_index<action_permission_index>();
_db.add_index<key_value_index>();
_db.add_index<keystr_value_index>();
_db.add_index<key128x128_value_index>();
_db.add_index<key64x64x64_value_index>();
_db.add_index<contracts::table_id_multi_index>();
_db.add_index<contracts::key_value_index>();
_db.add_index<contracts::keystr_value_index>();
_db.add_index<contracts::key128x128_value_index>();
_db.add_index<contracts::key64x64x64_value_index>();
_db.add_index<global_property_multi_index>();
_db.add_index<dynamic_global_property_multi_index>();
......
......@@ -37,8 +37,6 @@ void chain_initializer::register_types(chain_controller& chain, chainbase::datab
db.add_index<proxy_vote_multi_index>();
db.add_index<producer_schedule_multi_index>();
db.add_index<balance_multi_index>();
#define SET_APP_HANDLER( contract, scope, action, nspace ) \
chain._set_apply_handler( #contract, #scope, #action, &BOOST_PP_CAT(contracts::apply_, BOOST_PP_CAT(contract, BOOST_PP_CAT(_,action) ) ) )
......@@ -196,6 +194,10 @@ abi_def chain_initializer::eos_contract_abi()
return eos_abi;
}
// forward declared method from eosio contract
void intialize_eosio_tokens(chainbase::database& db, const account_name& system_account, share_type initial_tokens);
std::vector<action> chain_initializer::prepare_database( chain_controller& chain,
chainbase::database& db) {
std::vector<action> messages_to_process;
......@@ -224,10 +226,7 @@ std::vector<action> chain_initializer::prepare_database( chain_controller& chain
p.name = "active";
p.auth.threshold = 1;
});
db.create<balance_object>([&name, liquid_balance]( auto& b) {
b.owner_name = name;
b.balance = liquid_balance;
});
intialize_eosio_tokens(db, name, liquid_balance);
db.create<staked_balance_object>([&](auto& sb) { sb.owner_name = name; });
idump(("create bandwidth_usage_object")(name));
db.create<bandwidth_usage_object>([&](auto& sb) { sb.owner = name; });
......
......@@ -3,6 +3,7 @@
* @copyright defined in eos/LICENSE.txt
*/
#include <eosio/chain/contracts/eos_contract.hpp>
#include <eosio/chain/contracts/contract_table_objects.hpp>
#include <eosio/chain/chain_controller.hpp>
#include <eosio/chain/apply_context.hpp>
......@@ -10,7 +11,6 @@
#include <eosio/chain/exceptions.hpp>
#include <eosio/chain/account_object.hpp>
#include <eosio/chain/contracts/balance_object.hpp>
#include <eosio/chain/permission_object.hpp>
#include <eosio/chain/permission_link_object.hpp>
#include <eosio/chain/global_property_object.hpp>
......@@ -26,6 +26,47 @@
namespace eosio { namespace chain { namespace contracts {
void intialize_eosio_tokens(chainbase::database& db, const account_name& system_account, share_type initial_tokens) {
const auto& t_id = db.create<contracts::table_id_object>([&](contracts::table_id_object &t_id){
t_id.scope = system_account;
t_id.code = config::system_account_name;
t_id.table = N(currency);
});
db.create<key_value_object>([&](key_value_object &o){
o.t_id = t_id.id;
o.primary_key = N(account);
o.value.insert(0, reinterpret_cast<const char *>(&initial_tokens), sizeof(share_type));
});
}
static void modify_eosio_balance( apply_context& context, const account_name& account, share_type amt) {
const auto& t_id = context.find_or_create_table(account, config::system_account_name, N(currency));
uint64_t key = N(account);
share_type balance = 0;
context.front_record<key_value_index, by_scope_primary>(t_id, &key, (char *)&balance, sizeof(balance));
balance += amt;
context.store_record<key_value_object>(t_id, &key, (const char *)&balance, sizeof(balance));
}
share_type get_eosio_balance( const chainbase::database& db, const account_name &account ) {
const auto* t_id = db.find<table_id_object, by_scope_code_table>(boost::make_tuple(account, config::system_account_name, N(currency)));
if (!t_id) {
return share_type(0);
}
const auto& idx = db.get_index<key_value_index, by_scope_primary>();
auto itr = idx.lower_bound(boost::make_tuple(t_id->id));
if ( itr == idx.end() || itr->t_id != t_id->id ) {
return share_type(0);
}
FC_ASSERT(itr->value.size() == sizeof(share_type), "Invalid data in EOSIO balance table");
return *reinterpret_cast<const share_type *>(itr->value.data());
}
void validate_authority_precondition( const apply_context& context, const authority& auth ) {
for(const auto& a : auth.accounts) {
context.db.get<account_object, by_name>(a.permission.actor);
......@@ -78,20 +119,13 @@ void apply_eosio_newaccount(apply_context& context) {
p.auth = std::move(create.active);
});
const auto& creator_balance = context.mutable_db.get<balance_object, by_owner_name>(create.creator);
share_type creator_balance = get_eosio_balance(context.db, create.creator);
EOS_ASSERT(creator_balance.balance >= create.deposit.amount, action_validate_exception,
EOS_ASSERT(creator_balance >= create.deposit.amount, action_validate_exception,
"Creator '${c}' has insufficient funds to make account creation deposit of ${a}",
("c", create.creator)("a", create.deposit));
context.mutable_db.modify(creator_balance, [&create](balance_object& b) {
b.balance -= create.deposit.amount;
});
context.mutable_db.create<balance_object>([&create](balance_object& b) {
b.owner_name = create.name;
b.balance = 0; //create.deposit.amount; TODO: make sure we credit this in @staked
});
modify_eosio_balance(context, create.creator, -create.deposit.amount);
context.mutable_db.create<staked_balance_object>([&]( staked_balance_object& sbo) {
sbo.owner_name = create.name;
......@@ -127,18 +161,14 @@ void apply_eosio_transfer(apply_context& context) {
try {
auto& db = context.mutable_db;
const auto& from = db.get<balance_object, by_owner_name>(transfer.from);
share_type from_balance = get_eosio_balance(db, transfer.from);
EOS_ASSERT(from.balance >= transfer.amount, action_validate_exception, "Insufficient Funds",
("from.balance",from.balance)("transfer.amount",transfer.amount));
EOS_ASSERT(from_balance >= transfer.amount, action_validate_exception, "Insufficient Funds",
("from_balance", from_balance)("transfer.amount",transfer.amount));
modify_eosio_balance(context, transfer.from, - share_type(transfer.amount) );
modify_eosio_balance(context, transfer.to, share_type(transfer.amount) );
const auto& to = db.get<balance_object, by_owner_name>(transfer.to);
db.modify(from, [&](balance_object& a) {
a.balance -= share_type(transfer.amount);
});
db.modify(to, [&](balance_object& a) {
a.balance += share_type(transfer.amount);
});
} FC_CAPTURE_AND_RETHROW( (transfer) )
}
///@}
......@@ -160,14 +190,12 @@ void apply_eosio_lock(apply_context& context) {
context.require_recipient(lock.to);
context.require_recipient(lock.from);
const auto& locker = context.db.get<balance_object, by_owner_name>(lock.from);
share_type locker_balance = get_eosio_balance(context.db, lock.from);
EOS_ASSERT( locker.balance >= lock.amount, action_validate_exception,
"Account ${a} lacks sufficient funds to lock ${amt} EOS", ("a", lock.from)("amt", lock.amount)("available",locker.balance) );
EOS_ASSERT( locker_balance >= lock.amount, action_validate_exception,
"Account ${a} lacks sufficient funds to lock ${amt} EOS", ("a", lock.from)("amt", lock.amount)("available",locker_balance) );
context.mutable_db.modify(locker, [&lock](balance_object& a) {
a.balance -= lock.amount;
});
modify_eosio_balance(context, lock.from, -share_type(lock.amount));
const auto& balance = context.db.get<staked_balance_object, by_owner_name>(lock.to);
balance.stake_tokens(lock.amount, context.mutable_db);
......@@ -261,10 +289,7 @@ void apply_eosio_claim(apply_context& context) {
const auto& staked_balance = context.db.get<staked_balance_object, by_owner_name>(claim.account);
staked_balance.finish_unstaking_tokens(claim.amount, context.mutable_db);
const auto& liquid_balance = context.db.get<balance_object, by_owner_name>(claim.account);
context.mutable_db.modify(liquid_balance, [&claim](balance_object& a) {
a.balance += claim.amount;
});
modify_eosio_balance(context, claim.account, share_type(claim.amount));
}
......
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#pragma once
#include <eosio/chain/types.hpp>
#include <eosio/chain/multi_index_includes.hpp>
#include <chainbase/chainbase.hpp>
namespace eosio { namespace chain { namespace contracts {
/**
* @brief The balance_object class tracks the EOS balance for accounts
*/
class balance_object : public chainbase::object<balance_object_type, balance_object> {
OBJECT_CTOR(balance_object)
id_type id;
account_name owner_name;
share_type balance = 0;
};
struct by_owner_name;
using balance_multi_index = chainbase::shared_multi_index_container<
balance_object,
indexed_by<
ordered_unique<tag<by_id>,
member<balance_object, balance_object::id_type, &balance_object::id>
>,
ordered_unique<tag<by_owner_name>,
member<balance_object, account_name, &balance_object::owner_name>
>
>
>;
} } } // namespace eosio::chain::contracts
CHAINBASE_SET_INDEX_TYPE(eosio::chain::contracts::balance_object, eosio::chain::contracts::balance_multi_index)
......@@ -3,11 +3,45 @@
* @copyright defined in eos/LICENSE.txt
*/
#pragma once
#include <eosio/chain/types.hpp>
#include "multi_index_includes.hpp"
#include <eosio/chain/contracts/types.hpp>
#include <eosio/chain/multi_index_includes.hpp>
namespace eosio { namespace chain {
#include <chainbase/chainbase.hpp>
namespace eosio { namespace chain { namespace contracts {
/**
* @brief The table_id_object class tracks the mapping of (scope, code, table) to an opaque identifier
*/
class table_id_object : public chainbase::object<table_id_object_type, table_id_object> {
OBJECT_CTOR(table_id_object)
id_type id;
scope_name scope;
account_name code;
table_name table;
};
struct by_scope_code_table;
using table_id_multi_index = chainbase::shared_multi_index_container<
table_id_object,
indexed_by<
ordered_unique<tag<by_id>,
member<table_id_object, table_id_object::id_type, &table_id_object::id>
>,
ordered_unique<tag<by_scope_code_table>,
composite_key< table_id_object,
member<table_id_object, scope_name, &table_id_object::scope>,
member<table_id_object, account_name, &table_id_object::code>,
member<table_id_object, table_name, &table_id_object::table>
>
>
>
>;
using table_id = table_id_object::id_type;
struct by_scope_primary;
struct by_scope_secondary;
......@@ -15,14 +49,12 @@ namespace eosio { namespace chain {
struct key_value_object : public chainbase::object<key_value_object_type, key_value_object> {
OBJECT_CTOR(key_value_object, (value))
typedef uint64_t key_type;
static const int number_of_keys = 1;
id_type id;
account_name scope;
account_name code;
account_name table;
table_id t_id;
uint64_t primary_key;
shared_string value;
};
......@@ -31,14 +63,12 @@ namespace eosio { namespace chain {
key_value_object,
indexed_by<
ordered_unique<tag<by_id>, member<key_value_object, key_value_object::id_type, &key_value_object::id>>,
ordered_unique<tag<by_scope_primary>,
ordered_unique<tag<by_scope_primary>,
composite_key< key_value_object,
member<key_value_object, account_name, &key_value_object::scope>,
member<key_value_object, account_name, &key_value_object::code>,
member<key_value_object, account_name, &key_value_object::table>,
member<key_value_object, table_id, &key_value_object::t_id>,
member<key_value_object, uint64_t, &key_value_object::primary_key>
>,
composite_key_compare< std::less<account_name>,std::less<account_name>,std::less<account_name>,std::less<uint64_t> >
composite_key_compare< std::less<table_id>, std::less<uint64_t> >
>
>
>;
......@@ -63,16 +93,14 @@ namespace eosio { namespace chain {
struct keystr_value_object : public chainbase::object<keystr_value_object_type, keystr_value_object> {
OBJECT_CTOR(keystr_value_object, (primary_key)(value))
typedef std::string key_type;
static const int number_of_keys = 1;
const char* data() const { return primary_key.data(); }
id_type id;
account_name scope;
account_name code;
account_name table;
table_id t_id;
shared_string primary_key;
shared_string value;
};
......@@ -81,14 +109,12 @@ namespace eosio { namespace chain {
keystr_value_object,
indexed_by<
ordered_unique<tag<by_id>, member<keystr_value_object, keystr_value_object::id_type, &keystr_value_object::id>>,
ordered_unique<tag<by_scope_primary>,
ordered_unique<tag<by_scope_primary>,
composite_key< keystr_value_object,
member<keystr_value_object, account_name, &keystr_value_object::scope>,
member<keystr_value_object, account_name, &keystr_value_object::code>,
member<keystr_value_object, account_name, &keystr_value_object::table>,
member<keystr_value_object, table_id, &keystr_value_object::t_id>,
const_mem_fun<keystr_value_object, const char*, &keystr_value_object::data>
>,
composite_key_compare< std::less<account_name>,std::less<account_name>,std::less<account_name>,shared_string_less>
composite_key_compare< std::less<table_id>, shared_string_less>
>
>
>;
......@@ -100,9 +126,7 @@ namespace eosio { namespace chain {
static const int number_of_keys = 2;
id_type id;
account_name scope;
account_name code;
account_name table;
table_id t_id;
uint128_t primary_key;
uint128_t secondary_key;
shared_string value;
......@@ -112,25 +136,21 @@ namespace eosio { namespace chain {
key128x128_value_object,
indexed_by<
ordered_unique<tag<by_id>, member<key128x128_value_object, key128x128_value_object::id_type, &key128x128_value_object::id>>,
ordered_unique<tag<by_scope_primary>,
ordered_unique<tag<by_scope_primary>,
composite_key< key128x128_value_object,
member<key128x128_value_object, account_name, &key128x128_value_object::scope>,
member<key128x128_value_object, account_name, &key128x128_value_object::code>,
member<key128x128_value_object, account_name, &key128x128_value_object::table>,
member<key128x128_value_object, table_id, &key128x128_value_object::t_id>,
member<key128x128_value_object, uint128_t, &key128x128_value_object::primary_key>,
member<key128x128_value_object, uint128_t, &key128x128_value_object::secondary_key>
>,
composite_key_compare< std::less<account_name>,std::less<account_name>,std::less<account_name>,std::less<uint128_t>,std::less<uint128_t> >
composite_key_compare< std::less<table_id>,std::less<uint128_t>,std::less<uint128_t> >
>,
ordered_unique<tag<by_scope_secondary>,
ordered_unique<tag<by_scope_secondary>,
composite_key< key128x128_value_object,
member<key128x128_value_object, account_name, &key128x128_value_object::scope>,
member<key128x128_value_object, account_name, &key128x128_value_object::code>,
member<key128x128_value_object, account_name, &key128x128_value_object::table>,
member<key128x128_value_object, table_id, &key128x128_value_object::t_id>,
member<key128x128_value_object, uint128_t, &key128x128_value_object::secondary_key>,
member<key128x128_value_object, uint128_t, &key128x128_value_object::primary_key>
member<key128x128_value_object, typename key128x128_value_object::id_type, &key128x128_value_object::id>
>,
composite_key_compare< std::less<account_name>,std::less<account_name>,std::less<account_name>,std::less<uint128_t>,std::less<uint128_t> >
composite_key_compare< std::less<table_id>,std::less<uint128_t>,std::less<typename key128x128_value_object::id_type> >
>
>
>;
......@@ -142,9 +162,7 @@ namespace eosio { namespace chain {
static const int number_of_keys = 3;
id_type id;
account_name scope;
account_name code;
account_name table;
table_id t_id;
uint64_t primary_key;
uint64_t secondary_key;
uint64_t tertiary_key;
......@@ -157,20 +175,16 @@ namespace eosio { namespace chain {
ordered_unique<tag<by_id>, member<key64x64x64_value_object, key64x64x64_value_object::id_type, &key64x64x64_value_object::id>>,
ordered_unique<tag<by_scope_primary>,
composite_key< key64x64x64_value_object,
member<key64x64x64_value_object, account_name, &key64x64x64_value_object::scope>,
member<key64x64x64_value_object, account_name, &key64x64x64_value_object::code>,
member<key64x64x64_value_object, account_name, &key64x64x64_value_object::table>,
member<key64x64x64_value_object, table_id, &key64x64x64_value_object::t_id>,
member<key64x64x64_value_object, uint64_t, &key64x64x64_value_object::primary_key>,
member<key64x64x64_value_object, uint64_t, &key64x64x64_value_object::secondary_key>,
member<key64x64x64_value_object, uint64_t, &key64x64x64_value_object::tertiary_key>
>,
composite_key_compare< std::less<account_name>,std::less<account_name>,std::less<account_name>,std::less<uint64_t>,std::less<uint64_t>,std::less<uint64_t> >
composite_key_compare< std::less<table_id>,std::less<uint64_t>,std::less<uint64_t>,std::less<uint64_t> >
>,
ordered_unique<tag<by_scope_secondary>,
composite_key< key64x64x64_value_object,
member<key64x64x64_value_object, account_name, &key64x64x64_value_object::scope>,
member<key64x64x64_value_object, account_name, &key64x64x64_value_object::code>,
member<key64x64x64_value_object, account_name, &key64x64x64_value_object::table>,
member<key64x64x64_value_object, table_id, &key64x64x64_value_object::t_id>,
member<key64x64x64_value_object, uint64_t, &key64x64x64_value_object::secondary_key>,
member<key64x64x64_value_object, uint64_t, &key64x64x64_value_object::tertiary_key>,
member<key64x64x64_value_object, typename key64x64x64_value_object::id_type, &key64x64x64_value_object::id>
......@@ -178,25 +192,26 @@ namespace eosio { namespace chain {
>,
ordered_unique<tag<by_scope_tertiary>,
composite_key< key64x64x64_value_object,
member<key64x64x64_value_object, account_name, &key64x64x64_value_object::scope>,
member<key64x64x64_value_object, account_name, &key64x64x64_value_object::code>,
member<key64x64x64_value_object, account_name, &key64x64x64_value_object::table>,
member<key64x64x64_value_object, table_id, &key64x64x64_value_object::t_id>,
member<key64x64x64_value_object, uint64_t, &key64x64x64_value_object::tertiary_key>,
member<key64x64x64_value_object, typename key64x64x64_value_object::id_type, &key64x64x64_value_object::id>
>,
composite_key_compare< std::less<account_name>,std::less<account_name>,std::less<account_name>,std::less<uint64_t>,std::less<typename key64x64x64_value_object::id_type> >
>
composite_key_compare< std::less<table_id>,std::less<uint64_t>,std::less<typename key64x64x64_value_object::id_type> >
>
>
>;
} } } // namespace eosio::chain::contracts
CHAINBASE_SET_INDEX_TYPE(eosio::chain::contracts::table_id_object, eosio::chain::contracts::table_id_multi_index)
CHAINBASE_SET_INDEX_TYPE(eosio::chain::contracts::key_value_object, eosio::chain::contracts::key_value_index)
CHAINBASE_SET_INDEX_TYPE(eosio::chain::contracts::keystr_value_object, eosio::chain::contracts::keystr_value_index)
CHAINBASE_SET_INDEX_TYPE(eosio::chain::contracts::key128x128_value_object, eosio::chain::contracts::key128x128_value_index)
CHAINBASE_SET_INDEX_TYPE(eosio::chain::contracts::key64x64x64_value_object, eosio::chain::contracts::key64x64x64_value_index)
} } // eosio::chain
CHAINBASE_SET_INDEX_TYPE(eosio::chain::key_value_object, eosio::chain::key_value_index)
CHAINBASE_SET_INDEX_TYPE(eosio::chain::keystr_value_object, eosio::chain::keystr_value_index)
CHAINBASE_SET_INDEX_TYPE(eosio::chain::key128x128_value_object, eosio::chain::key128x128_value_index)
CHAINBASE_SET_INDEX_TYPE(eosio::chain::key64x64x64_value_object, eosio::chain::key64x64x64_value_index)
FC_REFLECT(eosio::chain::key_value_object, (id)(scope)(code)(table)(primary_key)(value) )
FC_REFLECT(eosio::chain::contracts::table_id_object, (id)(scope)(code)(table) )
FC_REFLECT(eosio::chain::contracts::key_value_object, (id)(t_id)(primary_key)(value) )
FC_REFLECT(eosio::chain::contracts::keystr_value_object, (id)(t_id)(primary_key)(value) )
FC_REFLECT(eosio::chain::contracts::key128x128_value_object, (id)(t_id)(primary_key)(secondary_key)(value) )
FC_REFLECT(eosio::chain::contracts::key64x64x64_value_object, (id)(t_id)(primary_key)(secondary_key)(tertiary_key)(value) )
\ No newline at end of file
......@@ -26,4 +26,6 @@ namespace eosio { namespace chain { namespace contracts {
void apply_eosio_unlinkauth(apply_context&);
void apply_eosio_nonce(apply_context&);
share_type get_eosio_balance( const chainbase::database& db, const account_name& account );
} } } /// namespace eosio::contracts
......@@ -7,5 +7,4 @@
/// @file This file #include's all database objects/indices used by the C++ native contract implementation
#include <eosio/chain/contracts/staked_balance_objects.hpp>
#include <eosio/chain/contracts/balance_object.hpp>
#include <eosio/chain/contracts/producer_objects.hpp>
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#include <eosio/chain/key_value_object.hpp>
namespace eosio { namespace chain {
/// find_tuple helper
template <typename T>
struct find_tuple {};
template <>
struct find_tuple<key_value_object> {
inline static auto get(name scope, name code, name table, uint64_t* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table),
*keys);
}
};
template <>
struct find_tuple<keystr_value_object> {
inline static auto get(name scope, name code, name table, std::string* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table),
keys->data());
}
};
template <>
struct find_tuple<key128x128_value_object> {
inline static auto get(name scope, name code, name table, uint128_t* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table),
*keys, *(keys+1) );
}
};
template <>
struct find_tuple<key64x64x64_value_object> {
inline static auto get(name scope, name code, name table, uint64_t* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table),
*keys, *(keys+1), *(keys+2) );
}
};
/// key_helper helper
template <typename T>
struct key_helper {};
template <>
struct key_helper<key_value_object> {
inline static void set(key_value_object& object, uint64_t* keys) {
object.primary_key = *keys;
}
inline static void set(uint64_t* keys, const key_value_object& object) {
*keys = object.primary_key;
}
inline static bool compare(const key_value_object& object, uint64_t* keys) {
return object.primary_key == *keys;
}
};
template <>
struct key_helper<keystr_value_object> {
inline static void set(keystr_value_object& object, std::string* keys) {
object.primary_key.assign(keys->data(), keys->size());
}
inline static void set(std::string* keys, const keystr_value_object& object) {
keys->assign(object.primary_key.data(), object.primary_key.size());
}
inline static bool compare(const keystr_value_object& object, std::string* keys) {
return !keys->compare(object.primary_key.c_str());
}
};
template <>
struct key_helper<key128x128_value_object> {
inline static auto set(key128x128_value_object& object, uint128_t* keys) {
object.primary_key = *keys;
object.secondary_key = *(keys+1);
}
inline static auto set(uint128_t* keys, const key128x128_value_object& object) {
*keys = object.primary_key;
*(keys+1) = object.secondary_key;
}
inline static bool compare(const key128x128_value_object& object, uint128_t* keys) {
return object.primary_key == *keys &&
object.secondary_key == *(keys+1);
}
};
template <>
struct key_helper<key64x64x64_value_object> {
inline static auto set(key64x64x64_value_object& object, uint64_t* keys) {
object.primary_key = *keys;
object.secondary_key = *(keys+1);
object.tertiary_key = *(keys+2);
}
inline static auto set(uint64_t* keys, const key64x64x64_value_object& object) {
*keys = object.primary_key;
*(keys+1) = object.secondary_key;
*(keys+2) = object.tertiary_key;
}
inline static bool compare(const key64x64x64_value_object& object, uint64_t* keys) {
return object.primary_key == *keys &&
object.secondary_key == *(keys+1) &&
object.tertiary_key == *(keys+2);
}
};
/// load_record_tuple helper
template <typename T, typename Q>
struct load_record_tuple {};
template <>
struct load_record_tuple<key_value_object, by_scope_primary> {
inline static auto get(name scope, name code, name table, uint64_t* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table), *keys);
}
};
template <>
struct load_record_tuple<keystr_value_object, by_scope_primary> {
inline static auto get(name scope, name code, name table, std::string* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table), keys->data());
}
};
template <>
struct load_record_tuple<key128x128_value_object, by_scope_primary> {
inline static auto get(name scope, name code, name table, uint128_t* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table), *keys, uint128_t(0));
}
};
template <>
struct load_record_tuple<key128x128_value_object, by_scope_secondary> {
inline static auto get(name scope, name code, name table, uint128_t* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table), *(keys+1), uint128_t(0));
}
};
template <>
struct load_record_tuple<key64x64x64_value_object, by_scope_primary> {
inline static auto get(name scope, name code, name table, uint64_t* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table), *keys, uint64_t(0), uint64_t(0));
}
};
template <>
struct load_record_tuple<key64x64x64_value_object, by_scope_secondary> {
inline static auto get(name scope, name code, name table, uint64_t* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table), *(keys+1), uint64_t(0));
}
};
template <>
struct load_record_tuple<key64x64x64_value_object, by_scope_tertiary> {
inline static auto get(name scope, name code, name table, uint64_t* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table), *(keys+2));
}
};
//load_record_compare
template <typename ObjectType, typename Scope>
struct load_record_compare {
inline static auto compare(const ObjectType& object, typename ObjectType::key_type* keys) {
return typename ObjectType::key_type(object.primary_key) == *keys;
}
};
template <>
struct load_record_compare<keystr_value_object, by_scope_primary> {
inline static auto compare(const keystr_value_object& object, std::string* keys) {
return !memcmp(object.primary_key.data(), keys->data(), keys->size());
}
};
template <>
struct load_record_compare<key128x128_value_object, by_scope_secondary> {
inline static auto compare(const key128x128_value_object& object, uint128_t* keys) {
return object.secondary_key == *(keys+1);
}
};
template <>
struct load_record_compare<key64x64x64_value_object, by_scope_primary> {
inline static auto compare(const key64x64x64_value_object& object, uint64_t* keys) {
return object.primary_key == *keys;
}
};
template <>
struct load_record_compare<key64x64x64_value_object, by_scope_secondary> {
inline static auto compare(const key64x64x64_value_object& object, uint64_t* keys) {
return object.secondary_key == *(keys+1);
}
};
template <>
struct load_record_compare<key64x64x64_value_object, by_scope_tertiary> {
inline static auto compare(const key64x64x64_value_object& object, uint64_t* keys) {
return object.tertiary_key == *(keys+2);
}
};
//front_record_tuple
template <typename ObjectType>
struct front_record_tuple {
inline static auto get(name scope, name code, name table) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table),
typename ObjectType::key_type(0), typename ObjectType::key_type(0), typename ObjectType::key_type(0));
}
};
template <>
struct front_record_tuple<key_value_object> {
inline static auto get(name scope, name code, name table) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table), uint64_t(0));
}
};
template <>
struct front_record_tuple<keystr_value_object> {
inline static auto get(name scope, name code, name table) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table));
}
};
// //back_record_tuple
// template <typename ObjectType>
// struct back_record_tuple {
// inline static auto get(name scope, name code, name table) {
// return boost::make_tuple( account_name(scope), account_name(code), account_name(table),
// typename ObjectType::key_type(-1), typename ObjectType::key_type(-1), typename ObjectType::key_type(-1));
// }
// };
//next_record_tuple (same for previous)
template <typename T>
struct next_record_tuple {};
template <>
struct next_record_tuple<key_value_object> {
inline static auto get(name scope, name code, name table, uint64_t* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table),
*keys);
}
};
template <>
struct next_record_tuple<keystr_value_object> {
inline static auto get(name scope, name code, name table, std::string* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table),
keys->data());
}
};
template <>
struct next_record_tuple<key128x128_value_object> {
inline static auto get(name scope, name code, name table, uint128_t* keys) {
return boost::make_tuple( uint64_t(scope), uint64_t(code), uint64_t(table),
*keys, *(keys+1));
}
};
template <>
struct next_record_tuple<key64x64x64_value_object> {
inline static auto get(name scope, name code, name table, uint64_t* keys) {
return boost::make_tuple( uint64_t(scope), uint64_t(code), uint64_t(table),
*keys, *(keys+1), *(keys+2));
}
};
//lower_bound_tuple
template <typename ObjectType, typename Scope>
struct lower_bound_tuple{};
template <typename ObjectType>
struct lower_bound_tuple<ObjectType, by_scope_primary> {
inline static auto get(name scope, name code, name table, typename ObjectType::key_type* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table),
*keys, typename ObjectType::key_type(0), typename ObjectType::key_type(0) );
}
};
template <>
struct lower_bound_tuple<key_value_object, by_scope_primary> {
inline static auto get(name scope, name code, name table, uint64_t* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table), *keys);
}
};
template <>
struct lower_bound_tuple<keystr_value_object, by_scope_primary> {
inline static auto get(name scope, name code, name table, std::string* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table), keys->data());
}
};
template <>
struct lower_bound_tuple<key128x128_value_object, by_scope_secondary> {
inline static auto get(name scope, name code, name table, uint128_t* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table),
*(keys+1), uint128_t(0));
}
};
template <>
struct lower_bound_tuple<key64x64x64_value_object, by_scope_secondary> {
inline static auto get(name scope, name code, name table, uint64_t* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table),
*(keys+1), uint64_t(0));
}
};
template <>
struct lower_bound_tuple<key64x64x64_value_object, by_scope_tertiary> {
inline static auto get(name scope, name code, name table, uint64_t* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table),
*(keys+2));
}
};
//upper_bound_tuple
template <typename ObjectType, typename Scope>
struct upper_bound_tuple{};
template <typename ObjectType>
struct upper_bound_tuple<ObjectType, by_scope_primary> {
inline static auto get(name scope, name code, name table, typename ObjectType::key_type* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table),
*keys, typename ObjectType::key_type(-1), typename ObjectType::key_type(-1) );
}
};
template <>
struct upper_bound_tuple<keystr_value_object, by_scope_primary> {
inline static auto get(name scope, name code, name table, std::string* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table),
keys->data());
}
};
template <>
struct upper_bound_tuple<key128x128_value_object, by_scope_secondary> {
inline static auto get(name scope, name code, name table, uint128_t* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table),
*(keys+1), uint128_t(-1) );
}
};
template <>
struct upper_bound_tuple<key64x64x64_value_object, by_scope_secondary> {
inline static auto get(name scope, name code, name table, uint64_t* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table),
*(keys+1), uint64_t(-1) );
}
};
template <>
struct upper_bound_tuple<key64x64x64_value_object, by_scope_tertiary> {
inline static auto get(name scope, name code, name table, uint64_t* keys) {
return boost::make_tuple( account_name(scope), account_name(code), account_name(table),
*(keys+2));
}
};
} } // eosio::chain
......@@ -139,6 +139,7 @@ namespace eosio { namespace chain {
scope_sequence_object_type,
bandwidth_usage_object_type,
compute_usage_object_type,
table_id_object_type,
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
};
......@@ -189,6 +190,7 @@ FC_REFLECT_ENUM(eosio::chain::object_type,
(scope_sequence_object_type)
(bandwidth_usage_object_type)
(compute_usage_object_type)
(table_id_object_type)
(OBJECT_TYPE_COUNT)
)
FC_REFLECT( eosio::chain::void_t, )
......@@ -7,40 +7,39 @@
#include <boost/multiprecision/cpp_dec_float.hpp>
namespace eosio { namespace chain {
using namespace IR;
using namespace Runtime;
using namespace fc;
namespace eosio { namespace chain {
using wasm_double = boost::multiprecision::cpp_dec_float_50;
struct wasm_cache::entry {
entry(ModuleInstance* instance, Module* module)
:instance(instance)
,module(module)
{}
entry(ModuleInstance *instance, Module *module)
: instance(instance), module(module) {}
ModuleInstance* instance;
Module* module;
ModuleInstance *instance;
Module *module;
};
struct wasm_context {
wasm_cache::entry& code;
apply_context& context;
wasm_cache::entry &code;
apply_context &context;
};
struct wasm_interface_impl {
optional<wasm_context> current_context;
void call(const string& entry_point, const vector<Value>& args, wasm_cache::entry& code, apply_context& context);
void call(const string &entry_point, const vector <Value> &args, wasm_cache::entry &code, apply_context &context);
};
class intrinsics_accessor {
public:
static wasm_context& get_context(wasm_interface& wasm) {
FC_ASSERT(wasm.my->current_context.valid());
return *wasm.my->current_context;
}
static wasm_context &get_context(wasm_interface &wasm) {
FC_ASSERT(wasm.my->current_context.valid());
return *wasm.my->current_context;
}
};
template<typename T>
......@@ -50,7 +49,7 @@ struct class_from_wasm {
* @param wasm - the wasm_interface to use
* @return
*/
static auto value(wasm_interface& wasm) {
static auto value(wasm_interface &wasm) {
return T(wasm);
}
};
......@@ -62,7 +61,7 @@ struct class_from_wasm<apply_context> {
* @param wasm
* @return
*/
static auto& value(wasm_interface &wasm) {
static auto &value(wasm_interface &wasm) {
return intrinsics_accessor::get_context(wasm).context;
}
};
......@@ -79,16 +78,16 @@ struct array_ptr {
return *value;
}
T* operator->() const noexcept {
T *operator->() const noexcept {
return value;
}
template<typename U>
operator U*() const {
return static_cast<U*>(value);
operator U *() const {
return static_cast<U *>(value);
}
T* value;
T *value;
};
......@@ -105,21 +104,45 @@ struct native_to_wasm {
* specialization for maping pointers to int32's
*/
template<typename T>
struct native_to_wasm<T*> {
struct native_to_wasm<T *> {
using type = I32;
};
/**
* Mappings for native types
*/
template<> struct native_to_wasm<int32_t> { using type = I32; };
template<> struct native_to_wasm<uint32_t> { using type = I32; };
template<> struct native_to_wasm<int64_t> { using type = I64; };
template<> struct native_to_wasm<uint64_t> { using type = I64; };
template<> struct native_to_wasm<bool> { using type = I32; };
template<> struct native_to_wasm<const name&> { using type = I64; };
template<> struct native_to_wasm<name> { using type = I64; };
template<> struct native_to_wasm<wasm_double> { using type = I64; };
template<>
struct native_to_wasm<int32_t> {
using type = I32;
};
template<>
struct native_to_wasm<uint32_t> {
using type = I32;
};
template<>
struct native_to_wasm<int64_t> {
using type = I64;
};
template<>
struct native_to_wasm<uint64_t> {
using type = I64;
};
template<>
struct native_to_wasm<bool> {
using type = I32;
};
template<>
struct native_to_wasm<const name &> {
using type = I64;
};
template<>
struct native_to_wasm<name> {
using type = I64;
};
template<>
struct native_to_wasm<wasm_double> {
using type = I64;
};
// convenience alias
......@@ -131,8 +154,8 @@ auto convert_native_to_wasm(T val) {
return native_to_wasm_t<T>(val);
}
auto convert_native_to_wasm(const name& val) {
return native_to_wasm_t<const name&>(val.value);
auto convert_native_to_wasm(const name &val) {
return native_to_wasm_t<const name &>(val.value);
}
template<typename T>
......@@ -142,31 +165,53 @@ auto convert_wasm_to_native(native_to_wasm_t<T> val) {
template<>
auto convert_wasm_to_native<wasm_double>(I64 val) {
return wasm_double(*reinterpret_cast<wasm_double*>(&val));
return wasm_double(*reinterpret_cast<wasm_double *>(&val));
}
template<typename T>
struct wasm_to_value_type;
template<> struct wasm_to_value_type<I32> { static constexpr auto value = ValueType::i32; };
template<> struct wasm_to_value_type<I64> { static constexpr auto value = ValueType::i64; };
template<>
struct wasm_to_value_type<I32> {
static constexpr auto value = ValueType::i32;
};
template<>
struct wasm_to_value_type<I64> {
static constexpr auto value = ValueType::i64;
};
template<typename T>
constexpr auto wasm_to_value_type_v = wasm_to_value_type<T>::value;
template<typename T>
struct wasm_to_rvalue_type;
template<> struct wasm_to_rvalue_type<I32> { static constexpr auto value = ResultType::i32; };
template<> struct wasm_to_rvalue_type<I64> { static constexpr auto value = ResultType::i64; };
template<> struct wasm_to_rvalue_type<void> { static constexpr auto value = ResultType::none; };
template<> struct wasm_to_rvalue_type<const name&> { static constexpr auto value = ResultType::i64; };
template<> struct wasm_to_rvalue_type<name> { static constexpr auto value = ResultType::i64; };
template<>
struct wasm_to_rvalue_type<I32> {
static constexpr auto value = ResultType::i32;
};
template<>
struct wasm_to_rvalue_type<I64> {
static constexpr auto value = ResultType::i64;
};
template<>
struct wasm_to_rvalue_type<void> {
static constexpr auto value = ResultType::none;
};
template<>
struct wasm_to_rvalue_type<const name &> {
static constexpr auto value = ResultType::i64;
};
template<>
struct wasm_to_rvalue_type<name> {
static constexpr auto value = ResultType::i64;
};
template<typename T>
constexpr auto wasm_to_rvalue_type_v = wasm_to_rvalue_type<T>::value;
struct void_type{};
struct void_type {
};
/**
* Forward declaration of provider for FunctionType given a desired C ABI signature
......@@ -179,8 +224,8 @@ struct wasm_function_type_provider;
*/
template<typename Ret, typename ...Args>
struct wasm_function_type_provider<Ret(Args...)> {
static const FunctionType* type() {
return FunctionType::get(wasm_to_rvalue_type_v<Ret>, { wasm_to_value_type_v<Args> ... });
static const FunctionType *type() {
return FunctionType::get(wasm_to_rvalue_type_v<Ret>, {wasm_to_value_type_v<Args> ...});
}
};
......@@ -202,11 +247,11 @@ struct intrinsic_invoker_impl;
*/
template<typename Ret, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<>, std::tuple<Translated...>> {
using next_method_type = Ret (*)(wasm_interface&, Translated...);
using next_method_type = Ret (*)(wasm_interface &, Translated...);
template<next_method_type Method>
static native_to_wasm_t<Ret> invoke(Translated... translated) {
wasm_interface& wasm = wasm_interface::get();
wasm_interface &wasm = wasm_interface::get();
return convert_native_to_wasm(Method(wasm, translated...));
}
......@@ -222,11 +267,11 @@ struct intrinsic_invoker_impl<Ret, std::tuple<>, std::tuple<Translated...>> {
*/
template<typename ...Translated>
struct intrinsic_invoker_impl<void_type, std::tuple<>, std::tuple<Translated...>> {
using next_method_type = void_type (*)(wasm_interface&, Translated...);
using next_method_type = void_type (*)(wasm_interface &, Translated...);
template<next_method_type Method>
static void invoke(Translated... translated) {
wasm_interface& wasm = wasm_interface::get();
wasm_interface &wasm = wasm_interface::get();
Method(wasm, translated...);
}
......@@ -247,10 +292,10 @@ template<typename Ret, typename Input, typename... Inputs, typename... Translate
struct intrinsic_invoker_impl<Ret, std::tuple<Input, Inputs...>, std::tuple<Translated...>> {
using translated_type = native_to_wasm_t<Input>;
using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., translated_type>>;
using then_type = Ret (*)(wasm_interface&, Input, Inputs..., Translated...);
using then_type = Ret (*)(wasm_interface &, Input, Inputs..., Translated...);
template<then_type Then>
static Ret translate_one(wasm_interface& wasm, Inputs... rest, Translated... translated, translated_type last) {
static Ret translate_one(wasm_interface &wasm, Inputs... rest, Translated... translated, translated_type last) {
auto native = convert_wasm_to_native<Input>(last);
return Then(wasm, native, rest..., translated...);
};
......@@ -273,13 +318,13 @@ struct intrinsic_invoker_impl<Ret, std::tuple<Input, Inputs...>, std::tuple<Tran
template<typename T, typename Ret, typename... Inputs, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<array_ptr<T>, size_t, Inputs...>, std::tuple<Translated...>> {
using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., I32, I32>>;
using then_type = Ret(*)(wasm_interface&, array_ptr<T>, size_t, Inputs..., Translated...);
using then_type = Ret(*)(wasm_interface &, array_ptr<T>, size_t, Inputs..., Translated...);
template<then_type Then>
static Ret translate_one(wasm_interface& wasm, Inputs... rest, Translated... translated, I32 ptr, I32 size) {
static Ret translate_one(wasm_interface &wasm, Inputs... rest, Translated... translated, I32 ptr, I32 size) {
auto mem = getDefaultMemory(intrinsics_accessor::get_context(wasm).code.instance);
size_t length = size_t(size);
T* base = memoryArrayPtr<T>( mem, ptr, length );
T *base = memoryArrayPtr<T>(mem, ptr, length);
return Then(wasm, array_ptr<T>{base}, length, rest..., translated...);
};
......@@ -299,14 +344,14 @@ struct intrinsic_invoker_impl<Ret, std::tuple<array_ptr<T>, size_t, Inputs...>,
* @tparam Translated - the list of transcribed wasm parameters
*/
template<typename T, typename Ret, typename... Inputs, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<T*, Inputs...>, std::tuple<Translated...>> {
struct intrinsic_invoker_impl<Ret, std::tuple<T *, Inputs...>, std::tuple<Translated...>> {
using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., I32>>;
using then_type = Ret (*)(wasm_interface&, T*, Inputs..., Translated...);
using then_type = Ret (*)(wasm_interface &, T *, Inputs..., Translated...);
template<then_type Then>
static Ret translate_one(wasm_interface& wasm, Inputs... rest, Translated... translated, I32 ptr) {
static Ret translate_one(wasm_interface &wasm, Inputs... rest, Translated... translated, I32 ptr) {
auto mem = getDefaultMemory(intrinsics_accessor::get_context(wasm).code.instance);
T* base = memoryArrayPtr<T>( mem, ptr, 1 );
T *base = memoryArrayPtr<T>(mem, ptr, 1);
return Then(wasm, base, rest..., translated...);
};
......@@ -326,12 +371,12 @@ struct intrinsic_invoker_impl<Ret, std::tuple<T*, Inputs...>, std::tuple<Transla
* @tparam Translated - the list of transcribed wasm parameters
*/
template<typename Ret, typename... Inputs, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<const name&, Inputs...>, std::tuple<Translated...>> {
struct intrinsic_invoker_impl<Ret, std::tuple<const name &, Inputs...>, std::tuple<Translated...>> {
using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., I64 >>;
using then_type = Ret (*)(wasm_interface&, const name&, Inputs..., Translated...);
using then_type = Ret (*)(wasm_interface &, const name &, Inputs..., Translated...);
template<then_type Then>
static Ret translate_one(wasm_interface& wasm, Inputs... rest, Translated... translated, I64 name_value) {
static Ret translate_one(wasm_interface &wasm, Inputs... rest, Translated... translated, I64 name_value) {
auto value = name(name_value);
return Then(wasm, value, rest..., translated...);
}
......@@ -353,16 +398,16 @@ struct intrinsic_invoker_impl<Ret, std::tuple<const name&, Inputs...>, std::tupl
* @tparam Translated - the list of transcribed wasm parameters
*/
template<typename T, typename Ret, typename... Inputs, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<T&, Inputs...>, std::tuple<Translated...>> {
struct intrinsic_invoker_impl<Ret, std::tuple<T &, Inputs...>, std::tuple<Translated...>> {
using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., I32>>;
using then_type = Ret (*)(wasm_interface&, T&, Inputs..., Translated...);
using then_type = Ret (*)(wasm_interface &, T &, Inputs..., Translated...);
template<then_type Then>
static Ret translate_one(wasm_interface& wasm, Inputs... rest, Translated... translated, I32 ptr) {
static Ret translate_one(wasm_interface &wasm, Inputs... rest, Translated... translated, I32 ptr) {
// references cannot be created for null pointers
FC_ASSERT(ptr != 0);
auto mem = getDefaultMemory(intrinsics_accessor::get_context(wasm).code.instance);
T& base = memoryRef<T>( mem, ptr );
T &base = memoryRef<T>(mem, ptr);
return Then(wasm, base, rest..., translated...);
}
......@@ -379,15 +424,16 @@ template<typename WasmSig, typename Ret, typename MethodSig, typename Cls, typen
struct intrinsic_function_invoker {
using impl = intrinsic_invoker_impl<Ret, std::tuple<Params...>, std::tuple<>>;
template< MethodSig Method >
static Ret wrapper(wasm_interface& wasm, Params... params) {
template<MethodSig Method>
static Ret wrapper(wasm_interface &wasm, Params... params) {
return (class_from_wasm<Cls>::value(wasm).*Method)(params...);
}
template< MethodSig Method >
static const WasmSig* fn() {
template<MethodSig Method>
static const WasmSig *fn() {
auto fn = impl::template fn<wrapper<Method>>();
static_assert(std::is_same<WasmSig*, decltype(fn)>::value, "Intrinsic function signature does not match the ABI");
static_assert(std::is_same<WasmSig *, decltype(fn)>::value,
"Intrinsic function signature does not match the ABI");
return fn;
}
};
......@@ -396,16 +442,17 @@ template<typename WasmSig, typename MethodSig, typename Cls, typename... Params>
struct intrinsic_function_invoker<WasmSig, void, MethodSig, Cls, Params...> {
using impl = intrinsic_invoker_impl<void_type, std::tuple<Params...>, std::tuple<>>;
template< MethodSig Method >
static void_type wrapper(wasm_interface& wasm, Params... params) {
template<MethodSig Method>
static void_type wrapper(wasm_interface &wasm, Params... params) {
(class_from_wasm<Cls>::value(wasm).*Method)(params...);
return void_type();
}
template< MethodSig Method >
static const WasmSig* fn() {
template<MethodSig Method>
static const WasmSig *fn() {
auto fn = impl::template fn<wrapper<Method>>();
static_assert(std::is_same<WasmSig*, decltype(fn)>::value, "Intrinsic function signature does not match the ABI");
static_assert(std::is_same<WasmSig *, decltype(fn)>::value,
"Intrinsic function signature does not match the ABI");
return fn;
}
};
......@@ -414,22 +461,22 @@ template<typename, typename>
struct intrinsic_function_invoker_wrapper;
template<typename WasmSig, typename Cls, typename Ret, typename... Params>
struct intrinsic_function_invoker_wrapper<WasmSig, Ret (Cls::*)(Params...)> {
struct intrinsic_function_invoker_wrapper<WasmSig, Ret (Cls::*)(Params...)> {
using type = intrinsic_function_invoker<WasmSig, Ret, Ret (Cls::*)(Params...), Cls, Params...>;
};
template<typename WasmSig, typename Cls, typename Ret, typename... Params>
struct intrinsic_function_invoker_wrapper<WasmSig, Ret (Cls::*)(Params...) const> {
struct intrinsic_function_invoker_wrapper<WasmSig, Ret (Cls::*)(Params...) const> {
using type = intrinsic_function_invoker<WasmSig, Ret, Ret (Cls::*)(Params...) const, Cls, Params...>;
};
template<typename WasmSig, typename Cls, typename Ret, typename... Params>
struct intrinsic_function_invoker_wrapper<WasmSig, Ret (Cls::*)(Params...) volatile> {
struct intrinsic_function_invoker_wrapper<WasmSig, Ret (Cls::*)(Params...) volatile> {
using type = intrinsic_function_invoker<WasmSig, Ret, Ret (Cls::*)(Params...) volatile, Cls, Params...>;
};
template<typename WasmSig, typename Cls, typename Ret, typename... Params>
struct intrinsic_function_invoker_wrapper<WasmSig, Ret (Cls::*)(Params...) const volatile> {
struct intrinsic_function_invoker_wrapper<WasmSig, Ret (Cls::*)(Params...) const volatile> {
using type = intrinsic_function_invoker<WasmSig, Ret, Ret (Cls::*)(Params...) const volatile, Cls, Params...>;
};
......@@ -476,4 +523,4 @@ struct intrinsic_function_invoker_wrapper<WasmSig, Ret (Cls::*)(Params...) const
#define REGISTER_INTRINSICS(CLS, MEMBERS)\
BOOST_PP_SEQ_FOR_EACH(_REGISTER_INTRINSIC, CLS, _WRAPPED_SEQ(MEMBERS))
} };
\ No newline at end of file
} } // eosio::chain
\ No newline at end of file
......@@ -9,12 +9,12 @@
#include "IR/Module.h"
#include "Platform/Platform.h"
#include "WAST/WAST.h"
#include "Runtime/Runtime.h"
#include "Runtime/Linker.h"
#include "Runtime/Intrinsics.h"
#include "IR/Operators.h"
#include "IR/Validate.h"
#include "IR/Types.h"
#include "Runtime/Runtime.h"
#include "Runtime/Linker.h"
#include "Runtime/Intrinsics.h"
#include <boost/asio.hpp>
#include <boost/bind.hpp>
......@@ -63,6 +63,7 @@ using boost::asio::io_service;
#endif
namespace eosio { namespace chain {
using namespace contracts;
/**
* Integration with the WASM Linker to resolve our intrinsics
......@@ -692,6 +693,98 @@ class console_api : public context_aware_api {
}
};
template<typename ObjectType>
class db_api : public context_aware_api {
using KeyType = typename ObjectType::key_type;
static constexpr int KeyCount = ObjectType::number_of_keys;
using KeyArrayType = KeyType[KeyCount];
using ContextMethodType = int(apply_context::*)(const table_id_object&, const KeyType*, const char*, size_t);
private:
int call(ContextMethodType method, const scope_name& scope, const name& table, array_ptr<const char> data, size_t data_len) {
const auto& t_id = context.find_or_create_table(scope, context.receiver, table);
FC_ASSERT(data_len >= KeyCount * sizeof(KeyType), "Data is not long enough to contain keys");
const KeyType* keys = reinterpret_cast<const KeyType *>((const char *)data);
const char* record_data = ((const char*)data) + sizeof(KeyArrayType);
size_t record_len = data_len - sizeof(KeyArrayType);
return (context.*(method))(t_id, keys, record_data, record_len);
}
public:
using context_aware_api::context_aware_api;
int store(const scope_name& scope, const name& table, array_ptr<const char> data, size_t data_len) {
return call(&apply_context::store_record<ObjectType>, scope, table, data, data_len);
}
int update(const scope_name& scope, const name& table, array_ptr<const char> data, size_t data_len) {
return call(&apply_context::update_record<ObjectType>, scope, table, data, data_len);
}
int remove(const scope_name& scope, const name& table, const KeyArrayType &keys) {
const auto& t_id = context.find_or_create_table(scope, context.receiver, table);
return context.remove_record<ObjectType>(t_id, keys);
}
};
template<typename IndexType, typename Scope>
class db_index_api : public context_aware_api {
using KeyType = typename IndexType::value_type::key_type;
static constexpr int KeyCount = IndexType::value_type::number_of_keys;
using KeyArrayType = KeyType[KeyCount];
using ContextMethodType = int(apply_context::*)(const table_id_object&, KeyType*, char*, size_t);
int call(ContextMethodType method, const scope_name& scope, const account_name& code, const name& table, array_ptr<char> data, size_t data_len) {
auto maybe_t_id = context.find_table(scope, context.receiver, table);
if (maybe_t_id == nullptr) {
return 0;
}
const auto& t_id = *maybe_t_id;
FC_ASSERT(data_len >= KeyCount * sizeof(KeyType), "Data is not long enough to contain keys");
KeyType* keys = reinterpret_cast<KeyType *>((char *)data);
char* record_data = ((char*)data) + sizeof(KeyArrayType);
size_t record_len = data_len - sizeof(KeyArrayType);
return (context.*(method))(t_id, keys, record_data, record_len);
}
public:
using context_aware_api::context_aware_api;
int load(const scope_name& scope, const account_name& code, const name& table, array_ptr<char> data, size_t data_len) {
return call(&apply_context::load_record<IndexType, Scope>, scope, code, table, data, data_len);
}
int front(const scope_name& scope, const account_name& code, const name& table, array_ptr<char> data, size_t data_len) {
return call(&apply_context::front_record<IndexType, Scope>, scope, code, table, data, data_len);
}
int back(const scope_name& scope, const account_name& code, const name& table, array_ptr<char> data, size_t data_len) {
return call(&apply_context::back_record<IndexType, Scope>, scope, code, table, data, data_len);
}
int next(const scope_name& scope, const account_name& code, const name& table, array_ptr<char> data, size_t data_len) {
return call(&apply_context::next_record<IndexType, Scope>, scope, code, table, data, data_len);
}
int previous(const scope_name& scope, const account_name& code, const name& table, array_ptr<char> data, size_t data_len) {
return call(&apply_context::previous_record<IndexType, Scope>, scope, code, table, data, data_len);
}
int lower_bound(const scope_name& scope, const account_name& code, const name& table, array_ptr<char> data, size_t data_len) {
return call(&apply_context::lower_bound_record<IndexType, Scope>, scope, code, table, data, data_len);
}
int upper_bound(const scope_name& scope, const account_name& code, const name& table, array_ptr<char> data, size_t data_len) {
return call(&apply_context::upper_bound_record<IndexType, Scope>, scope, code, table, data, data_len);
}
};
REGISTER_INTRINSICS(system_api,
(assert, void(int, int))
);
......@@ -719,5 +812,44 @@ REGISTER_INTRINSICS(console_api,
(printhex, void(int, int) )
);
#define DB_METHOD_SEQ(SUFFIX) \
(store, int32_t(int64_t, int64_t, int, int), "store_"#SUFFIX )\
(update, int32_t(int64_t, int64_t, int, int), "update_"#SUFFIX )\
(remove, int32_t(int64_t, int64_t, int), "remove_"#SUFFIX )
#define DB_INDEX_METHOD_SEQ(SUFFIX)\
(load, int32_t(int64_t, int64_t, int64_t, int, int), "load_"#SUFFIX )\
(front, int32_t(int64_t, int64_t, int64_t, int, int), "front_"#SUFFIX )\
(back, int32_t(int64_t, int64_t, int64_t, int, int), "back_"#SUFFIX )\
(next, int32_t(int64_t, int64_t, int64_t, int, int), "next_"#SUFFIX )\
(previous, int32_t(int64_t, int64_t, int64_t, int, int), "previous_"#SUFFIX )\
(lower_bound, int32_t(int64_t, int64_t, int64_t, int, int), "lower_bound_"#SUFFIX )\
(upper_bound, int32_t(int64_t, int64_t, int64_t, int, int), "upper_bound_"#SUFFIX )\
using db_api_key_value_object = db_api<key_value_object>;
using db_api_keystr_value_object = db_api<keystr_value_object>;
using db_api_key128x128_value_object = db_api<key128x128_value_object>;
using db_api_key64x64x64_value_object = db_api<key64x64x64_value_object>;
using db_index_api_key_value_index_by_scope_primary = db_index_api<key_value_index,by_scope_primary>;
using db_index_api_keystr_value_index_by_scope_primary = db_index_api<keystr_value_index,by_scope_primary>;
using db_index_api_key128x128_value_index_by_scope_primary = db_index_api<key128x128_value_index,by_scope_primary>;
using db_index_api_key128x128_value_index_by_scope_secondary = db_index_api<key128x128_value_index,by_scope_secondary>;
using db_index_api_key64x64x64_value_index_by_scope_primary = db_index_api<key64x64x64_value_index,by_scope_primary>;
using db_index_api_key64x64x64_value_index_by_scope_secondary = db_index_api<key64x64x64_value_index,by_scope_secondary>;
using db_index_api_key64x64x64_value_index_by_scope_tertiary = db_index_api<key64x64x64_value_index,by_scope_tertiary>;
REGISTER_INTRINSICS(db_api_key_value_object, DB_METHOD_SEQ(i64));
REGISTER_INTRINSICS(db_api_keystr_value_object, DB_METHOD_SEQ(str));
REGISTER_INTRINSICS(db_api_key128x128_value_object, DB_METHOD_SEQ(i128i128));
REGISTER_INTRINSICS(db_api_key64x64x64_value_object, DB_METHOD_SEQ(i64i64i64));
REGISTER_INTRINSICS(db_index_api_key_value_index_by_scope_primary, DB_INDEX_METHOD_SEQ(i64));
REGISTER_INTRINSICS(db_index_api_keystr_value_index_by_scope_primary, DB_INDEX_METHOD_SEQ(str));
REGISTER_INTRINSICS(db_index_api_key128x128_value_index_by_scope_primary, DB_INDEX_METHOD_SEQ(primary_i128i128));
REGISTER_INTRINSICS(db_index_api_key128x128_value_index_by_scope_secondary, DB_INDEX_METHOD_SEQ(secondary_i128i128));
REGISTER_INTRINSICS(db_index_api_key64x64x64_value_index_by_scope_primary, DB_INDEX_METHOD_SEQ(primary_i64i64i64));
REGISTER_INTRINSICS(db_index_api_key64x64x64_value_index_by_scope_secondary, DB_INDEX_METHOD_SEQ(primary_i64i64i64));
REGISTER_INTRINSICS(db_index_api_key64x64x64_value_index_by_scope_tertiary, DB_INDEX_METHOD_SEQ(tertiary_i64i64i64));
} } /// eosio::chain
......@@ -54,6 +54,7 @@ namespace eosio { namespace testing {
bool chain_has_transaction( const transaction_id_type& txid ) const;
const transaction_receipt& get_transaction_receipt( const transaction_id_type& txid ) const;
share_type get_balance( const account_name& account ) const;
private:
fc::temp_directory tempdir;
......
#include <eosio/testing/tester.hpp>
#include <eosio/chain/contracts/balance_object.hpp>
#include <eosio/chain/contracts/staked_balance_objects.hpp>
using namespace eosio;
......@@ -26,7 +25,7 @@ int main( int argc, char** argv ) {
wdump((b));
}
const auto& b = test.get<balance_object, by_owner_name>( N(dan) );
auto b = test.get_balance(N(dan));
const auto& sb = test.get<staked_balance_object, by_owner_name>( N(dan) );
FC_ASSERT( asset( sb.staked_balance ) == asset::from_string("100.0000 EOS") );
......@@ -36,8 +35,8 @@ int main( int argc, char** argv ) {
FC_ASSERT( sb2.staked_balance == sb.staked_balance );
test.transfer( N(inita), N(stan), asset::from_string( "20.0000 EOS" ), "hello world" );
const auto& stan_balance = test.get<balance_object, by_owner_name>( N(stan) );
FC_ASSERT( stan_balance.balance == 200000 );
auto stan_balance = test.get_balance( N(stan) );
FC_ASSERT( stan_balance == 200000 );
} catch ( const fc::exception& e ) {
elog("${e}", ("e",e.to_detail_string()) );
......
#include <eosio/testing/tester.hpp>
#include <eosio/chain/asset.hpp>
#include <eosio/chain/contracts/types.hpp>
#include <eosio/chain/contracts/eos_contract.hpp>
#include <fc/utility.hpp>
#include <fc/io/json.hpp>
......@@ -236,5 +237,10 @@ namespace eosio { namespace testing {
return chain_transactions.at(txid);
}
share_type tester::get_balance( const account_name& account ) const {
const auto& db = control->get_database();
return contracts::get_eosio_balance(db, account);
}
} } /// eosio::test
......@@ -14,8 +14,8 @@
#include <eosio/chain/contracts/chain_initializer.hpp>
#include <eosio/chain/contracts/staked_balance_objects.hpp>
#include <eosio/chain/contracts/balance_object.hpp>
#include <eosio/chain/contracts/genesis_state.hpp>
#include <eosio/chain/contracts/eos_contract.hpp>
#include <eos/utilities/key_conversion.hpp>
#include <eosio/chain/wast_to_wasm.hpp>
......@@ -312,21 +312,21 @@ read_only::get_table_rows_result read_only::get_table_rows( const read_only::get
auto table_key = PRIMARY;
if( table_type == KEYi64 ) {
return get_table_rows_ex<key_value_index, by_scope_primary>(p,abi);
return get_table_rows_ex<contracts::key_value_index, contracts::by_scope_primary>(p,abi);
} else if( table_type == KEYstr ) {
return get_table_rows_ex<keystr_value_index, by_scope_primary>(p,abi);
return get_table_rows_ex<contracts::keystr_value_index, contracts::by_scope_primary>(p,abi);
} else if( table_type == KEYi128i128 ) {
if( table_key == PRIMARY )
return get_table_rows_ex<key128x128_value_index, by_scope_primary>(p,abi);
return get_table_rows_ex<contracts::key128x128_value_index, contracts::by_scope_primary>(p,abi);
if( table_key == SECONDARY )
return get_table_rows_ex<key128x128_value_index, by_scope_secondary>(p,abi);
return get_table_rows_ex<contracts::key128x128_value_index, contracts::by_scope_secondary>(p,abi);
} else if( table_type == KEYi64i64i64 ) {
if( table_key == PRIMARY )
return get_table_rows_ex<key64x64x64_value_index, by_scope_primary>(p,abi);
return get_table_rows_ex<contracts::key64x64x64_value_index, contracts::by_scope_primary>(p,abi);
if( table_key == SECONDARY )
return get_table_rows_ex<key64x64x64_value_index, by_scope_secondary>(p,abi);
return get_table_rows_ex<contracts::key64x64x64_value_index, contracts::by_scope_secondary>(p,abi);
if( table_key == TERTIARY )
return get_table_rows_ex<key64x64x64_value_index, by_scope_tertiary>(p,abi);
return get_table_rows_ex<contracts::key64x64x64_value_index, contracts::by_scope_tertiary>(p,abi);
}
FC_ASSERT( false, "invalid table type/key ${type}/${key}", ("type",table_type)("key",table_key)("abi",abi));
}
......@@ -414,10 +414,10 @@ read_only::get_account_results read_only::get_account( const get_account_params&
result.account_name = params.account_name;
const auto& d = db.get_database();
const auto& balance = d.get<balance_object,by_owner_name>( params.account_name );
share_type balance = contracts::get_eosio_balance(d, params.account_name );
const auto& staked_balance = d.get<staked_balance_object,by_owner_name>( params.account_name );
result.eos_balance = asset(balance.balance, EOS_SYMBOL);
result.eos_balance = asset(balance, EOS_SYMBOL);
result.staked_balance = asset(staked_balance.staked_balance);
result.unstaking_balance = asset(staked_balance.unstaking_balance);
result.last_unstaking_time = staked_balance.last_unstaking_time;
......
......@@ -9,7 +9,7 @@
#include <eosio/chain/account_object.hpp>
#include <eosio/chain/block.hpp>
#include <eosio/chain/chain_controller.hpp>
#include <eosio/chain/key_value_object.hpp>
#include <eosio/chain/contracts/contract_table_objects.hpp>
#include <eosio/chain/transaction.hpp>
#include <eosio/chain/contracts/abi_serializer.hpp>
......@@ -181,13 +181,13 @@ public:
get_table_rows_result get_table_rows( const get_table_rows_params& params )const;
void copy_row(const chain::key_value_object& obj, vector<char>& data)const {
void copy_row(const chain::contracts::key_value_object& obj, vector<char>& data)const {
data.resize( sizeof(uint64_t) + obj.value.size() );
memcpy( data.data(), &obj.primary_key, sizeof(uint64_t) );
memcpy( data.data()+sizeof(uint64_t), obj.value.data(), obj.value.size() );
}
void copy_row(const chain::keystr_value_object& obj, vector<char>& data)const {
void copy_row(const chain::contracts::keystr_value_object& obj, vector<char>& data)const {
data.resize( obj.primary_key.size() + obj.value.size() + 8 );
fc::datastream<char*> ds(data.data(), data.size());
fc::raw::pack(ds, obj.primary_key);
......@@ -195,14 +195,14 @@ public:
data.resize(ds.tellp());
}
void copy_row(const chain::key128x128_value_object& obj, vector<char>& data)const {
void copy_row(const chain::contracts::key128x128_value_object& obj, vector<char>& data)const {
data.resize( 2*sizeof(uint128_t) + obj.value.size() );
memcpy( data.data(), &obj.primary_key, sizeof(uint128_t) );
memcpy( data.data()+sizeof(uint128_t), &obj.secondary_key, sizeof(uint128_t) );
memcpy( data.data()+2*sizeof(uint128_t), obj.value.data(), obj.value.size() );
}
void copy_row(const chain::key64x64x64_value_object& obj, vector<char>& data)const {
void copy_row(const chain::contracts::key64x64x64_value_object& obj, vector<char>& data)const {
data.resize( 3*sizeof(uint64_t) + obj.value.size() );
memcpy( data.data(), &obj.primary_key, sizeof(uint64_t) );
memcpy( data.data()+sizeof(uint64_t), &obj.secondary_key, sizeof(uint64_t) );
......@@ -217,39 +217,44 @@ public:
abi_serializer abis;
abis.set_abi(abi);
const auto& idx = d.get_index<IndexType, Scope>();
auto lower = idx.lower_bound( boost::make_tuple(p.scope, p.code, p.table ) );
auto upper = idx.upper_bound( boost::make_tuple(p.scope, p.code, name(uint64_t(p.table)+1) ) );
if( p.lower_bound.size() ) {
lower = idx.lower_bound(boost::make_tuple(p.scope, p.code, p.table, fc::variant(p.lower_bound).as<typename IndexType::value_type::key_type>()));
}
if( p.upper_bound.size() ) {
upper = idx.lower_bound(boost::make_tuple(p.scope, p.code, p.table, fc::variant(p.upper_bound).as<typename IndexType::value_type::key_type>()));
}
vector<char> data;
auto end = fc::time_point::now() + fc::microseconds( 1000*10 ); /// 10ms max time
unsigned int count = 0;
auto itr = lower;
for( itr = lower; itr != upper && itr->table == p.table; ++itr ) {
copy_row(*itr, data);
if( p.json ) {
result.rows.emplace_back(abis.binary_to_variant(abis.get_table_type(p.table), data) );
} else {
result.rows.emplace_back(fc::variant(data));
const auto* t_id = d.find<chain::contracts::table_id_object, chain::contracts::by_scope_code_table>(boost::make_tuple(p.scope, p.code, p.table));
if (t_id != nullptr) {
const auto &idx = d.get_index<IndexType, Scope>();
decltype(t_id->id) next_tid(t_id->id._id + 1);
auto lower = idx.lower_bound(boost::make_tuple(t_id->id));
auto upper = idx.lower_bound(boost::make_tuple(next_tid));
if (p.lower_bound.size()) {
lower = idx.lower_bound(boost::make_tuple(t_id->id, fc::variant(
p.lower_bound).as<typename IndexType::value_type::key_type>()));
}
if (p.upper_bound.size()) {
upper = idx.lower_bound(boost::make_tuple(t_id->id, fc::variant(
p.upper_bound).as<typename IndexType::value_type::key_type>()));
}
vector<char> data;
auto end = fc::time_point::now() + fc::microseconds(1000 * 10); /// 10ms max time
unsigned int count = 0;
auto itr = lower;
for (itr = lower; itr != upper; ++itr) {
copy_row(*itr, data);
if( ++count == p.limit || fc::time_point::now() > end ) {
break;
if (p.json) {
result.rows.emplace_back(abis.binary_to_variant(abis.get_table_type(p.table), data));
} else {
result.rows.emplace_back(fc::variant(data));
}
if (++count == p.limit || fc::time_point::now() > end) {
break;
}
}
if (itr != upper) {
result.more = true;
}
}
if( itr != upper ) {
result.more = true;
}
return result;
}
......
#include <boost/test/unit_test.hpp>
#include <eosio/testing/tester.hpp>
#include <eosio/chain/contracts/balance_object.hpp>
#include <eosio/chain/contracts/staked_balance_objects.hpp>
......@@ -10,7 +9,6 @@ using namespace eosio::chain;
using namespace eosio::chain::contracts;
using namespace eosio::testing;
BOOST_AUTO_TEST_SUITE(transfer_tests)
......@@ -23,15 +21,15 @@ BOOST_AUTO_TEST_CASE( transfer_test ) { try {
test.transfer( N(inita), N(dan), "10.0000 EOS", "memo" );
{
const auto& dans_balance = test.get<balance_object,by_owner_name>( N(dan) );
FC_ASSERT( dans_balance.balance == asset::from_string("10.0000 EOS") );
const auto& dans_balance = test.get_balance( N(dan) );
FC_ASSERT( dans_balance == asset::from_string("10.0000 EOS") );
}
test.produce_block();
{
const auto& dans_balance = test.get<balance_object,by_owner_name>( N(dan) );
FC_ASSERT( dans_balance.balance == asset::from_string("10.0000 EOS") );
const auto& dans_balance = test.get_balance( N(dan) );
FC_ASSERT( dans_balance == asset::from_string("10.0000 EOS") );
}
/// insufficient funds
......@@ -42,13 +40,13 @@ BOOST_AUTO_TEST_CASE( transfer_test ) { try {
/// verify that bart now has 10.000
const auto& barts_balance = test.get<balance_object,by_owner_name>( N(bart) );
FC_ASSERT( barts_balance.balance == asset::from_string("10.0000 EOS") );
const auto& barts_balance = test.get_balance( N(bart) );
FC_ASSERT( barts_balance == asset::from_string("10.0000 EOS") );
{
/// verify that dan now has 0.000
const auto& dans_balance = test.get<balance_object,by_owner_name>( N(dan) );
FC_ASSERT( dans_balance.balance == asset::from_string("0.0000 EOS") );
const auto& dans_balance = test.get_balance( N(dan) );
FC_ASSERT( dans_balance == asset::from_string("0.0000 EOS") );
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册