提交 c2b5c5d0 编写于 作者: M Matt Witherspoon

Merge remote-tracking branch 'origin/master' into remove_net_plugin_dead_opt

......@@ -30,6 +30,7 @@ add_subdirectory(eosio.bios)
add_subdirectory(noop)
add_subdirectory(dice)
add_subdirectory(tic_tac_toe)
add_subdirectory(payloadless)
file(GLOB SKELETONS RELATIVE ${CMAKE_SOURCE_DIR}/contracts "skeleton/*")
......
......@@ -4,7 +4,6 @@
*/
#pragma once
#include <eosiolib/eosio.hpp>
#include <eosiolib/asset.hpp>
#include <eosiolib/eosio.hpp>
......
......@@ -43,8 +43,11 @@ namespace eosio {
//using malloc/free here potentially is not exception-safe, although WASM doesn't support exceptions
constexpr size_t max_stack_buffer_size = 512;
void* buffer = max_stack_buffer_size < size ? malloc(size) : alloca(size);
read_action_data( buffer, size );
void* buffer = nullptr;
if( size > 0 ) {
buffer = max_stack_buffer_size < size ? malloc(size) : alloca(size);
read_action_data( buffer, size );
}
auto args = unpack<std::tuple<std::decay_t<Args>...>>( (char*)buffer, size );
......
......@@ -17,6 +17,7 @@
#include <boost/multi_index/mem_fun.hpp>
#include <eosiolib/action.h>
#include <eosiolib/types.hpp>
#include <eosiolib/serialize.hpp>
#include <eosiolib/datastream.hpp>
......
......@@ -26,7 +26,7 @@ namespace eosio {
public:
singleton( account_name code, scope_name scope ) : _t( code, scope ) /*_code(code), _scope(scope)*/ {}
singleton( account_name code, scope_name scope ) : _t( code, scope ) {}
bool exists() {
return _t.find( pk_value ) != _t.end();
......
file(GLOB ABI_FILES "*.abi")
configure_file("${ABI_FILES}" "${CMAKE_CURRENT_BINARY_DIR}" COPYONLY)
add_wast_executable(TARGET identity
add_wast_library(TARGET identity_common
SOURCE_FILES common.cpp
INCLUDE_FOLDERS "${STANDARD_INCLUDE_FOLDERS}"
LIBRARIES libc++ libc eosiolib
DESTINATION_FOLDER ${CMAKE_CURRENT_BINARY_DIR}
)
add_wast_executable(TARGET identity
INCLUDE_FOLDERS "${STANDARD_INCLUDE_FOLDERS}"
LIBRARIES identity_common libc++ libc eosiolib
DESTINATION_FOLDER ${CMAKE_CURRENT_BINARY_DIR}
)
add_wast_library(TARGET identity_interface
SOURCE_FILES interface.cpp
INCLUDE_FOLDERS "${STANDARD_INCLUDE_FOLDERS}"
LIBRARIES identity_common libc++ libc eosiolib
DESTINATION_FOLDER ${CMAKE_CURRENT_BINARY_DIR}
)
add_subdirectory(test)
#include "common.hpp"
#include <eosiolib/chain.h>
namespace identity {
bool identity_base::is_trusted_by( account_name trusted, account_name by ) {
trust_table t( _self, by );
return t.find( trusted ) != t.end();
}
bool identity_base::is_trusted( account_name acnt ) {
account_name active_producers[21];
auto active_prod_size = get_active_producers( active_producers, sizeof(active_producers) );
auto count = active_prod_size / sizeof(account_name);
for( size_t i = 0; i < count; ++i ) {
if( active_producers[i] == acnt )
return true;
}
for( size_t i = 0; i < count; ++i ) {
if( is_trusted_by( acnt, active_producers[i] ) )
return true;
}
return false;
}
}
#pragma once
#include <eosiolib/singleton.hpp>
#include <eosiolib/multi_index.hpp>
namespace identity {
typedef uint64_t identity_name;
typedef uint64_t property_name;
typedef uint64_t property_type_name;
struct certvalue {
property_name property; ///< name of property, base32 encoded i64
std::string type; ///< defines type serialized in data
std::vector<char> data; ///<
std::string memo; ///< meta data documenting basis of certification
uint8_t confidence = 1; ///< used to define liability for lies,
/// 0 to delete
EOSLIB_SERIALIZE( certvalue, (property)(type)(data)(memo)(confidence) )
};
struct certrow {
uint64_t id;
property_name property;
uint64_t trusted;
account_name certifier;
uint8_t confidence = 0;
std::string type;
std::vector<char> data;
uint64_t primary_key() const { return id; }
/* constexpr */ static eosio::key256 key(uint64_t property, uint64_t trusted, uint64_t certifier) {
/*
key256 key;
key.uint64s[0] = property;
key.uint64s[1] = trusted;
key.uint64s[2] = certifier;
key.uint64s[3] = 0;
*/
return eosio::key256::make_from_word_sequence<uint64_t>(property, trusted, certifier);
}
eosio::key256 get_key() const { return key(property, trusted, certifier); }
EOSLIB_SERIALIZE( certrow , (property)(trusted)(certifier)(confidence)(type)(data)(id) )
};
struct identrow {
uint64_t identity;
account_name creator;
uint64_t primary_key() const { return identity; }
EOSLIB_SERIALIZE( identrow , (identity)(creator) )
};
struct trustrow {
account_name account;
uint64_t primary_key() const { return account; }
EOSLIB_SERIALIZE( trustrow, (account) )
};
typedef eosio::multi_index<N(certs), certrow,
eosio::indexed_by< N(bytuple), eosio::const_mem_fun<certrow, eosio::key256, &certrow::get_key> >
> certs_table;
typedef eosio::multi_index<N(ident), identrow> idents_table;
typedef eosio::singleton<N(account), identity_name> accounts_table;
typedef eosio::multi_index<N(trust), trustrow> trust_table;
class identity_base {
public:
identity_base( account_name acnt) : _self( acnt ) {}
bool is_trusted_by( account_name trusted, account_name by );
bool is_trusted( account_name acnt );
protected:
account_name _self;
};
}
#include "identity.hpp"
#include "common.hpp"
extern "C" {
/// The apply method implements the dispatch of events to this contract
void apply( uint64_t receiver, uint64_t code, uint64_t action ) {
identity::contract< N(identity) >::apply( code, action );
}
}
#include <eosiolib/contract.hpp>
#include <eosiolib/dispatcher.hpp>
#include <eosiolib/vector.hpp>
namespace identity {
using eosio::action_meta;
using eosio::singleton;
using eosio::key256;
using std::string;
using std::vector;
/**
* This contract maintains a graph database of certified statements about an
* identity. An identity is separated from the concept of an account because
* the mapping of identity to accounts is subject to community consensus.
*
* Some use cases need a global source of trust, this trust rooted in the voter
* who selects block producers. A block producer's opinion is "trusted" and so
* is the opinion of anyone the block producer marks as "trusted".
*
* When a block producer is voted out the implicit trust in every certification
* they made or those they trusted made is removed. All users are liable for
* making false certifications.
*
* An account needs to claim the identity and a trusted account must certify the
* claim.
*
* Data for an identity is stored:
*
* DeployToAccount / identity / certs / [property, trusted, certifier] => value
*
* Questions database is designed to answer:
*
* 1. has $identity.$unique been certified a "trusted" certifier
* 2. has $identity.$property been certified by $account
* 3. has $identity.$trusted been certified by a "trusted" certifier
* 4. what account has authority to speak on behalf of identity?
* - for each trusted owner certification
* check to see if the account has claimed it
*
* 5. what identity does account have authority to speak on behalf?
* - check what identity the account has self certified owner
* - verify that a trusted certifier has confirmed owner
*
* This database structure enables parallel opeartions on independent identities.
*
* When an account certs a property we check to see if that
*/
class contract : public identity_base {
public:
using identity_base::identity_base;
void settrust( const account_name trustor, ///< the account authorizing the trust
const account_name trusting, ///< the account receiving the trust
const uint8_t trust = 0 )/// 0 to remove, -1 to mark untrusted, 1 to mark trusted
{
require_auth( trustor );
require_recipient( trusting );
trust_table table( _self, trustor );
auto itr = table.find(trusting);
if( itr == table.end() && trust > 0 ) {
table.emplace( trustor, [&](trustrow& row) {
row.account = trusting;
});
} else if( itr != table.end() && trust == 0 ) {
table.erase(itr);
}
}
/**
* This action create a new globally unique 64 bit identifier,
* to minimize collisions each account is automatically assigned
* a 32 bit identity prefix based upon hash(account_name) ^ hash(tapos).
*
* With this method no two accounts are likely to be assigned the same
* 32 bit prefix consistantly due to the constantly changing tapos. This prevents
* abuse of 'creator' selection to generate intentional conflicts with other users.
*
* The creator can determine the last 32 bits using an algorithm of their choice. We
* presume the creator's algorithm can avoid collisions with itself.
*
* Even if two accounts get a collision in first 32 bits, a proper creator algorithm
* should generate randomness in last 32 bits that will minimize collisions. In event
* of collision transaction will fail and creator can try again.
*
* A 64 bit identity is used because the key is used frequently and it makes for more
* effecient tables/scopes/etc.
*/
void create( const account_name creator, const uint64_t identity ) {
require_auth( creator );
idents_table t( _self, _self );
auto itr = t.find( identity );
eosio_assert( itr == t.end(), "identity already exists" );
eosio_assert( identity != 0, "identity=0 is not allowed" );
t.emplace(creator, [&](identrow& i) {
i.identity = identity;
i.creator = creator;
});
}
void certprop( const account_name bill_storage_to, ///< account which is paying for storage
const account_name certifier,
const identity_name identity,
const vector<certvalue>& values )
{
require_auth( certifier );
if( bill_storage_to != certifier )
require_auth( bill_storage_to );
idents_table t( _self, _self );
eosio_assert( t.find( identity ) != t.end(), "identity does not exist" );
/// the table exists in the scope of the identity
certs_table certs( _self, identity );
bool trusted = is_trusted( certifier );
for( const auto& value : values ) {
auto idx = certs.template get_index<N(bytuple)>();
if (value.confidence) {
eosio_assert(value.type.size() <= 32, "certrow::type should be not longer than 32 bytes");
auto itr = idx.lower_bound( certrow::key(value.property, trusted, certifier) );
if (itr != idx.end() && itr->property == value.property && itr->trusted == trusted && itr->certifier == certifier) {
idx.modify(itr, 0, [&](certrow& row) {
row.confidence = value.confidence;
row.type = value.type;
row.data = value.data;
});
} else {
auto pk = certs.available_primary_key();
certs.emplace(_self, [&](certrow& row) {
row.id = pk;
row.property = value.property;
row.trusted = trusted;
row.certifier = certifier;
row.confidence = value.confidence;
row.type = value.type;
row.data = value.data;
});
}
auto itr_old = idx.lower_bound( certrow::key(value.property, !trusted, certifier) );
if (itr_old != idx.end() && itr_old->property == value.property && itr_old->trusted == !trusted && itr_old->certifier == certifier) {
idx.erase(itr_old);
}
//special handling for owner
if (value.property == N(owner)) {
eosio_assert(sizeof(account_name) == value.data.size(), "data size doesn't match account_name size");
account_name acnt = *reinterpret_cast<const account_name*>(value.data.data());
if (certifier == acnt) { //only self-certitication affects accounts_table
accounts_table( _self, acnt ).set( identity, acnt );
}
}
} else {
bool removed = false;
auto itr = idx.lower_bound( certrow::key(value.property, trusted, certifier) );
if (itr != idx.end() && itr->property == value.property && itr->trusted == trusted && itr->certifier == certifier) {
idx.erase(itr);
} else {
removed = true;
}
itr = idx.lower_bound( certrow::key(value.property, !trusted, certifier) );
if (itr != idx.end() && itr->property == value.property && itr->trusted == !trusted && itr->certifier == certifier) {
idx.erase(itr);
} else {
removed = true;
}
//special handling for owner
if (value.property == N(owner)) {
eosio_assert(sizeof(account_name) == value.data.size(), "data size doesn't match account_name size");
account_name acnt = *reinterpret_cast<const account_name*>(value.data.data());
if (certifier == acnt) { //only self-certitication affects accounts_table
accounts_table( _self, acnt ).remove();
}
}
}
}
}
};
} /// namespace identity
EOSIO_ABI( identity::contract, (create)(certprop)(settrust) );
#pragma once
#include <eosiolib/chain.h>
#include <eosiolib/dispatcher.hpp>
#include <eosiolib/singleton.hpp>
#include <eosiolib/multi_index.hpp>
#include <eosiolib/vector.hpp>
namespace identity {
using eosio::action_meta;
using eosio::singleton;
using eosio::key256;
using std::string;
using std::vector;
/**
* This contract maintains a graph database of certified statements about an
* identity. An identity is separated from the concept of an account because
* the mapping of identity to accounts is subject to community consensus.
*
* Some use cases need a global source of trust, this trust rooted in the voter
* who selects block producers. A block producer's opinion is "trusted" and so
* is the opinion of anyone the block producer marks as "trusted".
*
* When a block producer is voted out the implicit trust in every certification
* they made or those they trusted made is removed. All users are liable for
* making false certifications.
*
* An account needs to claim the identity and a trusted account must certify the
* claim.
*
* Data for an identity is stored:
*
* DeployToAccount / identity / certs / [property, trusted, certifier] => value
*
* Questions database is designed to answer:
*
* 1. has $identity.$unique been certified a "trusted" certifier
* 2. has $identity.$property been certified by $account
* 3. has $identity.$trusted been certified by a "trusted" certifier
* 4. what account has authority to speak on behalf of identity?
* - for each trusted owner certification
* check to see if the account has claimed it
*
* 5. what identity does account have authority to speak on behalf?
* - check what identity the account has self certified owner
* - verify that a trusted certifier has confirmed owner
*
* This database structure enables parallel opeartions on independent identities.
*
* When an account certs a property we check to see if that
*/
template<uint64_t DeployToAccount>
class contract {
public:
static const uint64_t code = DeployToAccount;
typedef uint64_t identity_name;
typedef uint64_t property_name;
typedef uint64_t property_type_name;
/**
* This action create a new globally unique 64 bit identifier,
* to minimize collisions each account is automatically assigned
* a 32 bit identity prefix based upon hash(account_name) ^ hash(tapos).
*
* With this method no two accounts are likely to be assigned the same
* 32 bit prefix consistantly due to the constantly changing tapos. This prevents
* abuse of 'creator' selection to generate intentional conflicts with other users.
*
* The creator can determine the last 32 bits using an algorithm of their choice. We
* presume the creator's algorithm can avoid collisions with itself.
*
* Even if two accounts get a collision in first 32 bits, a proper creator algorithm
* should generate randomness in last 32 bits that will minimize collisions. In event
* of collision transaction will fail and creator can try again.
*
* A 64 bit identity is used because the key is used frequently and it makes for more
* effecient tables/scopes/etc.
*/
struct create : public action_meta< code, N(create) >
{
account_name creator;
uint64_t identity = 0; ///< first 32 bits determinsitically derived from creator and tapos
EOSLIB_SERIALIZE( create, (creator)(identity) )
};
struct certvalue {
property_name property; ///< name of property, base32 encoded i64
string type; ///< defines type serialized in data
vector<char> data; ///<
string memo; ///< meta data documenting basis of certification
uint8_t confidence = 1; ///< used to define liability for lies,
/// 0 to delete
EOSLIB_SERIALIZE( certvalue, (property)(type)(data)(memo)(confidence) )
};
struct certprop : public action_meta< code, N(certprop) >
{
account_name bill_storage_to; ///< account which is paying for storage
account_name certifier;
identity_name identity;
vector<certvalue> values;
EOSLIB_SERIALIZE( certprop, (bill_storage_to)(certifier)(identity)(values) )
};
struct settrust : public action_meta< code, N(settrust) >
{
account_name trustor; ///< the account authorizing the trust
account_name trusting; ///< the account receiving the trust
uint8_t trust = 0; /// 0 to remove, -1 to mark untrusted, 1 to mark trusted
EOSLIB_SERIALIZE( settrust, (trustor)(trusting)(trust) )
};
struct certrow {
uint64_t id;
property_name property;
uint64_t trusted;
account_name certifier;
uint8_t confidence = 0;
string type;
vector<char> data;
uint64_t primary_key() const { return id; }
/* constexpr */ static key256 key(uint64_t property, uint64_t trusted, uint64_t certifier) {
/*
key256 key;
key.uint64s[0] = property;
key.uint64s[1] = trusted;
key.uint64s[2] = certifier;
key.uint64s[3] = 0;
*/
return key256::make_from_word_sequence<uint64_t>(property, trusted, certifier);
}
key256 get_key() const { return key(property, trusted, certifier); }
EOSLIB_SERIALIZE( certrow , (property)(trusted)(certifier)(confidence)(type)(data)(id) )
};
struct identrow {
uint64_t identity;
account_name creator;
uint64_t primary_key() const { return identity; }
EOSLIB_SERIALIZE( identrow , (identity)(creator) )
};
struct trustrow {
account_name account;
uint64_t primary_key() const { return account; }
EOSLIB_SERIALIZE( trustrow, (account) )
};
typedef eosio::multi_index<N(certs), certrow,
eosio::indexed_by< N(bytuple), eosio::const_mem_fun<certrow, key256, &certrow::get_key> >
> certs_table;
typedef eosio::multi_index<N(ident), identrow> idents_table;
typedef singleton<N(account), identity_name> accounts_table;
typedef eosio::multi_index<N(trust), trustrow> trust_table;
static identity_name get_claimed_identity( account_name acnt ) {
return accounts_table( code, acnt ).get_or_default(0);
}
static account_name get_owner_for_identity( uint64_t receiver, identity_name ident ) {
// for each trusted owner certification
// check to see if the certification is still trusted
// check to see if the account has claimed it
certs_table certs( code, ident );
auto idx = certs.template get_index<N(bytuple)>();
auto itr = idx.lower_bound(certrow::key(N(owner), 1, 0));
account_name owner = 0;
while (itr != idx.end() && itr->property == N(owner) && itr->trusted) {
if (sizeof(account_name) == itr->data.size()) {
account_name account = *reinterpret_cast<const account_name*>(itr->data.data());
if (ident == get_claimed_identity(account)) {
if (is_trusted(itr->certifier) ) {
// the certifier is still trusted
if (!owner || owner == account) {
owner = account;
} else {
//contradiction found: different owners certified for the same identity
return 0;
}
} else if (DeployToAccount == receiver){
//the certifier is no longer trusted, need to unset the flag
idx.modify(itr, 0, [&](certrow& r) {
r.trusted = 0;
});
} else {
// the certifier is no longer trusted, but the code runs in read-only mode
}
}
} else {
// bad row - skip it
}
++itr;
}
if (owner) {
//owner found, no contradictions among certifications flaged as trusted
return owner;
}
// trusted certification not found
// let's see if any untrusted certifications became trusted
itr = idx.lower_bound(certrow::key(N(owner), 0, 0));
while (itr != idx.end() && itr->property == N(owner) && !itr->trusted) {
if (sizeof(account_name) == itr->data.size()) {
account_name account = *reinterpret_cast<const account_name*>(itr->data.data());
if (ident == get_claimed_identity(account) && is_trusted(itr->certifier)) {
if (DeployToAccount == receiver) {
// the certifier became trusted and we have permissions to update the flag
idx.modify(itr, 0, [&](certrow& r) {
r.trusted = 1;
});
}
if (!owner || owner == account) {
owner = account;
} else {
//contradiction found: different owners certified for the same identity
return 0;
}
}
} else {
// bad row - skip it
}
++itr;
}
return owner;
}
static identity_name get_identity_for_account( uint64_t receiver, account_name acnt ) {
// check what identity the account has self certified owner
// verify that a trusted certifier has confirmed owner
auto identity = get_claimed_identity(acnt);
return (identity != 0 && acnt == get_owner_for_identity(receiver, identity)) ? identity : 0;
}
static bool is_trusted_by( account_name trusted, account_name by ) {
trust_table t( code, by );
return t.find( trusted ) != t.end();
}
static bool is_trusted( account_name acnt ) {
account_name active_producers[21];
auto active_prod_size = get_active_producers( active_producers, sizeof(active_producers) );
auto count = active_prod_size / sizeof(account_name);
for( size_t i = 0; i < count; ++i ) {
if( active_producers[i] == acnt )
return true;
}
for( size_t i = 0; i < count; ++i ) {
if( is_trusted_by( acnt, active_producers[i] ) )
return true;
}
return false;
}
static void on( const settrust& t ) {
require_auth( t.trustor );
require_recipient( t.trusting );
trust_table table( code, t.trustor );
auto itr = table.find(t.trusting);
if( itr == table.end() && t.trust > 0 ) {
table.emplace( t.trustor, [&](trustrow& row) {
row.account = t.trusting;
});
} else if( itr != table.end() && t.trust == 0 ) {
table.erase(itr);
}
}
static void on( const create& c ) {
require_auth( c.creator );
idents_table t( code, code);
auto itr = t.find( c.identity );
eosio_assert( itr == t.end(), "identity already exists" );
eosio_assert( c.identity != 0, "identity=0 is not allowed" );
t.emplace(c.creator, [&](identrow& i) {
i.identity = c.identity;
i.creator = c.creator;
});
}
static void on( const certprop& cert ) {
require_auth( cert.certifier );
if( cert.bill_storage_to != cert.certifier )
require_auth( cert.bill_storage_to );
idents_table t( code, code );
eosio_assert( t.find( cert.identity ) != t.end(), "identity does not exist" );
/// the table exists in the scope of the identity
certs_table certs( code, cert.identity );
bool trusted = is_trusted( cert.certifier );
for( const auto& value : cert.values ) {
auto idx = certs.template get_index<N(bytuple)>();
if (value.confidence) {
eosio_assert(value.type.size() <= 32, "certrow::type should be not longer than 32 bytes");
auto itr = idx.lower_bound( certrow::key(value.property, trusted, cert.certifier) );
if (itr != idx.end() && itr->property == value.property && itr->trusted == trusted && itr->certifier == cert.certifier) {
idx.modify(itr, 0, [&](certrow& row) {
row.confidence = value.confidence;
row.type = value.type;
row.data = value.data;
});
} else {
auto pk = certs.available_primary_key();
certs.emplace(code, [&](certrow& row) {
row.id = pk;
row.property = value.property;
row.trusted = trusted;
row.certifier = cert.certifier;
row.confidence = value.confidence;
row.type = value.type;
row.data = value.data;
});
}
auto itr_old = idx.lower_bound( certrow::key(value.property, !trusted, cert.certifier) );
if (itr_old != idx.end() && itr_old->property == value.property && itr_old->trusted == !trusted && itr_old->certifier == cert.certifier) {
idx.erase(itr_old);
}
//special handling for owner
if (value.property == N(owner)) {
eosio_assert(sizeof(account_name) == value.data.size(), "data size doesn't match account_name size");
account_name acnt = *reinterpret_cast<const account_name*>(value.data.data());
if (cert.certifier == acnt) { //only self-certitication affects accounts_table
accounts_table( code, acnt ).set( cert.identity, acnt );
}
}
} else {
bool removed = false;
auto itr = idx.lower_bound( certrow::key(value.property, trusted, cert.certifier) );
if (itr != idx.end() && itr->property == value.property && itr->trusted == trusted && itr->certifier == cert.certifier) {
idx.erase(itr);
} else {
removed = true;
}
itr = idx.lower_bound( certrow::key(value.property, !trusted, cert.certifier) );
if (itr != idx.end() && itr->property == value.property && itr->trusted == !trusted && itr->certifier == cert.certifier) {
idx.erase(itr);
} else {
removed = true;
}
//special handling for owner
if (value.property == N(owner)) {
eosio_assert(sizeof(account_name) == value.data.size(), "data size doesn't match account_name size");
account_name acnt = *reinterpret_cast<const account_name*>(value.data.data());
if (cert.certifier == acnt) { //only self-certitication affects accounts_table
accounts_table( code, acnt ).remove();
}
}
}
}
}
static void apply( account_name c, action_name act) {
eosio::dispatch<contract, create, certprop, settrust>(c,act);
}
};
} /// namespace identity
#include "interface.hpp"
namespace identity {
identity_name interface::get_claimed_identity( account_name acnt ) {
return accounts_table( _self, acnt ).get_or_default(0);
}
account_name interface::get_owner_for_identity( uint64_t receiver, identity_name ident ) {
// for each trusted owner certification
// check to see if the certification is still trusted
// check to see if the account has claimed it
certs_table certs( _self, ident );
auto idx = certs.template get_index<N(bytuple)>();
auto itr = idx.lower_bound(certrow::key(N(owner), 1, 0));
account_name owner = 0;
while (itr != idx.end() && itr->property == N(owner) && itr->trusted) {
if (sizeof(account_name) == itr->data.size()) {
account_name account = *reinterpret_cast<const account_name*>(itr->data.data());
if (ident == get_claimed_identity(account)) {
if (is_trusted(itr->certifier) ) {
// the certifier is still trusted
if (!owner || owner == account) {
owner = account;
} else {
//contradiction found: different owners certified for the same identity
return 0;
}
} else if (_self == receiver){
//the certifier is no longer trusted, need to unset the flag
idx.modify(itr, 0, [&](certrow& r) {
r.trusted = 0;
});
} else {
// the certifier is no longer trusted, but the code runs in read-only mode
}
}
} else {
// bad row - skip it
}
++itr;
}
if (owner) {
//owner found, no contradictions among certifications flaged as trusted
return owner;
}
// trusted certification not found
// let's see if any untrusted certifications became trusted
itr = idx.lower_bound(certrow::key(N(owner), 0, 0));
while (itr != idx.end() && itr->property == N(owner) && !itr->trusted) {
if (sizeof(account_name) == itr->data.size()) {
account_name account = *reinterpret_cast<const account_name*>(itr->data.data());
if (ident == get_claimed_identity(account) && is_trusted(itr->certifier)) {
if (_self == receiver) {
// the certifier became trusted and we have permissions to update the flag
idx.modify(itr, 0, [&](certrow& r) {
r.trusted = 1;
});
}
if (!owner || owner == account) {
owner = account;
} else {
//contradiction found: different owners certified for the same identity
return 0;
}
}
} else {
// bad row - skip it
}
++itr;
}
return owner;
}
identity_name interface::get_identity_for_account( uint64_t receiver, account_name acnt ) {
// check what identity the account has self certified owner
// verify that a trusted certifier has confirmed owner
auto identity = get_claimed_identity(acnt);
return (identity != 0 && acnt == get_owner_for_identity(receiver, identity)) ? identity : 0;
}
} // namespace identity
#pragma once
#include "common.hpp"
namespace identity {
class interface : public identity_base {
public:
using identity_base::identity_base;
identity_name get_claimed_identity( account_name acnt );
account_name get_owner_for_identity( uint64_t receiver, identity_name ident );
identity_name get_identity_for_account( uint64_t receiver, account_name acnt );
};
}
......@@ -2,6 +2,6 @@ set(ABI_FILES "identity_test.abi")
configure_file("${ABI_FILES}" "${CMAKE_CURRENT_BINARY_DIR}" COPYONLY)
add_wast_executable(TARGET identity_test
INCLUDE_FOLDERS "${STANDARD_INCLUDE_FOLDERS}"
LIBRARIES libc++ libc eosiolib
LIBRARIES identity_interface identity_common libc++ libc eosiolib
DESTINATION_FOLDER ${CMAKE_CURRENT_BINARY_DIR}
)
#include <eosiolib/chain.h>
#include <eosiolib/action.h>
#include <eosiolib/contract.hpp>
#include <eosiolib/dispatcher.hpp>
#include <eosiolib/singleton.hpp>
#include <eosiolib/vector.hpp>
#include <identity/identity.hpp>
#include <identity/interface.hpp>
namespace identity_test {
......@@ -12,56 +10,26 @@ namespace identity_test {
using std::string;
using std::vector;
class contract {
class contract : public eosio::contract {
public:
static const uint64_t code = N(identitytest);
typedef identity::contract< N(identity) > identity_contract;
typedef identity_contract::identity_name identity_name;
typedef identity_contract::property_name property_name;
struct get_owner_for_identity : public action_meta< code, N(getowner) >
{
uint64_t identity;
EOSLIB_SERIALIZE( get_owner_for_identity, (identity) )
};
struct get_identity_for_account : public action_meta< code, N(getidentity) >
{
account_name account ;
EOSLIB_SERIALIZE( get_identity_for_account, (account) )
};
static constexpr uint64_t code = N(identitytest);
typedef singleton<N(result), uint64_t> result_table;
static void on( uint64_t receiver, const get_owner_for_identity& c ) {
account_name owner = identity_contract::get_owner_for_identity(receiver, c.identity);
using eosio::contract::contract;
void getowner( const uint64_t identity ) {
identity::interface iface( N(identity) );
account_name owner = iface.get_owner_for_identity(current_receiver(), identity);
result_table( code, 0 ).set( owner, code ); //use scope = 0 for simplicity
}
static void on( uint64_t receiver, const get_identity_for_account& c ) {
identity_name idnt = identity_contract::get_identity_for_account(receiver, c.account);
void getidentity( const account_name account ) {
identity::interface iface( N(identity) );
identity::identity_name idnt = iface.get_identity_for_account(current_receiver(), account);
result_table( code, 0 ).set(idnt, code ); //use scope = 0 for simplicity
}
static void apply( uint64_t receiver, account_name c, action_name act) {
if( code == N(identitytest) ) {
if ( act == N(getowner) ) {
contract::on( receiver, eosio::unpack_action_data<get_owner_for_identity>() );
} else if ( act == N(getidentity) ) {
contract::on( receiver, eosio::unpack_action_data<get_identity_for_account>() );
}
}
}
};
} /// namespace identity
extern "C" {
/// The apply method implements the dispatch of events to this contract
void apply( uint64_t receiver, uint64_t code, uint64_t action ) {
identity_test::contract::apply( receiver, code, action );
}
}
EOSIO_ABI( identity_test::contract, (getowner)(getidentity) );
file(GLOB ABI_FILES "*.abi")
configure_file("${ABI_FILES}" "${CMAKE_CURRENT_BINARY_DIR}" COPYONLY)
add_wast_executable(TARGET payloadless
INCLUDE_FOLDERS "${STANDARD_INCLUDE_FOLDERS}"
LIBRARIES libc libc++ eosiolib
DESTINATION_FOLDER ${CMAKE_CURRENT_BINARY_DIR}
)
{
"____comment": "This file was generated by eosio-abigen. DO NOT EDIT - 2018-04-19T09:07:16",
"types": [],
"structs": [{
"name": "doit",
"base": "",
"fields": []
}
],
"actions": [{
"name": "doit",
"type": "doit",
"ricardian_contract": "# CONTRACT FOR payloadless::doit## ACTION NAME: doit\n### Parameters### Parameters\nInput paramters:Input paramters:\n\nImplied parameters: Implied parameters: \n\n### Intent### Intent\nINTENT. The intention of the author and the invoker of this contract is to print output. It shall have no other effect.INTENT. The intention of the author and the invoker of this contract is to print output. It shall have no other effect.\n\n### Term### Term\nTERM. This Contract expires at the conclusion of code execution.TERM. This Contract expires at the conclusion of code execution.\n"
}
],
"tables": [],
"ricardian_clauses": [{
"id": "Warranty",
"body": "WARRANTY. The invoker of the contract action shall uphold its Obligations under this Contract in a timely and workmanlike manner, using knowledge and recommendations for performing the services which meet generally acceptable standards set forth by EOS.IO Blockchain Block Producers.\n\n"
},{
"id": "Default",
"body": "DEFAULT. The occurrence of any of the following shall constitute a material default under this Contract: \n\n"
},{
"id": "Remedies",
"body": "REMEDIES. In addition to any and all other rights a party may have available according to law, if a party defaults by failing to substantially perform any provision, term or condition of this Contract, the other party may terminate the Contract by providing written notice to the defaulting party. This notice shall describe with sufficient detail the nature of the default. The party receiving such notice shall promptly be removed from being a Block Producer and this Contract shall be automatically terminated. \n \n"
},{
"id": "Force Majeure",
"body": "FORCE MAJEURE. If performance of this Contract or any obligation under this Contract is prevented, restricted, or interfered with by causes beyond either party's reasonable control (\"Force Majeure\"), and if the party unable to carry out its obligations gives the other party prompt written notice of such event, then the obligations of the party invoking this provision shall be suspended to the extent necessary by such event. The term Force Majeure shall include, without limitation, acts of God, fire, explosion, vandalism, storm or other similar occurrence, orders or acts of military or civil authority, or by national emergencies, insurrections, riots, or wars, or strikes, lock-outs, work stoppages, or supplier failures. The excused party shall use reasonable efforts under the circumstances to avoid or remove such causes of non-performance and shall proceed to perform with reasonable dispatch whenever such causes are removed or ceased. An act or omission shall be deemed within the reasonable control of a party if committed, omitted, or caused by such party, or its employees, officers, agents, or affiliates. \n \n"
},{
"id": "Dispute Resolution",
"body": "DISPUTE RESOLUTION. Any controversies or disputes arising out of or relating to this Contract will be resolved by binding arbitration under the default rules set forth by the EOS.IO Blockchain. The arbitrator's award will be final, and judgment may be entered upon it by any court having proper jurisdiction. \n \n"
},{
"id": "Entire Agreement",
"body": "ENTIRE AGREEMENT. This Contract contains the entire agreement of the parties, and there are no other promises or conditions in any other agreement whether oral or written concerning the subject matter of this Contract. This Contract supersedes any prior written or oral agreements between the parties. \n\n"
},{
"id": "Severability",
"body": "SEVERABILITY. If any provision of this Contract will be held to be invalid or unenforceable for any reason, the remaining provisions will continue to be valid and enforceable. If a court finds that any provision of this Contract is invalid or unenforceable, but that by limiting such provision it would become valid and enforceable, then such provision will be deemed to be written, construed, and enforced as so limited. \n\n"
},{
"id": "Amendment",
"body": "AMENDMENT. This Contract may be modified or amended in writing by mutual agreement between the parties, if the writing is signed by the party obligated under the amendment. \n\n"
},{
"id": "Governing Law",
"body": "GOVERNING LAW. This Contract shall be construed in accordance with the Maxims of Equity. \n\n"
},{
"id": "Notice",
"body": "NOTICE. Any notice or communication required or permitted under this Contract shall be sufficiently given if delivered to a verifiable email address or to such other email address as one party may have publicly furnished in writing, or published on a broadcast contract provided by this blockchain for purposes of providing notices of this type. \n"
},{
"id": "Waiver of Contractual Right",
"body": "WAIVER OF CONTRACTUAL RIGHT. The failure of either party to enforce any provision of this Contract shall not be construed as a waiver or limitation of that party's right to subsequently enforce and compel strict compliance with every provision of this Contract. \n\n"
},{
"id": "Arbitrator's Fees to Prevailing Party",
"body": "ARBITRATOR'S FEES TO PREVAILING PARTY. In any action arising hereunder or any separate action pertaining to the validity of this Agreement, both sides shall pay half the initial cost of arbitration, and the prevailing party shall be awarded reasonable arbitrator's fees and costs.\n \n"
},{
"id": "Construction and Interpretation",
"body": "CONSTRUCTION AND INTERPRETATION. The rule requiring construction or interpretation against the drafter is waived. The document shall be deemed as if it were drafted by both parties in a mutual effort. \n \n"
}
]
}
\ No newline at end of file
#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>
using namespace eosio;
class payloadless : public eosio::contract {
public:
using contract::contract;
void doit() {
print( "Im a payloadless action" );
}
};
EOSIO_ABI( payloadless, (doit) )
# CONTRACT FOR payloadless::doit
## ACTION NAME: doit
### Parameters
Input paramters:
Implied parameters:
### Intent
INTENT. The intention of the author and the invoker of this contract is to print output. It shall have no other effect.
### Term
TERM. This Contract expires at the conclusion of code execution.
### CLAUSE NAME: Warranty
WARRANTY. The invoker of the contract action shall uphold its Obligations under this Contract in a timely and workmanlike manner, using knowledge and recommendations for performing the services which meet generally acceptable standards set forth by EOS.IO Blockchain Block Producers.
### CLAUSE NAME: Default
DEFAULT. The occurrence of any of the following shall constitute a material default under this Contract:
### CLAUSE NAME: Remedies
REMEDIES. In addition to any and all other rights a party may have available according to law, if a party defaults by failing to substantially perform any provision, term or condition of this Contract, the other party may terminate the Contract by providing written notice to the defaulting party. This notice shall describe with sufficient detail the nature of the default. The party receiving such notice shall promptly be removed from being a Block Producer and this Contract shall be automatically terminated.
### CLAUSE NAME: Force Majeure
FORCE MAJEURE. If performance of this Contract or any obligation under this Contract is prevented, restricted, or interfered with by causes beyond either party's reasonable control ("Force Majeure"), and if the party unable to carry out its obligations gives the other party prompt written notice of such event, then the obligations of the party invoking this provision shall be suspended to the extent necessary by such event. The term Force Majeure shall include, without limitation, acts of God, fire, explosion, vandalism, storm or other similar occurrence, orders or acts of military or civil authority, or by national emergencies, insurrections, riots, or wars, or strikes, lock-outs, work stoppages, or supplier failures. The excused party shall use reasonable efforts under the circumstances to avoid or remove such causes of non-performance and shall proceed to perform with reasonable dispatch whenever such causes are removed or ceased. An act or omission shall be deemed within the reasonable control of a party if committed, omitted, or caused by such party, or its employees, officers, agents, or affiliates.
### CLAUSE NAME: Dispute Resolution
DISPUTE RESOLUTION. Any controversies or disputes arising out of or relating to this Contract will be resolved by binding arbitration under the default rules set forth by the EOS.IO Blockchain. The arbitrator's award will be final, and judgment may be entered upon it by any court having proper jurisdiction.
### CLAUSE NAME: Entire Agreement
ENTIRE AGREEMENT. This Contract contains the entire agreement of the parties, and there are no other promises or conditions in any other agreement whether oral or written concerning the subject matter of this Contract. This Contract supersedes any prior written or oral agreements between the parties.
### CLAUSE NAME: Severability
SEVERABILITY. If any provision of this Contract will be held to be invalid or unenforceable for any reason, the remaining provisions will continue to be valid and enforceable. If a court finds that any provision of this Contract is invalid or unenforceable, but that by limiting such provision it would become valid and enforceable, then such provision will be deemed to be written, construed, and enforced as so limited.
### CLAUSE NAME: Amendment
AMENDMENT. This Contract may be modified or amended in writing by mutual agreement between the parties, if the writing is signed by the party obligated under the amendment.
### CLAUSE NAME: Governing Law
GOVERNING LAW. This Contract shall be construed in accordance with the Maxims of Equity.
### CLAUSE NAME: Notice
NOTICE. Any notice or communication required or permitted under this Contract shall be sufficiently given if delivered to a verifiable email address or to such other email address as one party may have publicly furnished in writing, or published on a broadcast contract provided by this blockchain for purposes of providing notices of this type.
### CLAUSE NAME: Waiver of Contractual Right
WAIVER OF CONTRACTUAL RIGHT. The failure of either party to enforce any provision of this Contract shall not be construed as a waiver or limitation of that party's right to subsequently enforce and compel strict compliance with every provision of this Contract.
### CLAUSE NAME: Arbitrator's Fees to Prevailing Party
ARBITRATOR'S FEES TO PREVAILING PARTY. In any action arising hereunder or any separate action pertaining to the validity of this Agreement, both sides shall pay half the initial cost of arbitration, and the prevailing party shall be awarded reasonable arbitrator's fees and costs.
### CLAUSE NAME: Construction and Interpretation
CONSTRUCTION AND INTERPRETATION. The rule requiring construction or interpretation against the drafter is waived. The document shall be deemed as if it were drafted by both parties in a mutual effort.
### CLAUSE NAME: In Witness Whereof
IN WITNESS WHEREOF, the parties hereto have caused this Agreement to be executed by themselves or their duly authorized representatives as of the date of execution, and authorized as proven by the cryptographic signature on the transaction that invokes this contract.
......@@ -173,7 +173,7 @@ void chain_controller::push_block(const signed_block& new_block, uint32_t skip)
} );
});
});
ilog( "\rpush block #${n} from ${pro} ${time} ${id} lib: ${l} success", ("n",new_block.block_num())("pro",name(new_block.producer))("time",new_block.timestamp)("id",new_block.id())("l",last_irreversible_block_num()));
ilog( "push block #${n} from ${pro} ${time} ${id} lib: ${l} success", ("n",new_block.block_num())("pro",name(new_block.producer))("time",new_block.timestamp)("id",new_block.id())("l",last_irreversible_block_num()));
} FC_CAPTURE_AND_RETHROW((new_block)) }
bool chain_controller::_push_block(const signed_block& new_block)
......@@ -723,7 +723,7 @@ void chain_controller::pop_block()
optional<signed_block> head_block = fetch_block_by_id( head_id );
EOS_ASSERT( head_block.valid(), pop_empty_chain, "there are no blocks to pop" );
wlog( "\rpop block #${n} from ${pro} ${time} ${id}", ("n",head_block->block_num())("pro",name(head_block->producer))("time",head_block->timestamp)("id",head_block->id()));
wlog( "pop block #${n} from ${pro} ${time} ${id}", ("n",head_block->block_num())("pro",name(head_block->producer))("time",head_block->timestamp)("id",head_block->id()));
_fork_db.pop_block();
_db.undo();
......
......@@ -135,7 +135,7 @@ namespace fc {
if (my->console_handle != INVALID_HANDLE_VALUE)
SetConsoleTextAttribute(my->console_handle, get_console_color(text_color));
#else
if(isatty(fileno(out))) fprintf( out, "\r%s", get_console_color( text_color ) );
if(isatty(fileno(out))) fprintf( out, "%s", get_console_color( text_color ) );
#endif
if( text.size() )
......@@ -145,7 +145,7 @@ namespace fc {
if (my->console_handle != INVALID_HANDLE_VALUE)
SetConsoleTextAttribute(my->console_handle, CONSOLE_DEFAULT);
#else
if(isatty(fileno(out))) fprintf( out, "\r%s", CONSOLE_DEFAULT );
if(isatty(fileno(out))) fprintf( out, "%s", CONSOLE_DEFAULT );
#endif
if( my->cfg.flush ) fflush( out );
......
/**
* @file
* @copyright defined in eos/LICENSE.txt
*
*/
#pragma once
#ifndef COMMON_HPP
#define COMMON_HPP
namespace eosio { namespace utilities { namespace common {
template<typename I>
std::string itoh(I n, size_t hlen = sizeof(I)<<1) {
static const char* digits = "0123456789abcdef";
std::string r(hlen, '0');
for(size_t i = 0, j = (hlen - 1) * 4 ; i < hlen; ++i, j -= 4)
r[i] = digits[(n>>j) & 0x0f];
return r;
}
}}}
#endif // COMMON_HPP
......@@ -17,6 +17,7 @@
#include <eosio/chain/contracts/eos_contract.hpp>
#include <eosio/utilities/key_conversion.hpp>
#include <eosio/utilities/common.hpp>
#include <eosio/chain/wast_to_wasm.hpp>
#include <fc/io/json.hpp>
......@@ -277,15 +278,8 @@ namespace chain_apis {
const string read_only::KEYi64 = "i64";
read_only::get_info_results read_only::get_info(const read_only::get_info_params&) const {
auto itoh = [](uint32_t n, size_t hlen = sizeof(uint32_t)<<1) {
static const char* digits = "0123456789abcdef";
std::string r(hlen, '0');
for(size_t i = 0, j = (hlen - 1) * 4 ; i < hlen; ++i, j -= 4)
r[i] = digits[(n>>j) & 0x0f];
return r;
};
return {
itoh(static_cast<uint32_t>(app().version())),
eosio::utilities::common::itoh(static_cast<uint32_t>(app().version())),
db.head_block_num(),
db.last_irreversible_block_num(),
db.head_block_id(),
......
......@@ -236,7 +236,7 @@ block_production_condition::block_production_condition_enum producer_plugin_impl
auto producer = db.head_block_producer();
// auto pending = db.pending().size();
wlog("\r${p} generated block ${id}... #${n} @ ${t} with ${count} trxs, lib: ${lib}", ("p", producer)("lib",db.last_irreversible_block_num())(capture) );
wlog("${p} generated block ${id}... #${n} @ ${t} with ${count} trxs, lib: ${lib}", ("p", producer)("lib",db.last_irreversible_block_num())(capture) );
break;
}
case block_production_condition::not_synced:
......
......@@ -2,28 +2,84 @@
This plugin provides a way to generate a given amount of transactions per second against the currency contract. It runs internally to eosd to reduce overhead.
## Setup
This general procedure was used when doing Dawn 3.0 performance testing as mentioned in https://github.com/EOSIO/eos/issues/2078.
Create the two accounts used for transactions and set the currency contract code. Must give account & private key used for creation.
## Performance testing
```
curl --data-binary '["inita", "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"]' http://localhost:7777/v1/txn_test_gen/create_test_accounts
```
The following instructions describe how to use the `txn_test_gen_plugin` plugin to generate 1,000 transaction per second load on a simple EOSIO node.
## Starting/Stopping
### Create config and data directories
Make an empty directory for our configs and data, `mkdir ~/eos.data`, and define a logging.json that doesn't print debug information (which occurs for each txn) to the console:
```bash
cat << EOF > ~/eos.data/logging.json
{
"includes": [],
"appenders": [{
"name": "consoleout",
"type": "console",
"args": {
"stream": "std_out",
"level_colors": [{
"level": "debug",
"color": "green"
},{
"level": "warn",
"color": "brown"
},{
"level": "error",
"color": "red"
}
]
},
"enabled": true
}
],
Start generation:
"loggers": [{
"name": "default",
"level": "info",
"enabled": true,
"additivity": false,
"appenders": [
"consoleout"
]
}
]
}
EOF
```
### Launch producer
```bash
$ ./nodeos -d ~/eos.data/producer_node --config-dir ~/eos.data/producer_node -l ~/eos.data/logging.json --http-server-address "" -p eosio -e
```
curl --data-binary '["one", 20, 16]' http://localhost:7777/v1/txn_test_gen/start_generation
### Launch non-producer that will generate transactions
```bash
$ ./nodeos -d ~/eos.data/generator_node --config-dir ~/eos.data/generator_node -l ~/eos.data/logging.json --plugin eosio::txn_test_gen_plugin --plugin eosio::wallet_api_plugin --plugin eosio::chain_api_plugin --p2p-peer-address localhost:9876 --p2p-listen-endpoint localhost:5555
```
First parameter is some "salt" added to the transaction memos to ensure if this test is being run on other nodes concurrently no identical transactions are created. The second parameter is the frequency in milliseconds to wake up and send transactions. The third parameter is how many transactions to send on each wake up (must be an even number). There are some constraints on these numbers to prevent anything too wild.
### Create a wallet on the non-producer and set bios contract
```bash
$ ./cleos wallet create
$ ./cleos set contract eosio ~/eos/build.release/contracts/eosio.bios/
```
Stop generation
### Initialize the accounts txn_test_gen_plugin uses
```bash
$ curl --data-binary '["eosio", "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"]' http://localhost:8888/v1/txn_test_gen/create_test_accounts
```
### Start transaction generation, this will submit 20 transactions evey 20ms (total of 1000TPS)
```bash
$ curl --data-binary '["", 20, 20]' http://localhost:8888/v1/txn_test_gen/start_generation
```
curl http://localhost:7777/v1/txn_test_gen/stop_generation
### Note the producer console prints
```bash
eosio generated block 9b8b851d... #3219 @ 2018-04-25T16:07:47.000 with 500 trxs, lib: 3218
eosio generated block e5b3cd5d... #3220 @ 2018-04-25T16:07:47.500 with 500 trxs, lib: 3219
eosio generated block b243aeaa... #3221 @ 2018-04-25T16:07:48.000 with 500 trxs, lib: 3220
```
Generation of transactions is also stopped if a transaction fails to be accepted for any reason.
\ No newline at end of file
Note in the console output there are 500 transactions in each of the blocks which are produced every 500 ms yielding 1,000 transactions / second.
......@@ -1190,7 +1190,7 @@ using App_p = std::unique_ptr<App>;
/** To use, create a new `Program()` instance with `argc`, `argv`, and a help description. The templated
* add_option methods make it easy to prepare options. Remember to call `.start` before starting your
* program, so that the options can be evaluated and the help option doesn't accidentally run your program. */
class App {
class App final {
friend Option;
friend detail::AppFriend;
......
......@@ -12,7 +12,9 @@
#include <istream>
#include <ostream>
#include <string>
#include <regex>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <fc/variant.hpp>
#include <fc/io/json.hpp>
#include <eosio/chain/exceptions.hpp>
......@@ -22,132 +24,155 @@
using boost::asio::ip::tcp;
namespace eosio { namespace client { namespace http {
fc::variant call( const std::string& server, uint16_t port,
const std::string& path,
const fc::variant& postdata ) {
try {
std::string postjson;
if( !postdata.is_null() )
postjson = fc::json::to_string( postdata );
void do_connect(tcp::socket& sock, const std::string& server, const std::string& port) {
// Get a list of endpoints corresponding to the server name.
tcp::resolver resolver(sock.get_io_service());
tcp::resolver::query query(server, port);
boost::asio::connect(sock, resolver.resolve(query));
}
boost::asio::io_service io_service;
template<class T>
std::string do_txrx(T& socket, boost::asio::streambuf& request_buff, unsigned int& status_code) {
// Send the request.
boost::asio::write(socket, request_buff);
// Read the response status line. The response streambuf will automatically
// grow to accommodate the entire line. The growth may be limited by passing
// a maximum size to the streambuf constructor.
boost::asio::streambuf response;
boost::asio::read_until(socket, response, "\r\n");
// Check that response is OK.
std::istream response_stream(&response);
std::string http_version;
response_stream >> http_version;
response_stream >> status_code;
std::string status_message;
std::getline(response_stream, status_message);
FC_ASSERT( !(!response_stream || http_version.substr(0, 5) != "HTTP/"), "Invalid Response" );
// Read the response headers, which are terminated by a blank line.
boost::asio::read_until(socket, response, "\r\n\r\n");
// Process the response headers.
std::string header;
int response_content_length = -1;
std::regex clregex(R"xx(^content-length:\s+(\d+))xx", std::regex_constants::icase);
while (std::getline(response_stream, header) && header != "\r") {
std::smatch match;
if(std::regex_search(header, match, clregex))
response_content_length = std::stoi(match[1]);
}
FC_ASSERT(response_content_length >= 0, "Invalid content-length response");
// Get a list of endpoints corresponding to the server name.
tcp::resolver resolver(io_service);
tcp::resolver::query query(server, std::to_string(port) );
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;
while( endpoint_iterator != end ) {
// Try each endpoint until we successfully establish a connection.
tcp::socket socket(io_service);
try {
boost::asio::connect(socket, endpoint_iterator);
endpoint_iterator = end;
} catch( std::exception& e ) {
++endpoint_iterator;
if( endpoint_iterator != end ) {
continue;
} else {
throw connection_exception(fc::log_messages{
FC_LOG_MESSAGE( error, "Connection to ${server}:${port}${path} is refused",
("server", server)("port", port)("path", path) ),
FC_LOG_MESSAGE( error, e.what())
});
}
}
// Form the request. We specify the "Connection: close" header so that the
// server will close the socket after transmitting the response. This will
// allow us to treat all data up until the EOF as the content.
boost::asio::streambuf request;
std::ostream request_stream(&request);
request_stream << "POST " << path << " HTTP/1.0\r\n";
request_stream << "Host: " << server << "\r\n";
request_stream << "content-length: " << postjson.size() << "\r\n";
request_stream << "Accept: */*\r\n";
request_stream << "Connection: close\r\n\r\n";
request_stream << postjson;
// Send the request.
boost::asio::write(socket, request);
// Read the response status line. The response streambuf will automatically
// grow to accommodate the entire line. The growth may be limited by passing
// a maximum size to the streambuf constructor.
boost::asio::streambuf response;
boost::asio::read_until(socket, response, "\r\n");
// Check that response is OK.
std::istream response_stream(&response);
std::string http_version;
response_stream >> http_version;
unsigned int status_code;
response_stream >> status_code;
std::string status_message;
std::getline(response_stream, status_message);
FC_ASSERT( !(!response_stream || http_version.substr(0, 5) != "HTTP/"), "Invalid Response" );
// Read the response headers, which are terminated by a blank line.
boost::asio::read_until(socket, response, "\r\n\r\n");
// Process the response headers.
std::string header;
while (std::getline(response_stream, header) && header != "\r")
{
// std::cout << header << "\n";
}
// std::cout << "\n";
std::stringstream re;
// Write whatever content we already have to output.
if (response.size() > 0)
// std::cout << &response;
re << &response;
// Read until EOF, writing data to output as we go.
boost::system::error_code error;
while (boost::asio::read(socket, response,
boost::asio::transfer_at_least(1), error))
re << &response;
if (error != boost::asio::error::eof)
throw boost::system::system_error(error);
// std::cout << re.str() <<"\n";
const auto response_result = fc::json::from_string(re.str());
if( status_code == 200 || status_code == 201 || status_code == 202 ) {
return response_result;
} else if( status_code == 404 ) {
// Unknown endpoint
if (path.compare(0, chain_func_base.size(), chain_func_base) == 0) {
throw chain::missing_chain_api_plugin_exception(FC_LOG_MESSAGE(error, "Chain API plugin is not enabled"));
} else if (path.compare(0, wallet_func_base.size(), wallet_func_base) == 0) {
throw chain::missing_wallet_api_plugin_exception(FC_LOG_MESSAGE(error, "Wallet is not available"));
} else if (path.compare(0, account_history_func_base.size(), account_history_func_base) == 0) {
throw chain::missing_account_history_api_plugin_exception(FC_LOG_MESSAGE(error, "Account History API plugin is not enabled"));
} else if (path.compare(0, net_func_base.size(), net_func_base) == 0) {
throw chain::missing_net_api_plugin_exception(FC_LOG_MESSAGE(error, "Net API plugin is not enabled"));
}
} else {
auto &&error_info = response_result.as<eosio::error_results>().error;
// Construct fc exception from error
const auto &error_details = error_info.details;
fc::log_messages logs;
for (auto itr = error_details.begin(); itr != error_details.end(); itr++) {
const auto& context = fc::log_context(fc::log_level::error, itr->file.data(), itr->line_number, itr->method.data());
logs.emplace_back(fc::log_message(context, itr->message));
}
throw fc::exception(logs, error_info.code, error_info.name, error_info.what);
}
FC_ASSERT( status_code == 200, "Error code ${c}\n: ${msg}\n", ("c", status_code)("msg", re.str()) );
std::stringstream re;
// Write whatever content we already have to output.
response_content_length -= response.size();
if (response.size() > 0)
re << &response;
boost::asio::read(socket, response, boost::asio::transfer_exactly(response_content_length));
re << &response;
return re.str();
}
fc::variant call( const std::string& server_url,
const std::string& path,
const fc::variant& postdata ) {
std::string postjson;
if( !postdata.is_null() )
postjson = fc::json::to_string( postdata );
boost::asio::io_service io_service;
string scheme, server, port, path_prefix;
//via rfc3986 and modified a bit to suck out the port number
//Sadly this doesn't work for ipv6 addresses
std::regex rgx(R"xx(^(([^:/?#]+):)?(//([^:/?#]*)(:(\d+))?)?([^?#]*)(\?([^#]*))?(#(.*))?)xx");
std::smatch match;
if(std::regex_search(server_url.begin(), server_url.end(), match, rgx)) {
scheme = match[2];
server = match[4];
port = match[6];
path_prefix = match[7];
}
if(scheme != "http" && scheme != "https")
FC_THROW("Unrecognized URL scheme (${s}) in URL \"${u}\"", ("s", scheme)("u", server_url));
if(server.empty())
FC_THROW("No server parsed from URL \"${u}\"", ("u", server_url));
if(port.empty())
port = scheme == "http" ? "8888" : "443";
boost::trim_right_if(path_prefix, boost::is_any_of("/"));
boost::asio::streambuf request;
std::ostream request_stream(&request);
request_stream << "POST " << path_prefix + path << " HTTP/1.0\r\n";
request_stream << "Host: " << server << "\r\n";
request_stream << "content-length: " << postjson.size() << "\r\n";
request_stream << "Accept: */*\r\n";
request_stream << "Connection: close\r\n\r\n";
request_stream << postjson;
unsigned int status_code;
std::string re;
if(scheme == "http") {
tcp::socket socket(io_service);
do_connect(socket, server, port);
re = do_txrx(socket, request, status_code);
}
else { //https
boost::asio::ssl::context ssl_context(boost::asio::ssl::context::sslv23_client);
#if defined( __APPLE__ )
//TODO: this is undocumented/not supported; fix with keychain based approach
ssl_context.load_verify_file("/private/etc/ssl/cert.pem");
#elif defined( _WIN32 )
FC_THROW("HTTPS on Windows not supported");
#else
ssl_context.set_default_verify_paths();
#endif
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket(io_service, ssl_context);
socket.set_verify_mode(boost::asio::ssl::verify_peer);
do_connect(socket.next_layer(), server, port);
socket.handshake(boost::asio::ssl::stream_base::client);
re = do_txrx(socket, request, status_code);
//try and do a clean shutdown; but swallow if this fails (other side could have already gave TCP the ax)
try {socket.shutdown();} catch(...) {}
}
const auto response_result = fc::json::from_string(re);
if( status_code == 200 || status_code == 201 || status_code == 202 ) {
return response_result;
} else if( status_code == 404 ) {
// Unknown endpoint
if (path.compare(0, chain_func_base.size(), chain_func_base) == 0) {
throw chain::missing_chain_api_plugin_exception(FC_LOG_MESSAGE(error, "Chain API plugin is not enabled"));
} else if (path.compare(0, wallet_func_base.size(), wallet_func_base) == 0) {
throw chain::missing_wallet_api_plugin_exception(FC_LOG_MESSAGE(error, "Wallet is not available"));
} else if (path.compare(0, account_history_func_base.size(), account_history_func_base) == 0) {
throw chain::missing_account_history_api_plugin_exception(FC_LOG_MESSAGE(error, "Account History API plugin is not enabled"));
} else if (path.compare(0, net_func_base.size(), net_func_base) == 0) {
throw chain::missing_net_api_plugin_exception(FC_LOG_MESSAGE(error, "Net API plugin is not enabled"));
}
} else {
auto &&error_info = response_result.as<eosio::error_results>().error;
// Construct fc exception from error
const auto &error_details = error_info.details;
fc::log_messages logs;
for (auto itr = error_details.begin(); itr != error_details.end(); itr++) {
const auto& context = fc::log_context(fc::log_level::error, itr->file.data(), itr->line_number, itr->method.data());
logs.emplace_back(fc::log_message(context, itr->message));
}
FC_ASSERT( !"unable to connect" );
} FC_CAPTURE_AND_RETHROW() // error, "Request Path: ${server}:${port}${path}\nRequest Post Data: ${postdata}" ,
// ("server", server)("port", port)("path", path)("postdata", postdata) )
throw fc::exception(logs, error_info.code, error_info.name, error_info.what);
}
FC_ASSERT( status_code == 200, "Error code ${c}\n: ${msg}\n", ("c", status_code)("msg", re) );
return response_result;
}
}}}
......@@ -5,7 +5,7 @@
#pragma once
namespace eosio { namespace client { namespace http {
fc::variant call( const std::string& server, uint16_t port,
fc::variant call( const std::string& server_url,
const std::string& path,
const fc::variant& postdata = fc::variant() );
......
......@@ -22,11 +22,10 @@ Usage: programs/cleos/cleos [OPTIONS] SUBCOMMAND
Options:
-h,--help Print this help message and exit
-H,--host TEXT=localhost the host where nodeos is running
-p,--port UINT=8888 the port where nodeos is running
--wallet-host TEXT=localhost
the host where keosd is running
--wallet-port UINT=8888 the port where keosd is running
-u,--url TEXT=http://localhost:8888/
the http/https URL where nodeos is running
--wallet-url TEXT=http://localhost:8888/
the http/https URL where keosd is running
-v,--verbose output verbose actions on error
Subcommands:
......@@ -39,7 +38,8 @@ Subcommands:
wallet Interact with local wallet
sign Sign a transaction
push Push arbitrary transactions to the blockchain
multisig Multisig contract commands
```
To get help with any particular subcommand, run it with no arguments as well:
```
......@@ -135,13 +135,8 @@ FC_DECLARE_EXCEPTION( localized_exception, 10000000, "an error occured" );
FC_MULTILINE_MACRO_END \
)
string program = "eosc";
string host = "localhost";
uint32_t port = 8888;
// restricting use of wallet to localhost
string wallet_host = "localhost";
uint32_t wallet_port = 8888;
string url = "http://localhost:8888/";
string wallet_url = "http://localhost:8888/";
auto tx_expiration = fc::seconds(30);
string tx_ref_block_num_or_id;
......@@ -195,16 +190,27 @@ vector<chain::permission_level> get_account_permissions(const vector<string>& pe
}
template<typename T>
fc::variant call( const std::string& server, uint16_t port,
fc::variant call( const std::string& url,
const std::string& path,
const T& v ) { return eosio::client::http::call( server, port, path, fc::variant(v) ); }
const T& v ) {
try {
return eosio::client::http::call( url, path, fc::variant(v) );
}
catch(boost::system::system_error& e) {
if(url == ::url)
std::cerr << localized("Failed to connect to nodeos at ${u}; is nodeos running?", ("u", url)) << std::endl;
else if(url == ::wallet_url)
std::cerr << localized("Failed to connect to keosd at ${u}; is keosd running?", ("u", url)) << std::endl;
throw connection_exception(fc::log_messages{FC_LOG_MESSAGE(error, e.what())});
}
}
template<typename T>
fc::variant call( const std::string& path,
const T& v ) { return eosio::client::http::call( host, port, path, fc::variant(v) ); }
const T& v ) { return ::call( url, path, fc::variant(v) ); }
eosio::chain_apis::read_only::get_info_results get_info() {
return call(host, port, get_info_func ).as<eosio::chain_apis::read_only::get_info_results>();
return ::call(url, get_info_func, fc::variant()).as<eosio::chain_apis::read_only::get_info_results>();
}
string generate_nonce_value() {
......@@ -229,18 +235,18 @@ chain::action generate_nonce() {
fc::variant determine_required_keys(const signed_transaction& trx) {
// TODO better error checking
//wdump((trx));
const auto& public_keys = call(wallet_host, wallet_port, wallet_public_keys);
const auto& public_keys = call(wallet_url, wallet_public_keys);
auto get_arg = fc::mutable_variant_object
("transaction", (transaction)trx)
("available_keys", public_keys);
const auto& required_keys = call(host, port, get_required_keys, get_arg);
const auto& required_keys = call(get_required_keys, get_arg);
return required_keys["required_keys"];
}
void sign_transaction(signed_transaction& trx, fc::variant& required_keys) {
// TODO determine chain id
fc::variants sign_args = {fc::variant(trx), required_keys, fc::variant(chain_id_type{})};
const auto& signed_trx = call(wallet_host, wallet_port, wallet_sign_trx, sign_args);
const auto& signed_trx = call(wallet_url, wallet_sign_trx, sign_args);
trx = signed_trx.as<signed_transaction>();
}
......@@ -582,6 +588,14 @@ struct set_action_permission_subcommand {
}
};
CLI::callback_t old_host_port = [](CLI::results_t) {
std::cerr << localized("Host and port options (-H, --wallet-host, etc.) have been replaced with -u/--url and --wallet-url\n"
"Use for example -u http://localhost:8888 or --url https://example.invalid/\n");
exit(1);
return false;
};
struct register_producer_subcommand {
string producer_str;
string producer_key_str;
......@@ -845,10 +859,13 @@ int main( int argc, char** argv ) {
CLI::App app{"Command Line Interface to EOSIO Client"};
app.require_subcommand();
app.add_option( "-H,--host", host, localized("the host where nodeos is running"), true );
app.add_option( "-p,--port", port, localized("the port where nodeos is running"), true );
app.add_option( "--wallet-host", wallet_host, localized("the host where keosd is running"), true );
app.add_option( "--wallet-port", wallet_port, localized("the port where keosd is running"), true );
app.add_option( "-H,--host", old_host_port, localized("the host where nodeos is running") )->group("hidden");
app.add_option( "-p,--port", old_host_port, localized("the port where nodeos is running") )->group("hidden");
app.add_option( "--wallet-host", old_host_port, localized("the host where keosd is running") )->group("hidden");
app.add_option( "--wallet-port", old_host_port, localized("the port where keosd is running") )->group("hidden");
app.add_option( "-u,--url", url, localized("the http/https URL where nodeos is running"), true );
app.add_option( "--wallet-url", wallet_url, localized("the http/https URL where keosd is running"), true );
bool verbose_errors = false;
app.add_flag( "-v,--verbose", verbose_errors, localized("output verbose actions on error"));
......@@ -1228,27 +1245,27 @@ int main( int argc, char** argv ) {
auto connect = net->add_subcommand("connect", localized("start a new connection to a peer"), false);
connect->add_option("host", new_host, localized("The hostname:port to connect to."))->required();
connect->set_callback([&] {
const auto& v = call(host, port, net_connect, new_host);
const auto& v = call(net_connect, new_host);
std::cout << fc::json::to_pretty_string(v) << std::endl;
});
auto disconnect = net->add_subcommand("disconnect", localized("close an existing connection"), false);
disconnect->add_option("host", new_host, localized("The hostname:port to disconnect from."))->required();
disconnect->set_callback([&] {
const auto& v = call(host, port, net_disconnect, new_host);
const auto& v = call(net_disconnect, new_host);
std::cout << fc::json::to_pretty_string(v) << std::endl;
});
auto status = net->add_subcommand("status", localized("status of existing connection"), false);
status->add_option("host", new_host, localized("The hostname:port to query status of connection"))->required();
status->set_callback([&] {
const auto& v = call(host, port, net_status, new_host);
const auto& v = call(net_status, new_host);
std::cout << fc::json::to_pretty_string(v) << std::endl;
});
auto connections = net->add_subcommand("peers", localized("status of all existing peers"), false);
connections->set_callback([&] {
const auto& v = call(host, port, net_connections, new_host);
const auto& v = call(net_connections, new_host);
std::cout << fc::json::to_pretty_string(v) << std::endl;
});
......@@ -1262,7 +1279,7 @@ int main( int argc, char** argv ) {
auto createWallet = wallet->add_subcommand("create", localized("Create a new wallet locally"), false);
createWallet->add_option("-n,--name", wallet_name, localized("The name of the new wallet"), true);
createWallet->set_callback([&wallet_name] {
const auto& v = call(wallet_host, wallet_port, wallet_create, wallet_name);
const auto& v = call(wallet_url, wallet_create, wallet_name);
std::cout << localized("Creating wallet: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
std::cout << localized("Save password to use in the future to unlock this wallet.") << std::endl;
std::cout << localized("Without password imported keys will not be retrievable.") << std::endl;
......@@ -1273,8 +1290,7 @@ int main( int argc, char** argv ) {
auto openWallet = wallet->add_subcommand("open", localized("Open an existing wallet"), false);
openWallet->add_option("-n,--name", wallet_name, localized("The name of the wallet to open"));
openWallet->set_callback([&wallet_name] {
/*const auto& v = */call(wallet_host, wallet_port, wallet_open, wallet_name);
//std::cout << fc::json::to_pretty_string(v) << std::endl;
call(wallet_url, wallet_open, wallet_name);
std::cout << localized("Opened: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
});
......@@ -1282,17 +1298,14 @@ int main( int argc, char** argv ) {
auto lockWallet = wallet->add_subcommand("lock", localized("Lock wallet"), false);
lockWallet->add_option("-n,--name", wallet_name, localized("The name of the wallet to lock"));
lockWallet->set_callback([&wallet_name] {
/*const auto& v = */call(wallet_host, wallet_port, wallet_lock, wallet_name);
call(wallet_url, wallet_lock, wallet_name);
std::cout << localized("Locked: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
//std::cout << fc::json::to_pretty_string(v) << std::endl;
});
// lock all wallets
auto locakAllWallets = wallet->add_subcommand("lock_all", localized("Lock all unlocked wallets"), false);
locakAllWallets->set_callback([] {
/*const auto& v = */call(wallet_host, wallet_port, wallet_lock_all);
//std::cout << fc::json::to_pretty_string(v) << std::endl;
call(wallet_url, wallet_lock_all);
std::cout << localized("Locked All Wallets") << std::endl;
});
......@@ -1311,9 +1324,8 @@ int main( int argc, char** argv ) {
fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_pw)};
/*const auto& v = */call(wallet_host, wallet_port, wallet_unlock, vs);
call(wallet_url, wallet_unlock, vs);
std::cout << localized("Unlocked: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
//std::cout << fc::json::to_pretty_string(v) << std::endl;
});
// import keys into wallet
......@@ -1331,23 +1343,22 @@ int main( int argc, char** argv ) {
public_key_type pubkey = wallet_key.get_public_key();
fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_key)};
const auto& v = call(wallet_host, wallet_port, wallet_import_key, vs);
call(wallet_url, wallet_import_key, vs);
std::cout << localized("imported private key for: ${pubkey}", ("pubkey", std::string(pubkey))) << std::endl;
//std::cout << fc::json::to_pretty_string(v) << std::endl;
});
// list wallets
auto listWallet = wallet->add_subcommand("list", localized("List opened wallets, * = unlocked"), false);
listWallet->set_callback([] {
std::cout << localized("Wallets:") << std::endl;
const auto& v = call(wallet_host, wallet_port, wallet_list);
const auto& v = call(wallet_url, wallet_list);
std::cout << fc::json::to_pretty_string(v) << std::endl;
});
// list keys
auto listKeys = wallet->add_subcommand("keys", localized("List of private keys from all unlocked wallets in wif format."), false);
listKeys->set_callback([] {
const auto& v = call(wallet_host, wallet_port, wallet_list_keys);
const auto& v = call(wallet_url, wallet_list_keys);
std::cout << fc::json::to_pretty_string(v) << std::endl;
});
......@@ -1741,18 +1752,9 @@ int main( int argc, char** argv ) {
} catch (const explained_exception& e) {
return 1;
} catch (connection_exception& e) {
auto errorString = e.to_detail_string();
if (errorString.find(fc::json::to_string(port)) != string::npos) {
std::cerr << localized("Failed to connect to nodeos at ${ip}:${port}; is nodeos running?", ("ip", host)("port", port)) << std::endl;
} else if (errorString.find(fc::json::to_string(wallet_port)) != string::npos) {
std::cerr << localized("Failed to connect to keosd at ${ip}:${port}; is keosd running?", ("ip", wallet_host)("port", wallet_port)) << std::endl;
} else {
std::cerr << localized("Failed to connect") << std::endl;
}
if (verbose_errors) {
elog("connect error: ${e}", ("e", errorString));
}
if (verbose_errors) {
elog("connect error: ${e}", ("e", e.to_detail_string()));
}
} catch (const fc::exception& e) {
// attempt to extract the error code if one is present
if (!print_recognized_errors(e, verbose_errors)) {
......
......@@ -11,15 +11,6 @@
namespace eosio { namespace nodeos { namespace config {
constexpr uint64_t version = 0x${nodeos_BUILD_VERSION};
template<typename I>
std::string itoh(I n, size_t hlen = sizeof(I)<<1) {
static const char* digits = "0123456789abcdef";
std::string r(hlen, '0');
for(size_t i = 0, j = (hlen - 1) * 4 ; i < hlen; ++i, j -= 4)
r[i] = digits[(n>>j) & 0x0f];
return r;
}
}}}
#endif // CONFIG_HPP_IN
......@@ -8,6 +8,7 @@
#include <eosio/http_plugin/http_plugin.hpp>
#include <eosio/net_plugin/net_plugin.hpp>
#include <eosio/producer_plugin/producer_plugin.hpp>
#include <eosio/utilities/common.hpp>
#include <fc/log/logger_config.hpp>
#include <fc/log/appender.hpp>
......@@ -87,7 +88,7 @@ int main(int argc, char** argv)
if(!app().initialize<chain_plugin, http_plugin, net_plugin, producer_plugin>(argc, argv))
return -1;
initialize_logging();
ilog("nodeos version ${ver}", ("ver", eosio::nodeos::config::itoh(static_cast<uint32_t>(app().version()))));
ilog("nodeos version ${ver}", ("ver", eosio::utilities::common::itoh(static_cast<uint32_t>(app().version()))));
ilog("eosio root is ${root}", ("root", root.string()));
app().startup();
app().exec();
......
......@@ -5,6 +5,10 @@
#include <eosio/chain/resource_limits.hpp>
#include <eosio/chain/resource_limits_private.hpp>
#include <eosio/testing/tester_network.hpp>
#include <eosio/chain/producer_object.hpp>
#include <eosio.system/eosio.system.wast.hpp>
#include <eosio.system/eosio.system.abi.hpp>
#ifdef NON_VALIDATING_TEST
#define TESTER tester
#else
......@@ -437,4 +441,50 @@ try {
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_CASE( linkauth_special ) { try {
TESTER chain;
const auto& tester_account = N(tester);
std::vector<transaction_id_type> ids;
chain.set_code(config::system_account_name, eosio_system_wast);
chain.set_abi(config::system_account_name, eosio_system_abi);
chain.produce_blocks();
chain.create_account(N(currency));
chain.produce_blocks();
chain.create_account(N(tester));
chain.create_account(N(tester2));
chain.produce_blocks();
chain.push_action(config::system_account_name, contracts::updateauth::get_name(), tester_account, fc::mutable_variant_object()
("account", "tester")
("permission", "first")
("parent", "active")
("data", authority(chain.get_public_key(tester_account, "first")))
("delay", 5));
auto validate_disallow = [&] (const char *type) {
BOOST_REQUIRE_EXCEPTION(
chain.push_action(config::system_account_name, contracts::linkauth::get_name(), tester_account, fc::mutable_variant_object()
("account", "tester")
("code", "eosio")
("type", type)
("requirement", "first")),
action_validate_exception,
[] (const action_validate_exception &ex)->bool {
BOOST_REQUIRE_EQUAL(std::string("message validation exception"), ex.what());
return true;
});
};
validate_disallow("linkauth");
validate_disallow("unlinkauth");
validate_disallow("deleteauth");
validate_disallow("updateauth");
validate_disallow("canceldelay");
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_SUITE_END()
......@@ -1154,6 +1154,16 @@ BOOST_AUTO_TEST_CASE(get_required_keys)
#if 0
BOOST_AUTO_TEST_CASE(transaction_mroot)
{ try {
// Utility function get on_block tx_mroot given the block_num
auto get_on_block_tx_mroot = [](uint32_t block_num) -> digest_type {
tester temp;
temp.produce_blocks(block_num - 1);
signed_block blk = temp.produce_block();
// Since the block is empty, its transaction_mroot should represent the onblock tx
return blk.transaction_mroot;
};
validating_tester chain;
// Finalize current block (which has set contract transaction for eosio)
chain.produce_block();
......@@ -1177,13 +1187,13 @@ BOOST_AUTO_TEST_CASE(transaction_mroot)
}
auto expected_shard_tx_root = merkle(tx_roots);
// Hardcoded on_block tx_root, since there's no easy way to calculate the tx_root with current interface
auto on_block_tx_root = digest_type("aa63d366cc2ef41746bb150258d1c0662c8133469f785425cb996dbe2a227086");
// Compare with head block tx mroot
chain.produce_block();
auto on_block_tx_root = get_on_block_tx_mroot(chain.control->head_block_num());
// There is only 1 region, 2 cycle, 1 shard in first cycle, 1 shard in second cycle
auto expected_tx_mroot = merkle({on_block_tx_root, expected_shard_tx_root});
// Compare with head block tx mroot
chain.produce_block();
auto head_block_tx_mroot = chain.control->head_block_header().transaction_mroot;
BOOST_TEST(expected_tx_mroot.str() == head_block_tx_mroot.str());
......
......@@ -166,6 +166,153 @@ BOOST_AUTO_TEST_CASE( link_delay_direct_test ) { try {
} FC_LOG_AND_RETHROW() }/// schedule_test
BOOST_AUTO_TEST_CASE(delete_auth_test) { try {
TESTER chain;
const auto& tester_account = N(tester);
chain.set_code(config::system_account_name, eosio_system_wast);
chain.set_abi(config::system_account_name, eosio_system_abi);
chain.produce_blocks();
chain.create_account(N(eosio.token));
chain.produce_blocks(10);
chain.set_code(N(eosio.token), eosio_token_wast);
chain.set_abi(N(eosio.token), eosio_token_abi);
chain.produce_blocks();
chain.create_account(N(tester));
chain.create_account(N(tester2));
chain.produce_blocks(10);
transaction_trace trace;
// can't delete auth because it doesn't exist
BOOST_REQUIRE_EXCEPTION(
trace = chain.push_action(config::system_account_name, contracts::deleteauth::get_name(), tester_account, fc::mutable_variant_object()
("account", "tester")
("permission", "first")),
permission_query_exception,
[] (const permission_query_exception &e)->bool {
std::string check_str = "3010001 permission_query_exception: Permission Query Exception\nFailed to retrieve permission";
BOOST_REQUIRE_EQUAL(check_str, e.to_detail_string().substr(0, check_str.length()));
return true;
});
// update auth
chain.push_action(config::system_account_name, contracts::updateauth::get_name(), tester_account, fc::mutable_variant_object()
("account", "tester")
("permission", "first")
("parent", "active")
("data", authority(chain.get_public_key(tester_account, "first")))
("delay", 0));
// link auth
chain.push_action(config::system_account_name, contracts::linkauth::get_name(), tester_account, fc::mutable_variant_object()
("account", "tester")
("code", "eosio.token")
("type", "transfer")
("requirement", "first"));
// create CUR token
chain.produce_blocks();
chain.push_action(N(eosio.token), N(create), N(eosio.token), mutable_variant_object()
("issuer", "eosio.token" )
("maximum_supply", "9000000.0000 CUR" )
("can_freeze", 0)
("can_recall", 0)
("can_whitelist", 0)
);
// issue to account "eosio.token"
chain.push_action(N(eosio.token), name("issue"), N(eosio.token), fc::mutable_variant_object()
("to", "eosio.token")
("quantity", "1000000.0000 CUR")
("memo", "for stuff")
);
// transfer from eosio.token to tester
trace = chain.push_action(N(eosio.token), name("transfer"), N(eosio.token), fc::mutable_variant_object()
("from", "eosio.token")
("to", "tester")
("quantity", "100.0000 CUR")
("memo", "hi" )
);
BOOST_REQUIRE_EQUAL(transaction_receipt::executed, trace.status);
BOOST_REQUIRE_EQUAL(0, trace.deferred_transaction_requests.size());
chain.produce_blocks();
auto liquid_balance = get_currency_balance(chain, N(eosio.token));
BOOST_REQUIRE_EQUAL(asset::from_string("999900.0000 CUR"), liquid_balance);
liquid_balance = get_currency_balance(chain, N(tester));
BOOST_REQUIRE_EQUAL(asset::from_string("100.0000 CUR"), liquid_balance);
trace = chain.push_action(N(eosio.token), name("transfer"), N(tester), fc::mutable_variant_object()
("from", "tester")
("to", "tester2")
("quantity", "1.0000 CUR")
("memo", "hi" )
);
BOOST_REQUIRE_EQUAL(transaction_receipt::executed, trace.status);
liquid_balance = get_currency_balance(chain, N(eosio.token));
BOOST_REQUIRE_EQUAL(asset::from_string("999900.0000 CUR"), liquid_balance);
liquid_balance = get_currency_balance(chain, N(tester));
BOOST_REQUIRE_EQUAL(asset::from_string("99.0000 CUR"), liquid_balance);
liquid_balance = get_currency_balance(chain, N(tester2));
BOOST_REQUIRE_EQUAL(asset::from_string("1.0000 CUR"), liquid_balance);
// can't delete auth because it's linked
BOOST_REQUIRE_EXCEPTION(
trace = chain.push_action(config::system_account_name, contracts::deleteauth::get_name(), tester_account, fc::mutable_variant_object()
("account", "tester")
("permission", "first")),
action_validate_exception,
[] (const action_validate_exception &e)->bool {
std::string check_str = "3040000 action_validate_exception: message validation exception\nCannot delete a linked authority";
BOOST_REQUIRE_EQUAL(check_str, e.to_detail_string().substr(0, check_str.length()));
return true;
});
// unlink auth
trace = chain.push_action(config::system_account_name, contracts::unlinkauth::get_name(), tester_account, fc::mutable_variant_object()
("account", "tester")
("code", "eosio.token")
("type", "transfer"));
BOOST_REQUIRE_EQUAL(transaction_receipt::executed, trace.status);
// delete auth
trace = chain.push_action(config::system_account_name, contracts::deleteauth::get_name(), tester_account, fc::mutable_variant_object()
("account", "tester")
("permission", "first"));
BOOST_REQUIRE_EQUAL(transaction_receipt::executed, trace.status);
chain.produce_blocks(1);;
trace = chain.push_action(N(eosio.token), name("transfer"), N(tester), fc::mutable_variant_object()
("from", "tester")
("to", "tester2")
("quantity", "3.0000 CUR")
("memo", "hi" )
);
BOOST_REQUIRE_EQUAL(transaction_receipt::executed, trace.status);
BOOST_REQUIRE_EQUAL(0, trace.deferred_transaction_requests.size());
chain.produce_blocks();
liquid_balance = get_currency_balance(chain, N(tester));
BOOST_REQUIRE_EQUAL(asset::from_string("96.0000 CUR"), liquid_balance);
liquid_balance = get_currency_balance(chain, N(tester2));
BOOST_REQUIRE_EQUAL(asset::from_string("4.0000 CUR"), liquid_balance);
} FC_LOG_AND_RETHROW() }/// delete_auth_test
// test link to permission with delay on permission which is parent of min permission (special logic in permission_object::satisfies)
BOOST_AUTO_TEST_CASE( link_delay_direct_parent_permission_test ) { try {
TESTER chain;
......
......@@ -158,7 +158,7 @@ class Node(object):
self.mongoHost=mongoHost
self.mongoPort=mongoPort
self.mongoDb=mongoDb
self.endpointArgs="--host %s --port %d" % (self.host, self.port)
self.endpointArgs="--url http://%s:%d" % (self.host, self.port)
self.mongoEndpointArgs=""
if self.enableMongo:
self.mongoEndpointArgs += "--host %s --port %d %s" % (mongoHost, mongoPort, mongoDb)
......@@ -274,7 +274,7 @@ class Node(object):
return arr.decode("utf-8")
def setWalletEndpointArgs(self, args):
self.endpointArgs="--host %s --port %d %s" % (self.host, self.port, args)
self.endpointArgs="--url http://%s:%d %s" % (self.host, self.port, args)
def getBlock(self, blockNum, retry=True, silentErrors=False):
if not self.enableMongo:
......@@ -986,10 +986,10 @@ class WalletMgr(object):
self.host=host
self.wallets={}
self.__walletPid=None
self.endpointArgs="--host %s --port %d" % (self.nodeosHost, self.nodeosPort)
self.endpointArgs="--url http://%s:%d" % (self.nodeosHost, self.nodeosPort)
self.walletEndpointArgs=""
if self.walletd:
self.walletEndpointArgs += " --wallet-host %s --wallet-port %d" % (self.host, self.port)
self.walletEndpointArgs += " --wallet-url http://%s:%d" % (self.host, self.port)
self.endpointArgs += self.walletEndpointArgs
def launch(self):
......@@ -1185,7 +1185,7 @@ class Cluster(object):
self.walletPort=walletPort
self.walletEndpointArgs=""
if self.walletd:
self.walletEndpointArgs += " --wallet-host %s --wallet-port %d" % (self.walletHost, self.walletPort)
self.walletEndpointArgs += " --wallet-url http://%s:%d" % (self.walletHost, self.walletPort)
self.mongoEndpointArgs=""
self.mongoUri=""
if self.enableMongo:
......
......@@ -300,7 +300,7 @@ BOOST_AUTO_TEST_CASE( exchange_create ) try {
extended_asset( A(0.01 USD), N(eosio.token) ) );
for( const auto& at : result.action_traces )
ilog( "\r${s}", ("s",at.console) );
ilog( "${s}", ("s",at.console) );
trader_ex_usd = t.get_exchange_balance( N(exchange), N(eosio.token), symbol(2,"USD"), N(trader) );
trader_ex_btc = t.get_exchange_balance( N(exchange), N(eosio.token), symbol(2,"BTC"), N(trader) );
......@@ -315,7 +315,7 @@ BOOST_AUTO_TEST_CASE( exchange_create ) try {
trader_ex_btc = t.get_exchange_balance( N(exchange), N(eosio.token), symbol(2,"BTC"), N(trader) );
for( const auto& at : result.action_traces )
ilog( "\r${s}", ("s",at.console) );
ilog( "${s}", ("s",at.console) );
wdump((trader_ex_btc.quantity));
wdump((trader_ex_usd.quantity));
......
......@@ -85,7 +85,7 @@ public:
get_owner_act.data = abi_ser_test.variant_to_binary("getowner", mutable_variant_object()
("identity", identity)
);
BOOST_REQUIRE_EQUAL(success(), push_action(std::move(get_owner_act), 0));
BOOST_REQUIRE_EQUAL(success(), push_action(std::move(get_owner_act), N(alice)));
return get_result_uint64();
}
......@@ -97,7 +97,7 @@ public:
get_identity_act.data = abi_ser_test.variant_to_binary("getidentity", mutable_variant_object()
("account", account)
);
BOOST_REQUIRE_EQUAL(success(), push_action(std::move(get_identity_act), 0));
BOOST_REQUIRE_EQUAL(success(), push_action(std::move(get_identity_act), N(alice)));
return get_result_uint64();
}
......@@ -144,7 +144,9 @@ public:
fc::variant get_certrow(uint64_t identity, const string& property, uint64_t trusted, const string& certifier) {
const auto& db = control->get_database();
const auto* t_id = db.find<table_id_object, by_code_scope_table>(boost::make_tuple(N(identity), identity, N( certs )));
FC_ASSERT(t_id != 0, "certrow not found");
if ( !t_id ) {
return fc::variant(nullptr);
}
const auto& idx = db.get_index<index256_index, by_secondary>();
auto key = key256::make_from_word_sequence<uint64_t>(string_to_name(property.c_str()), trusted, string_to_name(certifier.c_str()));
......@@ -401,8 +403,6 @@ BOOST_FIXTURE_TEST_CASE( trust_untrust, identity_tester ) try {
} FC_LOG_AND_RETHROW() //trust_untrust
// TODO: Anton please re-enable when finished with new db api changes
#if 0
BOOST_FIXTURE_TEST_CASE( certify_decertify_owner, identity_tester ) try {
BOOST_REQUIRE_EQUAL(success(), create_identity("alice", identity_val));
......@@ -677,6 +677,5 @@ BOOST_FIXTURE_TEST_CASE( ownership_contradiction, identity_tester ) try {
BOOST_REQUIRE_EQUAL(0, get_identity_for_account("alice"));
} FC_LOG_AND_RETHROW() //ownership_contradiction
#endif
BOOST_AUTO_TEST_SUITE_END()
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-compare"
#include <boost/test/unit_test.hpp>
#pragma GCC diagnostic pop
#include <boost/algorithm/string/predicate.hpp>
#include <eosio/testing/tester.hpp>
#include <eosio/chain/contracts/abi_serializer.hpp>
#include <payloadless/payloadless.wast.hpp>
#include <payloadless/payloadless.abi.hpp>
#include <Runtime/Runtime.h>
#include <fc/variant_object.hpp>
#include <fc/io/json.hpp>
#ifdef NON_VALIDATING_TEST
#define TESTER tester
#else
#define TESTER validating_tester
#endif
using namespace eosio;
using namespace eosio::chain;
using namespace eosio::chain::contracts;
using namespace eosio::testing;
using namespace fc;
class payloadless_tester : public TESTER {
};
BOOST_AUTO_TEST_SUITE(payloadless_tests)
BOOST_FIXTURE_TEST_CASE( test_doit, payloadless_tester ) {
create_accounts( {N(payloadless)} );
set_code( N(payloadless), payloadless_wast );
set_abi( N(payloadless), payloadless_abi );
auto trace = push_action(N(payloadless), N(doit), N(payloadless), mutable_variant_object());
auto msg = trace.action_traces.front().console;
BOOST_CHECK_EQUAL(msg == "Im a payloadless action", true);
}
BOOST_AUTO_TEST_SUITE_END()
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册