Merge branch 'github_master'

Conflicts:
	libraries/fc
This commit is contained in:
Eric Frias 2015-09-18 16:08:32 -04:00
commit 9c4a54edce
271 changed files with 18481 additions and 5676 deletions

2
.gitignore vendored
View file

@ -6,6 +6,8 @@ CMakeCache.txt
CMakeFiles
Makefile
compile_commands.json
moc_*
*.moc
libraries/utilities/git_revision.cpp

View file

@ -83,7 +83,7 @@ Witness node
The role of the witness node is to broadcast transactions, download blocks, and optionally sign them.
```
./witness_node --rpc-endpoint "127.0.0.1:8090" --enable-stale-production -w \""1.6.0"\" \""1.6.1"\" \""1.6.2"\" \""1.6.3"\" \""1.6.4"\"
./witness_node --rpc-endpoint 127.0.0.1:8090 --enable-stale-production -w '"1.6.0"' '"1.6.1"' '"1.6.2"' '"1.6.3"' '"1.6.4"' '"1.6.5"' '"1.6.6"' '"1.6.7"' '"1.6.8"' '"1.6.9"' '"1.6.10"' '"1.6.11"' '"1.6.12"' '"1.6.13"' '"1.6.14"' '"1.6.15"' '"1.6.16"' '"1.6.17"' '"1.6.18"' '"1.6.19"' '"1.6.20"' '"1.6.21"' '"1.6.22"' '"1.6.23"' '"1.6.24"' '"1.6.25"' '"1.6.26"' '"1.6.27"' '"1.6.28"' '"1.6.29"' '"1.6.30"' '"1.6.31"' '"1.6.32"' '"1.6.33"' '"1.6.34"' '"1.6.35"' '"1.6.36"' '"1.6.37"' '"1.6.38"' '"1.6.39"' '"1.6.40"' '"1.6.41"' '"1.6.42"' '"1.6.43"' '"1.6.44"' '"1.6.45"' '"1.6.46"' '"1.6.47"' '"1.6.48"' '"1.6.49"' '"1.6.50"' '"1.6.51"' '"1.6.52"' '"1.6.53"' '"1.6.54"' '"1.6.55"' '"1.6.56"' '"1.6.57"' '"1.6.58"' '"1.6.59"' '"1.6.60"' '"1.6.61"' '"1.6.62"' '"1.6.63"' '"1.6.64"' '"1.6.65"' '"1.6.66"' '"1.6.67"' '"1.6.68"' '"1.6.69"' '"1.6.70"' '"1.6.71"' '"1.6.72"' '"1.6.73"' '"1.6.74"' '"1.6.75"' '"1.6.76"' '"1.6.77"' '"1.6.78"' '"1.6.79"' '"1.6.80"' '"1.6.81"' '"1.6.82"' '"1.6.83"' '"1.6.84"' '"1.6.85"' '"1.6.86"' '"1.6.87"' '"1.6.88"' '"1.6.89"' '"1.6.90"' '"1.6.91"' '"1.6.92"' '"1.6.93"' '"1.6.94"' '"1.6.95"' '"1.6.96"' '"1.6.97"' '"1.6.98"' '"1.6.99"' '"1.6.100"'
```
Running specific tests
@ -99,20 +99,17 @@ When running `witness_node`, initially two API's are available:
API 0 provides read-only access to the database, while API 1 is
used to login and gain access to additional, restricted API's.
TODO: the following examples use the old authority definition and thus do not work. They need to be updated.
Here is an example using `wscat` package from `npm` for websockets:
$ npm install -g wscat
$ wscat -c ws://127.0.0.1:8090
> {"id":1, "method":"call", "params":[0,"get_accounts",[["1.2.0"]]]}
< {"id":1,"result":[{"id":"1.2.0","annotations":[],"registrar":"1.2.0","referrer":"1.2.0","referrer_percent":0,"name":"genesis","owner":{"weight_threshold":1,"key_auths":[["PUBLIC_KEY",1]]},"active":{"weight_threshold":1,"key_auths":[["PUBLIC_KEY",1]]},"memo_key":"PUBLIC_KEY","voting_account":"1.2.0","num_witness":0,"num_committee":0,"votes":[],"statistics":"2.7.0","whitelisting_accounts":[],"blacklisting_accounts":[]}]}
$
< {"id":1,"result":[{"id":"1.2.0","annotations":[],"membership_expiration_date":"1969-12-31T23:59:59","registrar":"1.2.0","referrer":"1.2.0","lifetime_referrer":"1.2.0","network_fee_percentage":2000,"lifetime_referrer_fee_percentage":8000,"referrer_rewards_percentage":0,"name":"committee-account","owner":{"weight_threshold":1,"account_auths":[],"key_auths":[],"address_auths":[]},"active":{"weight_threshold":6,"account_auths":[["1.2.5",1],["1.2.6",1],["1.2.7",1],["1.2.8",1],["1.2.9",1],["1.2.10",1],["1.2.11",1],["1.2.12",1],["1.2.13",1],["1.2.14",1]],"key_auths":[],"address_auths":[]},"options":{"memo_key":"GPH1111111111111111111111111111111114T1Anm","voting_account":"1.2.0","num_witness":0,"num_committee":0,"votes":[],"extensions":[]},"statistics":"2.7.0","whitelisting_accounts":[],"blacklisting_accounts":[]}]}
We can do the same thing using an HTTP client such as `curl` for API's which do not require login or other session state:
$ curl --data '{"jsonrpc": "2.0", "method": "call", "params": [0, "get_accounts", [["1.2.0"]]], "id": 1}' http://127.0.0.1:8090/rpc
{"id":1,"result":[{"id":"1.2.0","annotations":[],"registrar":"1.2.0","referrer":"1.2.0","referrer_percent":0,"name":"genesis","owner":{"weight_threshold":1,"key_auths":[["PUBLIC_KEY",1]]},"active":{"weight_threshold":1,"key_auths":[["PUBLIC_KEY",1]]},"memo_key":"PUBLIC_KEY","voting_account":"1.2.0","num_witness":0,"num_committee":0,"votes":[],"statistics":"2.7.0","whitelisting_accounts":[],"blacklisting_accounts":[]}]}
{"id":1,"result":[{"id":"1.2.0","annotations":[],"membership_expiration_date":"1969-12-31T23:59:59","registrar":"1.2.0","referrer":"1.2.0","lifetime_referrer":"1.2.0","network_fee_percentage":2000,"lifetime_referrer_fee_percentage":8000,"referrer_rewards_percentage":0,"name":"committee-account","owner":{"weight_threshold":1,"account_auths":[],"key_auths":[],"address_auths":[]},"active":{"weight_threshold":6,"account_auths":[["1.2.5",1],["1.2.6",1],["1.2.7",1],["1.2.8",1],["1.2.9",1],["1.2.10",1],["1.2.11",1],["1.2.12",1],["1.2.13",1],["1.2.14",1]],"key_auths":[],"address_auths":[]},"options":{"memo_key":"GPH1111111111111111111111111111111114T1Anm","voting_account":"1.2.0","num_witness":0,"num_committee":0,"votes":[],"extensions":[]},"statistics":"2.7.0","whitelisting_accounts":[],"blacklisting_accounts":[]}]}
API 0 is accessible using regular JSON-RPC:
@ -122,7 +119,8 @@ Accessing restricted API's
--------------------------
You can restrict API's to particular users by specifying an `apiaccess` file in `config.ini`. Here is an example `apiaccess` file which allows
user `bytemaster` with password `supersecret` to access four different API's:
user `bytemaster` with password `supersecret` to access four different API's, while allowing any other user to access the three public API's
necessary to use the wallet:
{
"permission_map" :
@ -134,6 +132,14 @@ user `bytemaster` with password `supersecret` to access four different API's:
"password_salt_b64" : "INDdM6iCi/8=",
"allowed_apis" : ["database_api", "network_broadcast_api", "history_api", "network_node_api"]
}
],
[
"*",
{
"password_hash_b64" : "*",
"password_salt_b64" : "*",
"allowed_apis" : ["database_api", "network_broadcast_api", "history_api"]
}
]
]
}
@ -159,29 +165,7 @@ it is fairly simple to write API methods to expose database methods.
Running private testnet
-----------------------
Normally `witness_node` assumes it won't be producing blocks from
genesis, or against very old chain state. We need to get `witness_node`
to discard this assumption if we actually want to start a new chain,
so we will need to specify in `config.ini`:
enable-stale-production = true
We also need to specify which witnesses will produce blocks locally;
`witness_node` does not assume that it should produce blocks for a given
witness just because it has the correct private key to do so. There are
ten witnesses at genesis of the testnet, block production can be
enabled for all of them by specifying multiple times in `config.ini`:
witness-id = "1.6.0"
witness-id = "1.6.1"
witness-id = "1.6.2"
witness-id = "1.6.3"
witness-id = "1.6.4"
witness-id = "1.6.5"
witness-id = "1.6.6"
witness-id = "1.6.7"
witness-id = "1.6.8"
witness-id = "1.6.9"
See the [documentation](https://github.com/cryptonomex/graphene/wiki/private-testnet) if you want to run a private testnet.
Questions
---------
@ -223,7 +207,7 @@ Questions
The second number specifies the *type*. The type of the object determines what fields it has. For a
complete list of type ID's, see `enum object_type` and `enum impl_object_type` in
[types.hpp](https://github.com/cryptonomex/graphene/blob/master/libraries/chain/include/graphene/chain/types.hpp).
[types.hpp](https://github.com/cryptonomex/graphene/blob/master/libraries/chain/include/graphene/chain/protocol/types.hpp).
The third number specifies the *instance*. The instance of the object is different for each individual
object.
@ -240,3 +224,16 @@ Questions
The first and second number together identify the kind of thing you're talking about (`1.2` for accounts,
`1.3` for assets). The third number identifies the particular thing.
- How do I get the `network_add_nodes` command to work? Why is it so complicated?
You need to follow the instructions in the "Accessing restricted API's" section to
allow a username/password access to the `network_node` API. Then you need
to pass the username/password to the `cli_wallet` on the command line or in a config file.
It's set up this way so that the default configuration is secure even if the RPC port is
publicly accessible. It's fine if your `witness_node` allows the general public to query
the database or broadcast transactions (in fact, this is how the hosted web UI works). It's
less fine if your `witness_node` allows the general public to control which p2p nodes it's
connecting to. Therefore the API to add p2p connections needs to be set up with proper access
controls.

112
Vagrantfile vendored Normal file
View file

@ -0,0 +1,112 @@
# Configures Ubuntu 14.04 VM to be used with BitShares 2.0 (Graphene)
# Downloads and builds all necessary software to run witness node and web GUI
# Use with Vagrant (http://docs.vagrantup.com/v2/getting-started/index.html)
# or just execute the shell script below.
# Vagrant setup supports the following providers: Virtual Box, Digital Ocean, Amazon EC2
$script = <<SCRIPT
# ------ shell script begin ------
echo_msg() {
/bin/echo -e "\e[1;36m*** $1 ***\e[0m"
}
echo_msg "Current user: `id`"
echo_msg "Current dir: `pwd`"
echo_msg "updating system.."
sudo apt-get -y update
sudo apt-get -yfV dist-upgrade
echo_msg "installing required packages.."
sudo apt-get install -yfV git libreadline-dev uuid-dev g++ libdb++-dev libdb-dev zip
sudo apt-get install -yfV libssl-dev openssl build-essential python-dev autotools-dev libicu-dev build-essential
sudo apt-get install -yfV libbz2-dev automake doxygen cmake ncurses-dev libtool nodejs nodejs-legacy npm mc
sudo apt-get -y autoremove
[ ! -d "bts" ] && mkdir bts && cd bts
[ ! -d "tmp" ] && mkdir tmp
[ ! -d "build" ] && mkdir build
if [ ! -d "tmp/boost_1_57_0" ]; then
echo_msg "building boost.."
cd tmp/
wget -nv 'http://sourceforge.net/projects/boost/files/boost/1.57.0/boost_1_57_0.tar.bz2/download'
tar -xf download
cd boost_1_57_0/
./bootstrap.sh --prefix=/usr/local/ > /dev/null
sudo ./b2 install > /dev/null
cd ~/bts
fi
if [ ! -d "graphene" ]; then
echo_msg "building bitshares graphene toolkit.."
git clone https://github.com/cryptonomex/graphene.git
cd graphene
git submodule update --init --recursive
cmake .
make
cd ~/bts
fi
if [ ! -d "graphene-ui" ]; then
echo_msg "installing ui dependencies.."
git clone https://github.com/cryptonomex/graphene-ui.git
cd graphene-ui/dl
npm install --silent
cd ../web
npm install --silent
npm run-script build
cd ~/bts
fi
# ------ shell script end ------
SCRIPT
Dir["*.sh"].each {|s| File.open(s,"r"){|f| $script << f.read()} } # includes additional .sh files (plug-ins)
Vagrant.configure(2) do |config|
config.vm.box = 'ubuntu_trusty_x64'
config.vm.provision 'shell', inline: $script, privileged: false
config.ssh.username = 'vagrant'
# to use with Digital Ocean please install this plugin https://github.com/smdahlen/vagrant-digitalocean
# note: due to bug in vagrant-digitalocean you need to run provision separetly:
# vagrant up --provider digital_ocean --no-provision
# vagrant provision
config.vm.provider :digital_ocean do |provider, override|
override.vm.hostname = 'graphene'
override.ssh.private_key_path = ENV['VAGRANT_KEY_PATH']
override.vm.box = 'digital_ocean'
override.vm.box_url = 'https://github.com/smdahlen/vagrant-digitalocean/raw/master/box/digital_ocean.box'
provider.setup = true
provider.region = 'nyc2'
provider.image = 'ubuntu-14-04-x64'
provider.size = '4GB' # 2GB may be not enought to compile graphene toolkit
provider.token = ENV['DIGITALOCEAN_TOKEN']
provider.ssh_key_name = 'vagrant'
end
config.vm.provider :aws do |aws, override|
aws.access_key_id = ENV['AWS_ACCESS_KEY']
aws.secret_access_key = ENV['AWS_SECRET_KEY']
aws.keypair_name = $1 if ENV['VAGRANT_KEY_PATH'] =~ /([\w]+)[\.\w]*$/
aws.region = "us-east-1"
aws.ami = 'ami-018c9568'
aws.instance_type = 'm1.small'
aws.security_groups = [ 'bitsharesxt' ]
override.vm.hostname = 'bitsharesxt-aws'
override.ssh.username = 'ubuntu'
override.ssh.private_key_path = ENV['VAGRANT_KEY_PATH']
override.vm.box = 'dummy'
end
config.vm.provider 'virtualbox' do |v|
v.customize ['modifyvm', :id, '--memory', '4096']
v.customize ['modifyvm', :id, '--cpus', 4]
end
end

View file

@ -2,7 +2,9 @@ add_subdirectory( fc )
add_subdirectory( db )
add_subdirectory( deterministic_openssl_rand )
add_subdirectory( chain )
add_subdirectory( egenesis )
add_subdirectory( net )
#add_subdirectory( p2p )
add_subdirectory( time )
add_subdirectory( utilities )
add_subdirectory( app )

View file

@ -1,15 +1,29 @@
file(GLOB HEADERS "include/graphene/app/*.hpp")
file(GLOB EGENESIS_HEADERS "../egenesis/include/graphene/app/*.hpp")
add_library( graphene_app
api.cpp
application.cpp
database_api.cpp
impacted.cpp
plugin.cpp
${HEADERS}
${EGENESIS_HEADERS}
)
target_link_libraries( graphene_app graphene_market_history graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities )
target_include_directories( graphene_app
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_SOURCE_DIR}/../egenesis/include" )
if(MSVC)
set_source_files_properties( application.cpp api.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
set_source_files_properties( application.cpp api.cpp database_api.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
endif(MSVC)
INSTALL( TARGETS
graphene_app
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)

View file

@ -15,331 +15,26 @@
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cctype>
#include <graphene/app/api.hpp>
#include <graphene/app/api_access.hpp>
#include <graphene/app/application.hpp>
#include <graphene/app/impacted.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/get_config.hpp>
#include <graphene/utilities/key_conversion.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <graphene/chain/withdraw_permission_object.hpp>
#include <graphene/chain/worker_evaluator.hpp>
#include <graphene/chain/transaction_object.hpp>
#include <graphene/chain/confidential_evaluator.hpp>
#include <fc/crypto/hex.hpp>
#include <fc/smart_ref_impl.hpp>
namespace graphene { namespace app {
database_api::database_api(graphene::chain::database& db):_db(db)
{
_change_connection = _db.changed_objects.connect([this](const vector<object_id_type>& ids) {
on_objects_changed(ids);
});
_applied_block_connection = _db.applied_block.connect([this](const signed_block&){ on_applied_block(); });
}
fc::variants database_api::get_objects(const vector<object_id_type>& ids)const
{
fc::variants result;
result.reserve(ids.size());
std::transform(ids.begin(), ids.end(), std::back_inserter(result),
[this](object_id_type id) -> fc::variant {
if(auto obj = _db.find_object(id))
return obj->to_variant();
return {};
});
return result;
}
optional<block_header> database_api::get_block_header(uint32_t block_num) const
{
auto result = _db.fetch_block_by_number(block_num);
if(result)
return *result;
return {};
}
optional<signed_block> database_api::get_block(uint32_t block_num)const
{
return _db.fetch_block_by_number(block_num);
}
processed_transaction database_api::get_transaction(uint32_t block_num, uint32_t trx_num)const
{
auto opt_block = _db.fetch_block_by_number(block_num);
FC_ASSERT( opt_block );
FC_ASSERT( opt_block->transactions.size() > trx_num );
return opt_block->transactions[trx_num];
}
vector<optional<account_object>> database_api::lookup_account_names(const vector<string>& account_names)const
{
const auto& accounts_by_name = _db.get_index_type<account_index>().indices().get<by_name>();
vector<optional<account_object> > result;
result.reserve(account_names.size());
std::transform(account_names.begin(), account_names.end(), std::back_inserter(result),
[&accounts_by_name](const string& name) -> optional<account_object> {
auto itr = accounts_by_name.find(name);
return itr == accounts_by_name.end()? optional<account_object>() : *itr;
});
return result;
}
vector<optional<asset_object>> database_api::lookup_asset_symbols(const vector<string>& symbols)const
{
const auto& assets_by_symbol = _db.get_index_type<asset_index>().indices().get<by_symbol>();
vector<optional<asset_object> > result;
result.reserve(symbols.size());
std::transform(symbols.begin(), symbols.end(), std::back_inserter(result),
[&assets_by_symbol](const string& symbol) -> optional<asset_object> {
auto itr = assets_by_symbol.find(symbol);
return itr == assets_by_symbol.end()? optional<asset_object>() : *itr;
});
return result;
}
global_property_object database_api::get_global_properties()const
{
return _db.get(global_property_id_type());
}
dynamic_global_property_object database_api::get_dynamic_global_properties()const
{
return _db.get(dynamic_global_property_id_type());
}
vector<optional<account_object>> database_api::get_accounts(const vector<account_id_type>& account_ids)const
{
vector<optional<account_object>> result; result.reserve(account_ids.size());
std::transform(account_ids.begin(), account_ids.end(), std::back_inserter(result),
[this](account_id_type id) -> optional<account_object> {
if(auto o = _db.find(id))
return *o;
return {};
});
return result;
}
vector<optional<asset_object>> database_api::get_assets(const vector<asset_id_type>& asset_ids)const
{
vector<optional<asset_object>> result; result.reserve(asset_ids.size());
std::transform(asset_ids.begin(), asset_ids.end(), std::back_inserter(result),
[this](asset_id_type id) -> optional<asset_object> {
if(auto o = _db.find(id))
return *o;
return {};
});
return result;
}
uint64_t database_api::get_account_count()const
{
return _db.get_index_type<account_index>().indices().size();
}
map<string,account_id_type> database_api::lookup_accounts(const string& lower_bound_name, uint32_t limit)const
{
FC_ASSERT( limit <= 1000 );
const auto& accounts_by_name = _db.get_index_type<account_index>().indices().get<by_name>();
map<string,account_id_type> result;
for( auto itr = accounts_by_name.lower_bound(lower_bound_name);
limit-- && itr != accounts_by_name.end();
++itr )
result.insert(make_pair(itr->name, itr->get_id()));
return result;
}
vector<asset> database_api::get_account_balances(account_id_type acnt, const flat_set<asset_id_type>& assets)const
{
vector<asset> result;
if (assets.empty())
{
// if the caller passes in an empty list of assets, return balances for all assets the account owns
const account_balance_index& balance_index = _db.get_index_type<account_balance_index>();
auto range = balance_index.indices().get<by_account>().equal_range(acnt);
for (const account_balance_object& balance : boost::make_iterator_range(range.first, range.second))
result.push_back(asset(balance.get_balance()));
}
else
{
result.reserve(assets.size());
std::transform(assets.begin(), assets.end(), std::back_inserter(result),
[this, acnt](asset_id_type id) { return _db.get_balance(acnt, id); });
}
return result;
}
vector<asset> database_api::get_named_account_balances(const std::string& name, const flat_set<asset_id_type>& assets) const
{
const auto& accounts_by_name = _db.get_index_type<account_index>().indices().get<by_name>();
auto itr = accounts_by_name.find(name);
FC_ASSERT( itr != accounts_by_name.end() );
return get_account_balances(itr->get_id(), assets);
}
/**
* @return the limit orders for both sides of the book for the two assets specified up to limit number on each side.
*/
vector<limit_order_object> database_api::get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const
{
const auto& limit_order_idx = _db.get_index_type<limit_order_index>();
const auto& limit_price_idx = limit_order_idx.indices().get<by_price>();
vector<limit_order_object> result;
uint32_t count = 0;
auto limit_itr = limit_price_idx.lower_bound(price::max(a,b));
auto limit_end = limit_price_idx.upper_bound(price::min(a,b));
while(limit_itr != limit_end && count < limit)
{
result.push_back(*limit_itr);
++limit_itr;
++count;
}
count = 0;
limit_itr = limit_price_idx.lower_bound(price::max(b,a));
limit_end = limit_price_idx.upper_bound(price::min(b,a));
while(limit_itr != limit_end && count < limit)
{
result.push_back(*limit_itr);
++limit_itr;
++count;
}
return result;
}
vector<call_order_object> database_api::get_call_orders(asset_id_type a, uint32_t limit)const
{
const auto& call_index = _db.get_index_type<call_order_index>().indices().get<by_price>();
const asset_object& mia = _db.get(a);
price index_price = price::min(mia.bitasset_data(_db).options.short_backing_asset, mia.get_id());
return vector<call_order_object>(call_index.lower_bound(index_price.min()),
call_index.lower_bound(index_price.max()));
}
vector<force_settlement_object> database_api::get_settle_orders(asset_id_type a, uint32_t limit)const
{
const auto& settle_index = _db.get_index_type<force_settlement_index>().indices().get<by_expiration>();
const asset_object& mia = _db.get(a);
return vector<force_settlement_object>(settle_index.lower_bound(mia.get_id()),
settle_index.upper_bound(mia.get_id()));
}
vector<asset_object> database_api::list_assets(const string& lower_bound_symbol, uint32_t limit)const
{
FC_ASSERT( limit <= 100 );
const auto& assets_by_symbol = _db.get_index_type<asset_index>().indices().get<by_symbol>();
vector<asset_object> result;
result.reserve(limit);
auto itr = assets_by_symbol.lower_bound(lower_bound_symbol);
if( lower_bound_symbol == "" )
itr = assets_by_symbol.begin();
while(limit-- && itr != assets_by_symbol.end())
result.emplace_back(*itr++);
return result;
}
fc::optional<committee_member_object> database_api::get_committee_member_by_account(account_id_type account) const
{
const auto& idx = _db.get_index_type<committee_member_index>().indices().get<by_account>();
auto itr = idx.find(account);
if( itr != idx.end() )
return *itr;
return {};
}
fc::optional<witness_object> database_api::get_witness_by_account(account_id_type account) const
{
const auto& idx = _db.get_index_type<witness_index>().indices().get<by_account>();
auto itr = idx.find(account);
if( itr != idx.end() )
return *itr;
return {};
}
uint64_t database_api::get_witness_count()const
{
return _db.get_index_type<witness_index>().indices().size();
}
map<string, witness_id_type> database_api::lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const
{
FC_ASSERT( limit <= 1000 );
const auto& witnesses_by_id = _db.get_index_type<witness_index>().indices().get<by_id>();
// we want to order witnesses by account name, but that name is in the account object
// so the witness_index doesn't have a quick way to access it.
// get all the names and look them all up, sort them, then figure out what
// records to return. This could be optimized, but we expect the
// number of witnesses to be few and the frequency of calls to be rare
std::map<std::string, witness_id_type> witnesses_by_account_name;
for (const witness_object& witness : witnesses_by_id)
if (auto account_iter = _db.find(witness.witness_account))
if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name
witnesses_by_account_name.insert(std::make_pair(account_iter->name, witness.id));
auto end_iter = witnesses_by_account_name.begin();
while (end_iter != witnesses_by_account_name.end() && limit--)
++end_iter;
witnesses_by_account_name.erase(end_iter, witnesses_by_account_name.end());
return witnesses_by_account_name;
}
map<string, committee_member_id_type> database_api::lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const
{
FC_ASSERT( limit <= 1000 );
const auto& committee_members_by_id = _db.get_index_type<committee_member_index>().indices().get<by_id>();
// we want to order committee_members by account name, but that name is in the account object
// so the committee_member_index doesn't have a quick way to access it.
// get all the names and look them all up, sort them, then figure out what
// records to return. This could be optimized, but we expect the
// number of committee_members to be few and the frequency of calls to be rare
std::map<std::string, committee_member_id_type> committee_members_by_account_name;
for (const committee_member_object& committee_member : committee_members_by_id)
if (auto account_iter = _db.find(committee_member.committee_member_account))
if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name
committee_members_by_account_name.insert(std::make_pair(account_iter->name, committee_member.id));
auto end_iter = committee_members_by_account_name.begin();
while (end_iter != committee_members_by_account_name.end() && limit--)
++end_iter;
committee_members_by_account_name.erase(end_iter, committee_members_by_account_name.end());
return committee_members_by_account_name;
}
vector<optional<witness_object>> database_api::get_witnesses(const vector<witness_id_type>& witness_ids)const
{
vector<optional<witness_object>> result; result.reserve(witness_ids.size());
std::transform(witness_ids.begin(), witness_ids.end(), std::back_inserter(result),
[this](witness_id_type id) -> optional<witness_object> {
if(auto o = _db.find(id))
return *o;
return {};
});
return result;
}
vector<optional<committee_member_object>> database_api::get_committee_members(const vector<committee_member_id_type>& committee_member_ids)const
{
vector<optional<committee_member_object>> result; result.reserve(committee_member_ids.size());
std::transform(committee_member_ids.begin(), committee_member_ids.end(), std::back_inserter(result),
[this](committee_member_id_type id) -> optional<committee_member_object> {
if(auto o = _db.find(id))
return *o;
return {};
});
return result;
}
login_api::login_api(application& a)
:_app(a)
{
@ -401,15 +96,18 @@ namespace graphene { namespace app {
{
if( _callbacks.size() )
{
/// we need to ensure the database_api is not deleted for the life of the async operation
auto capture_this = shared_from_this();
for( uint32_t trx_num = 0; trx_num < b.transactions.size(); ++trx_num )
{
const auto& trx = b.transactions[trx_num];
auto id = trx.id();
auto itr = _callbacks.find(id);
auto block_num = b.block_num();
if( itr != _callbacks.end() )
{
fc::async( [=](){ itr->second( fc::variant(transaction_confirmation{ id, block_num, trx_num, trx}) ); } );
auto block_num = b.block_num();
auto& callback = _callbacks.find(id)->second;
fc::async( [capture_this,this,id,block_num,trx_num,trx,callback](){ callback( fc::variant(transaction_confirmation{ id, block_num, trx_num, trx}) ); } );
}
}
}
@ -422,7 +120,13 @@ namespace graphene { namespace app {
_app.p2p_node()->broadcast_transaction(trx);
}
void network_broadcast_api::broadcast_transaction_with_callback( confirmation_callback cb, const signed_transaction& trx)
void network_broadcast_api::broadcast_block( const signed_block& b )
{
_app.chain_database()->push_block(b);
_app.p2p_node()->broadcast( net::block_message( b ));
}
void network_broadcast_api::broadcast_transaction_with_callback(confirmation_callback cb, const signed_transaction& trx)
{
trx.validate();
_callbacks[trx.id()] = cb;
@ -468,112 +172,132 @@ namespace graphene { namespace app {
return *_history_api;
}
void database_api::on_objects_changed(const vector<object_id_type>& ids)
vector<account_id_type> get_relevant_accounts( const object* obj )
{
vector<object_id_type> my_objects;
for(auto id : ids)
if(_subscriptions.find(id) != _subscriptions.end())
my_objects.push_back(id);
_broadcast_changes_complete = fc::async([=](){
for(auto id : my_objects)
{
const object* obj = _db.find_object(id);
if(obj)
{
_subscriptions[id](obj->to_variant());
}
else
{
_subscriptions[id](fc::variant(id));
}
}
});
}
/** note: this method cannot yield because it is called in the middle of
* apply a block.
*/
void database_api::on_applied_block()
{
if(_market_subscriptions.size() == 0)
return;
const auto& ops = _db.get_applied_operations();
map< std::pair<asset_id_type,asset_id_type>, vector<pair<operation, operation_result>> > subscribed_markets_ops;
for(const auto& op : ops)
vector<account_id_type> result;
if( obj->id.space() == protocol_ids )
{
std::pair<asset_id_type,asset_id_type> market;
switch(op.op.which())
switch( (object_type)obj->id.type() )
{
case operation::tag<limit_order_create_operation>::value:
market = op.op.get<limit_order_create_operation>().get_market();
break;
case operation::tag<fill_order_operation>::value:
market = op.op.get<fill_order_operation>().get_market();
break;
/*
case operation::tag<limit_order_cancel_operation>::value:
*/
default: break;
case null_object_type:
case base_object_type:
case OBJECT_TYPE_COUNT:
return result;
case account_object_type:{
result.push_back( obj->id );
break;
} case asset_object_type:{
const auto& aobj = dynamic_cast<const asset_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->issuer );
break;
} case force_settlement_object_type:{
const auto& aobj = dynamic_cast<const force_settlement_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->owner );
break;
} case committee_member_object_type:{
const auto& aobj = dynamic_cast<const committee_member_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->committee_member_account );
break;
} case witness_object_type:{
const auto& aobj = dynamic_cast<const witness_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->witness_account );
break;
} case limit_order_object_type:{
const auto& aobj = dynamic_cast<const limit_order_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->seller );
break;
} case call_order_object_type:{
const auto& aobj = dynamic_cast<const call_order_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->borrower );
break;
} case custom_object_type:{
} case proposal_object_type:{
const auto& aobj = dynamic_cast<const proposal_object*>(obj);
assert( aobj != nullptr );
flat_set<account_id_type> impacted;
transaction_get_impacted_accounts( aobj->proposed_transaction, impacted );
result.reserve( impacted.size() );
for( auto& item : impacted ) result.emplace_back(item);
break;
} case operation_history_object_type:{
const auto& aobj = dynamic_cast<const operation_history_object*>(obj);
assert( aobj != nullptr );
flat_set<account_id_type> impacted;
operation_get_impacted_accounts( aobj->op, impacted );
result.reserve( impacted.size() );
for( auto& item : impacted ) result.emplace_back(item);
break;
} case withdraw_permission_object_type:{
const auto& aobj = dynamic_cast<const withdraw_permission_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->withdraw_from_account );
result.push_back( aobj->authorized_account );
break;
} case vesting_balance_object_type:{
const auto& aobj = dynamic_cast<const vesting_balance_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->owner );
break;
} case worker_object_type:{
const auto& aobj = dynamic_cast<const worker_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->worker_account );
break;
} case balance_object_type:{
/** these are free from any accounts */
}
}
if(_market_subscriptions.count(market))
subscribed_markets_ops[market].push_back(std::make_pair(op.op, op.result));
}
fc::async([=](){
for(auto item : subscribed_markets_ops)
{
auto itr = _market_subscriptions.find(item.first);
if(itr != _market_subscriptions.end())
itr->second(fc::variant(item.second));
}
});
}
database_api::~database_api()
{
try {
if(_broadcast_changes_complete.valid())
{
_broadcast_changes_complete.cancel();
_broadcast_changes_complete.wait();
}
} catch (const fc::exception& e)
else if( obj->id.space() == implementation_ids )
{
wlog("${e}", ("e",e.to_detail_string()));
switch( (impl_object_type)obj->id.type() )
{
case impl_global_property_object_type:{
} case impl_dynamic_global_property_object_type:{
} case impl_index_meta_object_type:{
} case impl_asset_dynamic_data_type:{
} case impl_asset_bitasset_data_type:{
break;
} case impl_account_balance_object_type:{
const auto& aobj = dynamic_cast<const account_balance_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->owner );
break;
} case impl_account_statistics_object_type:{
const auto& aobj = dynamic_cast<const account_statistics_object*>(obj);
assert( aobj != nullptr );
result.push_back( aobj->owner );
break;
} case impl_transaction_object_type:{
const auto& aobj = dynamic_cast<const transaction_object*>(obj);
assert( aobj != nullptr );
flat_set<account_id_type> impacted;
transaction_get_impacted_accounts( aobj->trx, impacted );
result.reserve( impacted.size() );
for( auto& item : impacted ) result.emplace_back(item);
break;
} case impl_blinded_balance_object_type:{
const auto& aobj = dynamic_cast<const blinded_balance_object*>(obj);
assert( aobj != nullptr );
result.reserve( aobj->owner.account_auths.size() );
for( const auto& a : aobj->owner.account_auths )
result.push_back( a.first );
break;
} case impl_block_summary_object_type:{
} case impl_account_transaction_history_object_type:{
} case impl_chain_property_object_type: {
} case impl_witness_schedule_object_type: {
}
}
}
}
bool database_api::subscribe_to_objects( const std::function<void(const fc::variant&)>& callback, const vector<object_id_type>& ids)
{
for(auto id : ids) _subscriptions[id] = callback;
return true;
}
bool database_api::unsubscribe_from_objects(const vector<object_id_type>& ids)
{
for(auto id : ids) _subscriptions.erase(id);
return true;
}
void database_api::subscribe_to_market(std::function<void(const variant&)> callback, asset_id_type a, asset_id_type b)
{
if(a > b) std::swap(a,b);
FC_ASSERT(a != b);
_market_subscriptions[ std::make_pair(a,b) ] = callback;
}
void database_api::unsubscribe_from_market(asset_id_type a, asset_id_type b)
{
if(a > b) std::swap(a,b);
FC_ASSERT(a != b);
_market_subscriptions.erase(std::make_pair(a,b));
}
std::string database_api::get_transaction_hex(const signed_transaction& trx)const
{
return fc::to_hex(fc::raw::pack(trx));
}
return result;
} // end get_relevant_accounts( obj )
vector<operation_history_object> history_api::get_account_history(account_id_type account, operation_history_id_type stop, unsigned limit, operation_history_id_type start) const
{
@ -597,7 +321,6 @@ namespace graphene { namespace app {
return result;
}
flat_set<uint32_t> history_api::get_market_history_buckets()const
{
auto hist = _app.get_plugin<market_history_plugin>( "market_history" );
@ -605,7 +328,7 @@ namespace graphene { namespace app {
return hist->tracked_buckets();
}
vector<bucket_object> history_api::get_market_history( asset_id_type a, asset_id_type b,
vector<bucket_object> history_api::get_market_history( asset_id_type a, asset_id_type b,
uint32_t bucket_seconds, fc::time_point_sec start, fc::time_point_sec end )const
{ try {
FC_ASSERT(_app.chain_database());
@ -629,95 +352,4 @@ namespace graphene { namespace app {
return result;
} FC_CAPTURE_AND_RETHROW( (a)(b)(bucket_seconds)(start)(end) ) }
/**
* @return all accounts that referr to the key or account id in their owner or active authorities.
*/
vector<account_id_type> database_api::get_account_references( account_id_type account_id )const
{
const auto& idx = _db.get_index_type<account_index>();
const auto& aidx = dynamic_cast<const primary_index<account_index>&>(idx);
const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();
auto itr = refs.account_to_account_memberships.find(account_id);
vector<account_id_type> result;
if( itr != refs.account_to_account_memberships.end() )
{
result.reserve( itr->second.size() );
for( auto item : itr->second ) result.push_back(item);
}
return result;
}
/**
* @return all accounts that referr to the key or account id in their owner or active authorities.
*/
vector<account_id_type> database_api::get_key_references( public_key_type key )const
{
const auto& idx = _db.get_index_type<account_index>();
const auto& aidx = dynamic_cast<const primary_index<account_index>&>(idx);
const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();
auto itr = refs.account_to_key_memberships.find(key);
vector<account_id_type> result;
if( itr != refs.account_to_key_memberships.end() )
{
result.reserve( itr->second.size() );
for( auto item : itr->second ) result.push_back(item);
}
return result;
}
/** TODO: add secondary index that will accelerate this process */
vector<proposal_object> database_api::get_proposed_transactions( account_id_type id )const
{
const auto& idx = _db.get_index_type<proposal_index>();
vector<proposal_object> result;
idx.inspect_all_objects( [&](const object& obj){
const proposal_object& p = static_cast<const proposal_object&>(obj);
if( p.required_active_approvals.find( id ) != p.required_active_approvals.end() )
result.push_back(p);
else if ( p.required_owner_approvals.find( id ) != p.required_owner_approvals.end() )
result.push_back(p);
else if ( p.available_active_approvals.find( id ) != p.available_active_approvals.end() )
result.push_back(p);
});
return result;
}
vector<call_order_object> database_api::get_margin_positions( const account_id_type& id )const
{ try {
const auto& idx = _db.get_index_type<call_order_index>();
const auto& aidx = idx.indices().get<by_account>();
auto start = aidx.lower_bound( boost::make_tuple( id, 0 ) );
auto end = aidx.lower_bound( boost::make_tuple( id+1, 0 ) );
vector<call_order_object> result;
while( start != end )
{
result.push_back(*start);
++start;
}
return result;
} FC_CAPTURE_AND_RETHROW( (id) ) }
vector<balance_object> database_api::get_balance_objects( const vector<address>& addrs )const
{ try {
const auto& bal_idx = _db.get_index_type<balance_index>();
const auto& by_owner_idx = bal_idx.indices().get<by_owner>();
vector<balance_object> result;
for( const auto& owner : addrs )
{
auto itr = by_owner_idx.lower_bound( boost::make_tuple( owner, asset_id_type(0) ) );
while( itr != by_owner_idx.end() && itr->owner == owner )
{
result.push_back( *itr );
++itr;
}
}
return result;
} FC_CAPTURE_AND_RETHROW( (addrs) ) }
} } // graphene::app

View file

@ -20,19 +20,29 @@
#include <graphene/app/application.hpp>
#include <graphene/app/plugin.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <graphene/time/time.hpp>
#include <graphene/egenesis/egenesis.hpp>
#include <graphene/net/core_messages.hpp>
#include <graphene/net/exceptions.hpp>
#include <graphene/time/time.hpp>
#include <graphene/utilities/key_conversion.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <graphene/chain/worker_evaluator.hpp>
#include <fc/smart_ref_impl.hpp>
#include <fc/rpc/api_connection.hpp>
#include <fc/rpc/websocket_api.hpp>
#include <fc/network/resolve.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/signals2.hpp>
#include <boost/range/algorithm/reverse.hpp>
#include <iostream>
@ -40,6 +50,8 @@
#include <fc/log/logger.hpp>
#include <fc/log/logger_config.hpp>
#include <boost/range/adaptor/reversed.hpp>
namespace graphene { namespace app {
using net::item_hash_t;
using net::item_id;
@ -63,11 +75,11 @@ namespace detail {
dlog("Allocating all stake to ${key}", ("key", utilities::key_to_wif(nathan_key)));
genesis_state_type initial_state;
initial_state.initial_parameters.current_fees = fee_schedule::get_default();//->set_all_fees(GRAPHENE_BLOCKCHAIN_PRECISION);
initial_state.initial_active_witnesses = 10;
initial_state.initial_active_witnesses = GRAPHENE_DEFAULT_MIN_WITNESS_COUNT;
initial_state.initial_timestamp = time_point_sec(time_point::now().sec_since_epoch() /
initial_state.initial_parameters.block_interval *
initial_state.initial_parameters.block_interval);
for( int i = 0; i < initial_state.initial_active_witnesses; ++i )
for( uint64_t i = 0; i < initial_state.initial_active_witnesses; ++i )
{
auto name = "init"+fc::to_string(i);
initial_state.initial_accounts.emplace_back(name,
@ -82,6 +94,7 @@ namespace detail {
initial_state.initial_balances.push_back({nathan_key.get_public_key(),
GRAPHENE_SYMBOL,
GRAPHENE_MAX_SHARE_SUPPLY});
return initial_state;
}
@ -101,12 +114,15 @@ namespace detail {
if( _options->count("seed-node") )
{
auto seeds = _options->at("seed-node").as<vector<string>>();
for( const string& ep : seeds )
for( const string& endpoint_string : seeds )
{
fc::ip::endpoint node = fc::ip::endpoint::from_string(ep);
ilog("Adding seed node ${ip}", ("ip", node));
_p2p_network->add_node(node);
_p2p_network->connect_to_endpoint(node);
std::vector<fc::ip::endpoint> endpoints = resolve_string_to_ip_endpoints(endpoint_string);
for (const fc::ip::endpoint& endpoint : endpoints)
{
ilog("Adding seed node ${endpoint}", ("endpoint", endpoint));
_p2p_network->add_node(endpoint);
_p2p_network->connect_to_endpoint(endpoint);
}
}
}
@ -123,6 +139,33 @@ namespace detail {
std::vector<uint32_t>());
} FC_CAPTURE_AND_RETHROW() }
std::vector<fc::ip::endpoint> resolve_string_to_ip_endpoints(const std::string& endpoint_string)
{
try
{
string::size_type colon_pos = endpoint_string.find(':');
if (colon_pos == std::string::npos)
FC_THROW("Missing required port number in endpoint string \"${endpoint_string}\"",
("endpoint_string", endpoint_string));
std::string port_string = endpoint_string.substr(colon_pos + 1);
try
{
uint16_t port = boost::lexical_cast<uint16_t>(port_string);
std::string hostname = endpoint_string.substr(0, colon_pos);
std::vector<fc::ip::endpoint> endpoints = fc::resolve(hostname, port);
if (endpoints.empty())
FC_THROW_EXCEPTION(fc::unknown_host_exception, "The host name can not be resolved: ${hostname}", ("hostname", hostname));
return endpoints;
}
catch (const boost::bad_lexical_cast&)
{
FC_THROW("Bad port: ${port}", ("port", port_string));
}
}
FC_CAPTURE_AND_RETHROW((endpoint_string))
}
void reset_websocket_server()
{ try {
if( !_options->count("rpc-endpoint") )
@ -181,6 +224,15 @@ namespace detail {
fc::remove_all(_data_dir / "blockchain/dblock");
}
void set_dbg_init_key( genesis_state_type& genesis, const std::string& init_key )
{
flat_set< std::string > initial_witness_names;
public_key_type init_pubkey( init_key );
for( uint64_t i=0; i<genesis.initial_active_witnesses; i++ )
genesis.initial_witness_candidates[i].block_signing_key = init_pubkey;
return;
}
void startup()
{ try {
bool clean = !fc::exists(_data_dir / "blockchain/dblock");
@ -189,28 +241,55 @@ namespace detail {
auto initial_state = [&] {
ilog("Initializing database...");
if( _options->count("genesis-json") )
return fc::json::from_file(_options->at("genesis-json").as<boost::filesystem::path>())
.as<genesis_state_type>();
{
genesis_state_type genesis = fc::json::from_file(_options->at("genesis-json").as<boost::filesystem::path>()).as<genesis_state_type>();
bool modified_genesis = false;
if( _options->count("genesis-timestamp") )
{
genesis.initial_timestamp = fc::time_point_sec( graphene::time::now() ) + genesis.initial_parameters.block_interval + _options->at("genesis-timestamp").as<uint32_t>();
genesis.initial_timestamp -= genesis.initial_timestamp.sec_since_epoch() % genesis.initial_parameters.block_interval;
modified_genesis = true;
std::cerr << "Used genesis timestamp: " << genesis.initial_timestamp.to_iso_string() << " (PLEASE RECORD THIS)\n";
}
if( _options->count("dbg-init-key") )
{
std::string init_key = _options->at( "dbg-init-key" ).as<string>();
FC_ASSERT( genesis.initial_witness_candidates.size() >= genesis.initial_active_witnesses );
set_dbg_init_key( genesis, init_key );
modified_genesis = true;
std::cerr << "Set init witness key to " << init_key << "\n";
}
if( modified_genesis )
{
std::cerr << "WARNING: GENESIS WAS MODIFIED, YOUR CHAIN ID MAY BE DIFFERENT\n";
}
return genesis;
}
else
return create_example_genesis();
{
std::string egenesis_json;
graphene::egenesis::compute_egenesis_json( egenesis_json );
FC_ASSERT( egenesis_json != "" );
FC_ASSERT( graphene::egenesis::get_egenesis_json_hash() == fc::sha256::hash( egenesis_json ) );
return fc::json::from_string( egenesis_json ).as<genesis_state_type>();
}
};
if( _options->count("resync-blockchain") )
_chain_db->wipe(_data_dir / "blockchain", true);
flat_map<uint32_t,block_id_type> loaded_checkpoints;
if( _options->count("checkpoint") )
{
auto cps = _options->at("checkpoint").as<vector<string>>();
flat_map<uint32_t,block_id_type> loaded_checkpoints;
loaded_checkpoints.reserve( cps.size() );
for( auto cp : cps )
{
auto item = fc::json::from_string(cp).as<std::pair<uint32_t,block_id_type> >();
loaded_checkpoints[item.first] = item.second;
}
_chain_db->add_checkpoints( loaded_checkpoints );
}
_chain_db->add_checkpoints( loaded_checkpoints );
if( _options->count("replay-blockchain") )
{
@ -223,8 +302,20 @@ namespace detail {
_chain_db->reindex(_data_dir / "blockchain", initial_state());
}
if( _options->count("apiaccess") )
_apiaccess = fc::json::from_file( _options->at("apiaccess").as<boost::filesystem::path>() )
if (!_options->count("genesis-json") &&
_chain_db->get_chain_id() != graphene::egenesis::get_egenesis_chain_id()) {
elog("Detected old database. Nuking and starting over.");
_chain_db->wipe(_data_dir / "blockchain", true);
_chain_db.reset();
_chain_db = std::make_shared<chain::database>();
_chain_db->add_checkpoints(loaded_checkpoints);
_chain_db->open(_data_dir / "blockchain", initial_state);
}
graphene::time::now();
if( _options->count("api-access") )
_apiaccess = fc::json::from_file( _options->at("api-access").as<boost::filesystem::path>() )
.as<api_access>();
else
{
@ -258,6 +349,11 @@ namespace detail {
return it->second;
}
void set_api_access_info(const string& username, api_access_info&& permissions)
{
_apiaccess.permission_map.insert(std::make_pair(username, std::move(permissions)));
}
/**
* If delegate has the item, the network has no need to fetch it.
*/
@ -281,11 +377,40 @@ namespace detail {
*
* @throws exception if error validating the item, otherwise the item is safe to broadcast on.
*/
virtual bool handle_block(const graphene::net::block_message& blk_msg, bool sync_mode) override
virtual bool handle_block(const graphene::net::block_message& blk_msg, bool sync_mode,
std::vector<fc::uint160_t>& contained_transaction_message_ids) override
{ try {
ilog("Got block #${n} from network", ("n", blk_msg.block.block_num()));
auto latency = graphene::time::now() - blk_msg.block.timestamp;
if (!sync_mode || blk_msg.block.block_num() % 10000 == 0)
{
const auto& witness = blk_msg.block.witness(*_chain_db);
const auto& witness_account = witness.witness_account(*_chain_db);
ilog("Got block #${n} with time ${t} from network with latency of ${l} ms from ${w}", ("t",blk_msg.block.timestamp)("n", blk_msg.block.block_num())("l", (latency.count()/1000))("w",witness_account.name) );
}
try {
return _chain_db->push_block(blk_msg.block, _is_block_producer? database::skip_nothing : database::skip_transaction_signatures);
// TODO: in the case where this block is valid but on a fork that's too old for us to switch to,
// you can help the network code out by throwing a block_older_than_undo_history exception.
// when the net code sees that, it will stop trying to push blocks from that chain, but
// leave that peer connected so that they can get sync blocks from us
bool result = _chain_db->push_block(blk_msg.block, _is_block_producer ? database::skip_nothing : database::skip_transaction_signatures);
// the block was accepted, so we now know all of the transactions contained in the block
if (!sync_mode)
{
// if we're not in sync mode, there's a chance we will be seeing some transactions
// included in blocks before we see the free-floating transaction itself. If that
// happens, there's no reason to fetch the transactions, so construct a list of the
// transaction message ids we no longer need.
// during sync, it is unlikely that we'll see any old
for (const processed_transaction& transaction : blk_msg.block.transactions)
{
graphene::net::trx_message transaction_message(transaction);
contained_transaction_message_ids.push_back(graphene::net::message(transaction_message).id());
}
}
return result;
} catch( const fc::exception& e ) {
elog("Error when pushing block:\n${e}", ("e", e.to_detail_string()));
throw;
@ -298,12 +423,24 @@ namespace detail {
}
} FC_CAPTURE_AND_RETHROW( (blk_msg)(sync_mode) ) }
virtual bool handle_transaction(const graphene::net::trx_message& trx_msg, bool sync_mode) override
virtual void handle_transaction(const graphene::net::trx_message& transaction_message) override
{ try {
ilog("Got transaction from network");
_chain_db->push_transaction( trx_msg.trx );
return false;
} FC_CAPTURE_AND_RETHROW( (trx_msg)(sync_mode) ) }
_chain_db->push_transaction( transaction_message.trx );
} FC_CAPTURE_AND_RETHROW( (transaction_message) ) }
virtual void handle_message(const message& message_to_process) override
{
// not a transaction, not a block
FC_THROW( "Invalid Message Type" );
}
bool is_included_block(const block_id_type& block_id)
{
uint32_t block_num = block_header::num_from_id(block_id);
block_id_type block_id_in_preferred_chain = _chain_db->get_block_id_for_num(block_num);
return block_id == block_id_in_preferred_chain;
}
/**
* Assuming all data elements are ordered in some way, this method should
@ -314,37 +451,48 @@ namespace detail {
* in our blockchain after the last item returned in the result,
* or 0 if the result contains the last item in the blockchain
*/
virtual std::vector<item_hash_t> get_item_ids(uint32_t item_type,
const std::vector<item_hash_t>& blockchain_synopsis,
uint32_t& remaining_item_count,
uint32_t limit) override
virtual std::vector<item_hash_t> get_block_ids(const std::vector<item_hash_t>& blockchain_synopsis,
uint32_t& remaining_item_count,
uint32_t limit) override
{ try {
FC_ASSERT( item_type == graphene::net::block_message_type );
vector<block_id_type> result;
vector<block_id_type> result;
remaining_item_count = 0;
if( _chain_db->head_block_num() == 0 )
return result;
result.reserve(limit);
block_id_type last_known_block_id;
auto itr = blockchain_synopsis.rbegin();
while( itr != blockchain_synopsis.rend() )
if (blockchain_synopsis.empty() ||
(blockchain_synopsis.size() == 1 && blockchain_synopsis[0] == block_id_type()))
{
if( _chain_db->is_known_block(*itr) || *itr == block_id_type() )
{
last_known_block_id = *itr;
break;
}
++itr;
}
// peer has sent us an empty synopsis meaning they have no blocks.
// A bug in old versions would cause them to send a synopsis containing block 000000000
// when they had an empty blockchain, so pretend they sent the right thing here.
for( auto num = block_header::num_from_id(last_known_block_id);
// do nothing, leave last_known_block_id set to zero
}
else
{
bool found_a_block_in_synopsis = false;
for (const item_hash_t& block_id_in_synopsis : boost::adaptors::reverse(blockchain_synopsis))
if (block_id_in_synopsis == block_id_type() ||
(_chain_db->is_known_block(block_id_in_synopsis) && is_included_block(block_id_in_synopsis)))
{
last_known_block_id = block_id_in_synopsis;
found_a_block_in_synopsis = true;
break;
}
if (!found_a_block_in_synopsis)
FC_THROW_EXCEPTION(graphene::net::peer_is_on_an_unreachable_fork, "Unable to provide a list of blocks starting at any of the blocks in peer's synopsis");
}
for( uint32_t num = block_header::num_from_id(last_known_block_id);
num <= _chain_db->head_block_num() && result.size() < limit;
++num )
if( num > 0 )
result.push_back(_chain_db->get_block_id_for_num(num));
if( block_header::num_from_id(result.back()) < _chain_db->head_block_num() )
if( !result.empty() && block_header::num_from_id(result.back()) < _chain_db->head_block_num() )
remaining_item_count = _chain_db->head_block_num() - block_header::num_from_id(result.back());
return result;
@ -355,7 +503,7 @@ namespace detail {
*/
virtual message get_item(const item_id& id) override
{ try {
ilog("Request for item ${id}", ("id", id));
// ilog("Request for item ${id}", ("id", id));
if( id.item_type == graphene::net::block_message_type )
{
auto opt_block = _chain_db->fetch_block_by_id(id.item_hash);
@ -369,44 +517,184 @@ namespace detail {
return trx_message( _chain_db->get_recent_transaction( id.item_hash ) );
} FC_CAPTURE_AND_RETHROW( (id) ) }
virtual fc::sha256 get_chain_id()const override
virtual chain_id_type get_chain_id()const override
{
return _chain_db->get_global_properties().chain_id;
return _chain_db->get_chain_id();
}
/**
* Returns a synopsis of the blockchain used for syncing.
* This consists of a list of selected item hashes from our current preferred
* blockchain, exponentially falling off into the past. Horrible explanation.
* Returns a synopsis of the blockchain used for syncing. This consists of a list of
* block hashes at intervals exponentially increasing towards the genesis block.
* When syncing to a peer, the peer uses this data to determine if we're on the same
* fork as they are, and if not, what blocks they need to send us to get us on their
* fork.
*
* If the blockchain is empty, it will return the empty list.
* If the blockchain has one block, it will return a list containing just that block.
* If it contains more than one block:
* the first element in the list will be the hash of the genesis block
* the second element will be the hash of an item at the half way point in the blockchain
* the third will be ~3/4 of the way through the block chain
* the fourth will be at ~7/8...
* &c.
* the last item in the list will be the hash of the most recent block on our preferred chain
* In the over-simplified case, this is a straighforward synopsis of our current
* preferred blockchain; when we first connect up to a peer, this is what we will be sending.
* It looks like this:
* If the blockchain is empty, it will return the empty list.
* If the blockchain has one block, it will return a list containing just that block.
* If it contains more than one block:
* the first element in the list will be the hash of the highest numbered block that
* we cannot undo
* the second element will be the hash of an item at the half way point in the undoable
* segment of the blockchain
* the third will be ~3/4 of the way through the undoable segment of the block chain
* the fourth will be at ~7/8...
* &c.
* the last item in the list will be the hash of the most recent block on our preferred chain
* so if the blockchain had 26 blocks labeled a - z, the synopsis would be:
* a n u x z
* the idea being that by sending a small (<30) number of block ids, we can summarize a huge
* blockchain. The block ids are more dense near the end of the chain where because we are
* more likely to be almost in sync when we first connect, and forks are likely to be short.
* If the peer we're syncing with in our example is on a fork that started at block 'v',
* then they will reply to our synopsis with a list of all blocks starting from block 'u',
* the last block they know that we had in common.
*
* In the real code, there are several complications.
*
* First, as an optimization, we don't usually send a synopsis of the entire blockchain, we
* send a synopsis of only the segment of the blockchain that we have undo data for. If their
* fork doesn't build off of something in our undo history, we would be unable to switch, so there's
* no reason to fetch the blocks.
*
* Second, when a peer replies to our initial synopsis and gives us a list of the blocks they think
* we are missing, they only send a chunk of a few thousand blocks at once. After we get those
* block ids, we need to request more blocks by sending another synopsis (we can't just say "send me
* the next 2000 ids" because they may have switched forks themselves and they don't track what
* they've sent us). For faster performance, we want to get a fairly long list of block ids first,
* then start downloading the blocks.
* The peer doesn't handle these follow-up block id requests any different from the initial request;
* it treats the synopsis we send as our blockchain and bases its response entirely off that. So to
* get the response we want (the next chunk of block ids following the last one they sent us, or,
* failing that, the shortest fork off of the last list of block ids they sent), we need to construct
* a synopsis as if our blockchain was made up of:
* 1. the blocks in our block chain up to the fork point (if there is a fork) or the head block (if no fork)
* 2. the blocks we've already pushed from their fork (if there's a fork)
* 3. the block ids they've previously sent us
* Segment 3 is handled in the p2p code, it just tells us the number of blocks it has (in
* number_of_blocks_after_reference_point) so we can leave space in the synopsis for them.
* We're responsible for constructing the synopsis of Segments 1 and 2 from our active blockchain and
* fork database. The reference_point parameter is the last block from that peer that has been
* successfully pushed to the blockchain, so that tells us whether the peer is on a fork or on
* the main chain.
*/
virtual std::vector<item_hash_t> get_blockchain_synopsis(uint32_t item_type,
const graphene::net::item_hash_t& reference_point,
virtual std::vector<item_hash_t> get_blockchain_synopsis(const item_hash_t& reference_point,
uint32_t number_of_blocks_after_reference_point) override
{ try {
std::vector<item_hash_t> result;
result.reserve(30);
auto head_block_num = _chain_db->head_block_num();
result.push_back(_chain_db->head_block_id());
auto current = 1;
while( current < head_block_num )
{
result.push_back(_chain_db->get_block_id_for_num(head_block_num - current));
current = current*2;
}
std::reverse( result.begin(), result.end() );
idump((reference_point)(number_of_blocks_after_reference_point)(result));
return result;
} FC_CAPTURE_AND_RETHROW( (reference_point)(number_of_blocks_after_reference_point) ) }
std::vector<item_hash_t> synopsis;
synopsis.reserve(30);
uint32_t high_block_num;
uint32_t non_fork_high_block_num;
uint32_t low_block_num = _chain_db->last_non_undoable_block_num();
std::vector<block_id_type> fork_history;
if (reference_point != item_hash_t())
{
// the node is asking for a summary of the block chain up to a specified
// block, which may or may not be on a fork
// for now, assume it's not on a fork
if (is_included_block(reference_point))
{
// reference_point is a block we know about and is on the main chain
uint32_t reference_point_block_num = block_header::num_from_id(reference_point);
assert(reference_point_block_num > 0);
high_block_num = reference_point_block_num;
non_fork_high_block_num = high_block_num;
if (reference_point_block_num < low_block_num)
{
// we're on the same fork (at least as far as reference_point) but we've passed
// reference point and could no longer undo that far if we diverged after that
// block. This should probably only happen due to a race condition where
// the network thread calls this function, and then immediately pushes a bunch of blocks,
// then the main thread finally processes this function.
// with the current framework, there's not much we can do to tell the network
// thread what our current head block is, so we'll just pretend that
// our head is actually the reference point.
// this *may* enable us to fetch blocks that we're unable to push, but that should
// be a rare case (and correctly handled)
low_block_num = reference_point_block_num;
}
}
else
{
// block is a block we know about, but it is on a fork
try
{
fork_history = _chain_db->get_block_ids_on_fork(reference_point);
// returns a vector where the last element is the common ancestor with the preferred chain,
// and the first element is the reference point you passed in
assert(fork_history.size() >= 2);
if( fork_history.front() != reference_point )
{
edump( (fork_history)(reference_point) );
assert(fork_history.front() == reference_point);
}
block_id_type last_non_fork_block = fork_history.back();
fork_history.pop_back(); // remove the common ancestor
boost::reverse(fork_history);
if (last_non_fork_block == block_id_type()) // if the fork goes all the way back to genesis (does graphene's fork db allow this?)
non_fork_high_block_num = 0;
else
non_fork_high_block_num = block_header::num_from_id(last_non_fork_block);
high_block_num = non_fork_high_block_num + fork_history.size();
assert(high_block_num == block_header::num_from_id(fork_history.back()));
}
catch (const fc::exception& e)
{
// unable to get fork history for some reason. maybe not linked?
// we can't return a synopsis of its chain
elog("Unable to construct a blockchain synopsis for reference hash ${hash}: ${exception}", ("hash", reference_point)("exception", e));
throw;
}
if (non_fork_high_block_num < low_block_num)
{
wlog("Unable to generate a usable synopsis because the peer we're generating it for forked too long ago "
"(our chains diverge after block #${non_fork_high_block_num} but only undoable to block #${low_block_num})",
("low_block_num", low_block_num)
("non_fork_high_block_num", non_fork_high_block_num));
FC_THROW_EXCEPTION(graphene::net::block_older_than_undo_history, "Peer is are on a fork I'm unable to switch to");
}
}
}
else
{
// no reference point specified, summarize the whole block chain
high_block_num = _chain_db->head_block_num();
non_fork_high_block_num = high_block_num;
if (high_block_num == 0)
return synopsis; // we have no blocks
}
// at this point:
// low_block_num is the block before the first block we can undo,
// non_fork_high_block_num is the block before the fork (if the peer is on a fork, or otherwise it is the same as high_block_num)
// high_block_num is the block number of the reference block, or the end of the chain if no reference provided
// true_high_block_num is the ending block number after the network code appends any item ids it
// knows about that we don't
uint32_t true_high_block_num = high_block_num + number_of_blocks_after_reference_point;
do
{
// for each block in the synopsis, figure out where to pull the block id from.
// if it's <= non_fork_high_block_num, we grab it from the main blockchain;
// if it's not, we pull it from the fork history
if (low_block_num <= non_fork_high_block_num)
synopsis.push_back(_chain_db->get_block_id_for_num(low_block_num));
else
synopsis.push_back(fork_history[low_block_num - non_fork_high_block_num - 1]);
low_block_num += (true_high_block_num - low_block_num + 2) / 2;
}
while (low_block_num <= high_block_num);
idump((synopsis));
return synopsis;
} FC_CAPTURE_AND_RETHROW() }
/**
* Call this after the call to handle_message succeeds.
@ -465,6 +753,11 @@ namespace detail {
// notify GUI or something cool
}
uint8_t get_current_block_interval_in_seconds() const override
{
return _chain_db->get_global_properties().parameters.block_interval;
}
application* _self;
fc::path _data_dir;
@ -512,6 +805,7 @@ void application::set_program_options(boost::program_options::options_descriptio
("server-pem,p", bpo::value<string>()->implicit_value("server.pem"), "The TLS certificate file for this server")
("server-pem-password,P", bpo::value<string>()->implicit_value(""), "Password for this certificate")
("genesis-json", bpo::value<boost::filesystem::path>(), "File to read Genesis State from")
("dbg-init-key", bpo::value<string>(), "Block signing key to use for init witnesses, overrides genesis file")
("api-access", bpo::value<boost::filesystem::path>(), "JSON file specifying API permissions")
;
command_line_options.add(configuration_file_options);
@ -522,6 +816,7 @@ void application::set_program_options(boost::program_options::options_descriptio
"invalid file is found, it will be replaced with an example Genesis State.")
("replay-blockchain", "Rebuild object graph by replaying all blocks")
("resync-blockchain", "Delete all blocks and re-sync with network from scratch")
("genesis-timestamp", bpo::value<uint32_t>(), "Replace timestamp from genesis.json with current time plus this many seconds (experts only!)")
;
command_line_options.add(_cli_options);
configuration_file_options.add(_cfg_options);
@ -588,6 +883,11 @@ optional< api_access_info > application::get_api_access_info( const string& user
return my->get_api_access_info( username );
}
void application::set_api_access_info(const string& username, api_access_info&& permissions)
{
my->set_api_access_info(username, std::move(permissions));
}
bool application::is_finished_syncing() const
{
return my->_is_finished_syncing;
@ -604,6 +904,11 @@ void application::shutdown_plugins()
entry.second->plugin_shutdown();
return;
}
void application::shutdown()
{
if( my->_chain_db )
my->_chain_db->close();
}
void application::initialize_plugins( const boost::program_options::variables_map& options )
{

File diff suppressed because it is too large Load diff

208
libraries/app/impacted.cpp Normal file
View file

@ -0,0 +1,208 @@
/*
* Copyright (c) 2015, Cryptonomex, Inc.
* All rights reserved.
*
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
* are permitted until September 8, 2015, provided that the following conditions are met:
*
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <graphene/chain/protocol/authority.hpp>
#include <graphene/app/impacted.hpp>
namespace graphene { namespace app {
using namespace fc;
using namespace graphene::chain;
// TODO: Review all of these, especially no-ops
struct get_impacted_account_visitor
{
flat_set<account_id_type>& _impacted;
get_impacted_account_visitor( flat_set<account_id_type>& impact ):_impacted(impact) {}
typedef void result_type;
void operator()( const transfer_operation& op )
{
_impacted.insert( op.to );
}
void operator()( const limit_order_create_operation& op ) {}
void operator()( const limit_order_cancel_operation& op )
{
_impacted.insert( op.fee_paying_account );
}
void operator()( const call_order_update_operation& op ) {}
void operator()( const fill_order_operation& op )
{
_impacted.insert( op.account_id );
}
void operator()( const account_create_operation& op )
{
_impacted.insert( op.registrar );
_impacted.insert( op.referrer );
add_authority_accounts( _impacted, op.owner );
add_authority_accounts( _impacted, op.active );
}
void operator()( const account_update_operation& op )
{
_impacted.insert( op.account );
if( op.owner )
add_authority_accounts( _impacted, *(op.owner) );
if( op.active )
add_authority_accounts( _impacted, *(op.active) );
}
void operator()( const account_whitelist_operation& op )
{
_impacted.insert( op.account_to_list );
}
void operator()( const account_upgrade_operation& op ) {}
void operator()( const account_transfer_operation& op )
{
_impacted.insert( op.new_owner );
}
void operator()( const asset_create_operation& op ) {}
void operator()( const asset_update_operation& op )
{
if( op.new_issuer )
_impacted.insert( *(op.new_issuer) );
}
void operator()( const asset_update_bitasset_operation& op ) {}
void operator()( const asset_update_feed_producers_operation& op ) {}
void operator()( const asset_issue_operation& op )
{
_impacted.insert( op.issue_to_account );
}
void operator()( const asset_reserve_operation& op ) {}
void operator()( const asset_fund_fee_pool_operation& op ) {}
void operator()( const asset_settle_operation& op ) {}
void operator()( const asset_global_settle_operation& op ) {}
void operator()( const asset_publish_feed_operation& op ) {}
void operator()( const witness_create_operation& op )
{
_impacted.insert( op.witness_account );
}
void operator()( const witness_update_operation& op )
{
_impacted.insert( op.witness_account );
}
void operator()( const proposal_create_operation& op )
{
vector<authority> other;
for( const auto& proposed_op : op.proposed_ops )
operation_get_required_authorities( proposed_op.op, _impacted, _impacted, other );
for( auto& o : other )
add_authority_accounts( _impacted, o );
}
void operator()( const proposal_update_operation& op ) {}
void operator()( const proposal_delete_operation& op ) {}
void operator()( const withdraw_permission_create_operation& op )
{
_impacted.insert( op.authorized_account );
}
void operator()( const withdraw_permission_update_operation& op )
{
_impacted.insert( op.authorized_account );
}
void operator()( const withdraw_permission_claim_operation& op )
{
_impacted.insert( op.withdraw_from_account );
}
void operator()( const withdraw_permission_delete_operation& op )
{
_impacted.insert( op.authorized_account );
}
void operator()( const committee_member_create_operation& op )
{
_impacted.insert( op.committee_member_account );
}
void operator()( const committee_member_update_operation& op )
{
_impacted.insert( op.committee_member_account );
}
void operator()( const committee_member_update_global_parameters_operation& op ) {}
void operator()( const vesting_balance_create_operation& op )
{
_impacted.insert( op.owner );
}
void operator()( const vesting_balance_withdraw_operation& op ) {}
void operator()( const worker_create_operation& op ) {}
void operator()( const custom_operation& op ) {}
void operator()( const assert_operation& op ) {}
void operator()( const balance_claim_operation& op ) {}
void operator()( const override_transfer_operation& op )
{
_impacted.insert( op.to );
_impacted.insert( op.from );
_impacted.insert( op.issuer );
}
void operator()( const transfer_to_blind_operation& op )
{
_impacted.insert( op.from );
for( const auto& out : op.outputs )
add_authority_accounts( _impacted, out.owner );
}
void operator()( const blind_transfer_operation& op )
{
for( const auto& in : op.inputs )
add_authority_accounts( _impacted, in.owner );
for( const auto& out : op.outputs )
add_authority_accounts( _impacted, out.owner );
}
void operator()( const transfer_from_blind_operation& op )
{
_impacted.insert( op.to );
for( const auto& in : op.inputs )
add_authority_accounts( _impacted, in.owner );
}
void operator()( const asset_settle_cancel_operation& op )
{
_impacted.insert( op.account );
}
};
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )
{
get_impacted_account_visitor vtor = get_impacted_account_visitor( result );
op.visit( vtor );
}
void transaction_get_impacted_accounts( const transaction& tx, flat_set<account_id_type>& result )
{
for( const auto& op : tx.operations )
operation_get_impacted_accounts( op, result );
}
} }

View file

@ -16,288 +16,33 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/app/database_api.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/operation_history_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/market_evaluator.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/balance_object.hpp>
#include <graphene/net/node.hpp>
#include <graphene/market_history/market_history_plugin.hpp>
#include <graphene/net/node.hpp>
#include <fc/api.hpp>
#include <fc/optional.hpp>
#include <fc/network/ip.hpp>
#include <boost/container/flat_set.hpp>
#include <functional>
#include <map>
#include <string>
#include <vector>
namespace graphene { namespace app {
using namespace graphene::chain;
using namespace graphene::market_history;
using namespace std;
class application;
/**
* @brief The database_api class implements the RPC API for the chain database.
*
* This API exposes accessors on the database which query state tracked by a blockchain validating node. This API is
* read-only; all modifications to the database must be performed via transactions. Transactions are broadcast via
* the @ref network_broadcast_api.
*/
class database_api
{
public:
database_api(graphene::chain::database& db);
~database_api();
/**
* @brief Get the objects corresponding to the provided IDs
* @param ids IDs of the objects to retrieve
* @return The objects retrieved, in the order they are mentioned in ids
*
* If any of the provided IDs does not map to an object, a null variant is returned in its position.
*/
fc::variants get_objects(const vector<object_id_type>& ids)const;
/**
* @brief Retrieve a block header
* @param block_num Height of the block whose header should be returned
* @return header of the referenced block, or null if no matching block was found
*/
optional<block_header> get_block_header(uint32_t block_num)const;
/**
* @brief Retrieve a full, signed block
* @param block_num Height of the block to be returned
* @return the referenced block, or null if no matching block was found
*/
optional<signed_block> get_block(uint32_t block_num)const;
/**
* @brief used to fetch an individual transaction.
*/
processed_transaction get_transaction( uint32_t block_num, uint32_t trx_in_block )const;
/**
* @brief Retrieve the current @ref global_property_object
*/
global_property_object get_global_properties()const;
/**
* @brief Retrieve the current @ref dynamic_global_property_object
*/
dynamic_global_property_object get_dynamic_global_properties()const;
/**
* @brief Get a list of accounts by ID
* @param account_ids IDs of the accounts to retrieve
* @return The accounts corresponding to the provided IDs
*
* This function has semantics identical to @ref get_objects
*/
vector<optional<account_object>> get_accounts(const vector<account_id_type>& account_ids)const;
/**
* @brief Get a list of assets by ID
* @param asset_ids IDs of the assets to retrieve
* @return The assets corresponding to the provided IDs
*
* This function has semantics identical to @ref get_objects
*/
vector<optional<asset_object>> get_assets(const vector<asset_id_type>& asset_ids)const;
/**
* @brief Get a list of accounts by name
* @param account_names Names of the accounts to retrieve
* @return The accounts holding the provided names
*
* This function has semantics identical to @ref get_objects
*/
vector<optional<account_object>> lookup_account_names(const vector<string>& account_names)const;
/**
* @brief Get a list of assets by symbol
* @param asset_symbols Symbols of the assets to retrieve
* @return The assets corresponding to the provided symbols
*
* This function has semantics identical to @ref get_objects
*/
vector<optional<asset_object>> lookup_asset_symbols(const vector<string>& asset_symbols)const;
/**
* @brief Get an account's balances in various assets
* @param id ID of the account to get balances for
* @param assets IDs of the assets to get balances of
* @return Balances of the account
*/
vector<asset> get_account_balances(account_id_type id, const flat_set<asset_id_type>& assets)const;
/// Semantically equivalent to @ref get_account_balances, but takes a name instead of an ID.
vector<asset> get_named_account_balances(const std::string& name, const flat_set<asset_id_type>& assets)const;
/**
* @brief Get the total number of accounts registered with the blockchain
*/
uint64_t get_account_count()const;
/**
* @brief Get names and IDs for registered accounts
* @param lower_bound_name Lower bound of the first name to return
* @param limit Maximum number of results to return -- must not exceed 1000
* @return Map of account names to corresponding IDs
*/
map<string,account_id_type> lookup_accounts(const string& lower_bound_name, uint32_t limit)const;
/**
* @brief Get limit orders in a given market
* @param a ID of asset being sold
* @param b ID of asset being purchased
* @param limit Maximum number of orders to retrieve
* @return The limit orders, ordered from least price to greatest
*/
vector<limit_order_object> get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const;
/**
* @brief Get call orders in a given asset
* @param a ID of asset being called
* @param limit Maximum number of orders to retrieve
* @return The call orders, ordered from earliest to be called to latest
*/
vector<call_order_object> get_call_orders(asset_id_type a, uint32_t limit)const;
/**
* @brief Get forced settlement orders in a given asset
* @param a ID of asset being settled
* @param limit Maximum number of orders to retrieve
* @return The settle orders, ordered from earliest settlement date to latest
*/
vector<force_settlement_object> get_settle_orders(asset_id_type a, uint32_t limit)const;
/**
* @brief Get assets alphabetically by symbol name
* @param lower_bound_symbol Lower bound of symbol names to retrieve
* @param limit Maximum number of assets to fetch (must not exceed 100)
* @return The assets found
*/
vector<asset_object> list_assets(const string& lower_bound_symbol, uint32_t limit)const;
/**
* @brief Get the committee_member owned by a given account
* @param account The ID of the account whose committee_member should be retrieved
* @return The committee_member object, or null if the account does not have a committee_member
*/
fc::optional<committee_member_object> get_committee_member_by_account(account_id_type account)const;
/**
* @brief Get the witness owned by a given account
* @param account The ID of the account whose witness should be retrieved
* @return The witness object, or null if the account does not have a witness
*/
fc::optional<witness_object> get_witness_by_account(account_id_type account)const;
/**
* @brief Get the total number of witnesses registered with the blockchain
*/
uint64_t get_witness_count()const;
/**
* @brief Get names and IDs for registered witnesses
* @param lower_bound_name Lower bound of the first name to return
* @param limit Maximum number of results to return -- must not exceed 1000
* @return Map of witness names to corresponding IDs
*/
map<string, witness_id_type> lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const;
/**
* @brief Get names and IDs for registered committee_members
* @param lower_bound_name Lower bound of the first name to return
* @param limit Maximum number of results to return -- must not exceed 1000
* @return Map of committee_member names to corresponding IDs
*/
map<string, committee_member_id_type> lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const;
/**
* @brief Get a list of witnesses by ID
* @param witness_ids IDs of the witnesses to retrieve
* @return The witnesses corresponding to the provided IDs
*
* This function has semantics identical to @ref get_objects
*/
vector<optional<witness_object>> get_witnesses(const vector<witness_id_type>& witness_ids)const;
/**
* @brief Get a list of committee_members by ID
* @param committee_member_ids IDs of the committee_members to retrieve
* @return The committee_members corresponding to the provided IDs
*
* This function has semantics identical to @ref get_objects
*/
vector<optional<committee_member_object>> get_committee_members(const vector<committee_member_id_type>& committee_member_ids)const;
/**
* @group Push Notification Methods
* These methods may be used to get push notifications whenever an object or market is changed
* @{
*/
/**
* @brief Request notifications when some object(s) change
* @param callback Callback method which is called with the new version of a changed object
* @param ids The set of object IDs to watch
*/
bool subscribe_to_objects(const std::function<void(const fc::variant&)>& callback,
const vector<object_id_type>& ids);
/**
* @brief Stop receiving notifications for some object(s)
* @param ids The set of object IDs to stop watching
*/
bool unsubscribe_from_objects(const vector<object_id_type>& ids);
/**
* @brief Request notification when the active orders in the market between two assets changes
* @param callback Callback method which is called when the market changes
* @param a First asset ID
* @param b Second asset ID
*
* Callback will be passed a variant containing a vector<pair<operation, operation_result>>. The vector will
* contain, in order, the operations which changed the market, and their results.
*/
void subscribe_to_market(std::function<void(const variant&)> callback,
asset_id_type a, asset_id_type b);
/**
* @brief Unsubscribe from updates to a given market
* @param a First asset ID
* @param b Second asset ID
*/
void unsubscribe_from_market(asset_id_type a, asset_id_type b);
/**
* @brief Stop receiving any notifications
*
* This unsubscribes from all subscribed markets and objects.
*/
void cancel_all_subscriptions()
{ _subscriptions.clear(); _market_subscriptions.clear(); }
///@}
/// @brief Get a hexdump of the serialized binary form of a transaction
std::string get_transaction_hex(const signed_transaction& trx)const;
/**
* @return the set of proposed transactions relevant to the specified account id.
*/
vector<proposal_object> get_proposed_transactions( account_id_type id )const;
/**
* @return all accounts that referr to the key or account id in their owner or active authorities.
*/
vector<account_id_type> get_account_references( account_id_type account_id )const;
vector<account_id_type> get_key_references( public_key_type account_id )const;
/**
* @return all open margin positions for a given account id.
*/
vector<call_order_object> get_margin_positions( const account_id_type& id )const;
/** @return all unclaimed balance objects for a set of addresses */
vector<balance_object> get_balance_objects( const vector<address>& addrs )const;
private:
/** called every time a block is applied to report the objects that were changed */
void on_objects_changed(const vector<object_id_type>& ids);
void on_applied_block();
fc::future<void> _broadcast_changes_complete;
boost::signals2::scoped_connection _change_connection;
boost::signals2::scoped_connection _applied_block_connection;
map<object_id_type, std::function<void(const fc::variant&)> > _subscriptions;
map< pair<asset_id_type,asset_id_type>, std::function<void(const variant&)> > _market_subscriptions;
graphene::chain::database& _db;
};
/**
* @brief The history_api class implements the RPC API for account history
@ -322,17 +67,17 @@ namespace graphene { namespace app {
unsigned limit = 100,
operation_history_id_type start = operation_history_id_type())const;
vector<bucket_object> get_market_history( asset_id_type a, asset_id_type b, uint32_t bucket_seconds,
vector<bucket_object> get_market_history( asset_id_type a, asset_id_type b, uint32_t bucket_seconds,
fc::time_point_sec start, fc::time_point_sec end )const;
flat_set<uint32_t> get_market_history_buckets()const;
flat_set<uint32_t> get_market_history_buckets()const;
private:
application& _app;
application& _app;
};
/**
* @brief The network_broadcast_api class allows broadcasting of transactions.
*/
class network_broadcast_api
class network_broadcast_api : public std::enable_shared_from_this<network_broadcast_api>
{
public:
network_broadcast_api(application& a);
@ -362,6 +107,8 @@ namespace graphene { namespace app {
*/
void broadcast_transaction_with_callback( confirmation_callback cb, const signed_transaction& trx);
void broadcast_block( const signed_block& block );
/**
* @brief Not reflected, thus not accessible to API clients.
*
@ -393,17 +140,11 @@ namespace graphene { namespace app {
/**
* @brief Get status of all current connections to peers
* @brief Not reflected, thus not accessible to API clients.
*
* This function is registered to receive the applied_block
* signal from the chain database when a block is received.
* It then dispatches callbacks to clients who have requested
* to be notified when a particular txid is included in a block.
*/
std::vector<net::peer_status> get_connected_peers() const;
private:
application& _app;
application& _app;
};
/**
@ -440,7 +181,7 @@ namespace graphene { namespace app {
/// @brief Called to enable an API, not reflected.
void enable_api( const string& api_name );
application& _app;
application& _app;
optional< fc::api<database_api> > _database_api;
optional< fc::api<network_broadcast_api> > _network_broadcast_api;
optional< fc::api<network_node_api> > _network_node_api;
@ -452,44 +193,6 @@ namespace graphene { namespace app {
FC_REFLECT( graphene::app::network_broadcast_api::transaction_confirmation,
(id)(block_num)(trx_num)(trx) )
FC_API(graphene::app::database_api,
(get_objects)
(get_block_header)
(get_block)
(get_transaction)
(get_global_properties)
(get_dynamic_global_properties)
(get_accounts)
(get_assets)
(lookup_account_names)
(get_account_count)
(lookup_accounts)
(get_account_balances)
(get_named_account_balances)
(lookup_asset_symbols)
(get_limit_orders)
(get_call_orders)
(get_settle_orders)
(list_assets)
(get_committee_member_by_account)
(get_witnesses)
(get_committee_members)
(get_witness_by_account)
(get_witness_count)
(lookup_witness_accounts)
(lookup_committee_member_accounts)
(subscribe_to_objects)
(unsubscribe_from_objects)
(subscribe_to_market)
(unsubscribe_from_market)
(cancel_all_subscriptions)
(get_transaction_hex)
(get_proposed_transactions)
(get_account_references)
(get_key_references)
(get_margin_positions)
(get_balance_objects)
)
FC_API(graphene::app::history_api,
(get_account_history)
(get_market_history)
@ -498,6 +201,7 @@ FC_API(graphene::app::history_api,
FC_API(graphene::app::network_broadcast_api,
(broadcast_transaction)
(broadcast_transaction_with_callback)
(broadcast_block)
)
FC_API(graphene::app::network_node_api,
(add_node)

View file

@ -76,6 +76,7 @@ namespace graphene { namespace app {
void set_block_production(bool producing_blocks);
fc::optional< api_access_info > get_api_access_info( const string& username )const;
void set_api_access_info(const string& username, api_access_info&& permissions);
bool is_finished_syncing()const;
/// Emitted when syncing finishes (is_finished_syncing will return true)

View file

@ -0,0 +1,549 @@
/*
* Copyright (c) 2015, Cryptonomex, Inc.
* All rights reserved.
*
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
* are permitted until September 8, 2015, provided that the following conditions are met:
*
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/app/full_account.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/balance_object.hpp>
#include <graphene/chain/chain_property_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/market_evaluator.hpp>
#include <graphene/chain/operation_history_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/worker_evaluator.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/confidential_evaluator.hpp>
#include <fc/api.hpp>
#include <fc/optional.hpp>
#include <fc/variant_object.hpp>
#include <fc/network/ip.hpp>
#include <boost/container/flat_set.hpp>
#include <functional>
#include <map>
#include <memory>
#include <vector>
namespace graphene { namespace app {
using namespace graphene::chain;
using namespace std;
class database_api_impl;
/**
* @brief The database_api class implements the RPC API for the chain database.
*
* This API exposes accessors on the database which query state tracked by a blockchain validating node. This API is
* read-only; all modifications to the database must be performed via transactions. Transactions are broadcast via
* the @ref network_broadcast_api.
*/
class database_api
{
public:
database_api(graphene::chain::database& db);
~database_api();
/////////////
// Objects //
/////////////
/**
* @brief Get the objects corresponding to the provided IDs
* @param ids IDs of the objects to retrieve
* @return The objects retrieved, in the order they are mentioned in ids
*
* If any of the provided IDs does not map to an object, a null variant is returned in its position.
*/
fc::variants get_objects(const vector<object_id_type>& ids)const;
///////////////////
// Subscriptions //
///////////////////
void set_subscribe_callback( std::function<void(const variant&)> cb, bool clear_filter );
void set_pending_transaction_callback( std::function<void(const variant&)> cb );
void set_block_applied_callback( std::function<void(const variant& block_id)> cb );
/**
* @brief Stop receiving any notifications
*
* This unsubscribes from all subscribed markets and objects.
*/
void cancel_all_subscriptions();
/////////////////////////////
// Blocks and transactions //
/////////////////////////////
/**
* @brief Retrieve a block header
* @param block_num Height of the block whose header should be returned
* @return header of the referenced block, or null if no matching block was found
*/
optional<block_header> get_block_header(uint32_t block_num)const;
/**
* @brief Retrieve a full, signed block
* @param block_num Height of the block to be returned
* @return the referenced block, or null if no matching block was found
*/
optional<signed_block> get_block(uint32_t block_num)const;
/**
* @brief used to fetch an individual transaction.
*/
processed_transaction get_transaction( uint32_t block_num, uint32_t trx_in_block )const;
/////////////
// Globals //
/////////////
/**
* @brief Retrieve the @ref chain_property_object associated with the chain
*/
chain_property_object get_chain_properties()const;
/**
* @brief Retrieve the current @ref global_property_object
*/
global_property_object get_global_properties()const;
/**
* @brief Retrieve compile-time constants
*/
fc::variant_object get_config()const;
/**
* @brief Get the chain ID
*/
chain_id_type get_chain_id()const;
/**
* @brief Retrieve the current @ref dynamic_global_property_object
*/
dynamic_global_property_object get_dynamic_global_properties()const;
//////////
// Keys //
//////////
vector<vector<account_id_type>> get_key_references( vector<public_key_type> key )const;
//////////////
// Accounts //
//////////////
/**
* @brief Get a list of accounts by ID
* @param account_ids IDs of the accounts to retrieve
* @return The accounts corresponding to the provided IDs
*
* This function has semantics identical to @ref get_objects
*/
vector<optional<account_object>> get_accounts(const vector<account_id_type>& account_ids)const;
/**
* @brief Fetch all objects relevant to the specified accounts and subscribe to updates
* @param callback Function to call with updates
* @param names_or_ids Each item must be the name or ID of an account to retrieve
* @return Map of string from @ref names_or_ids to the corresponding account
*
* This function fetches all relevant objects for the given accounts, and subscribes to updates to the given
* accounts. If any of the strings in @ref names_or_ids cannot be tied to an account, that input will be
* ignored. All other accounts will be retrieved and subscribed.
*
*/
std::map<string,full_account> get_full_accounts( const vector<string>& names_or_ids, bool subscribe );
optional<account_object> get_account_by_name( string name )const;
/**
* @return all accounts that referr to the key or account id in their owner or active authorities.
*/
vector<account_id_type> get_account_references( account_id_type account_id )const;
/**
* @brief Get a list of accounts by name
* @param account_names Names of the accounts to retrieve
* @return The accounts holding the provided names
*
* This function has semantics identical to @ref get_objects
*/
vector<optional<account_object>> lookup_account_names(const vector<string>& account_names)const;
/**
* @brief Get names and IDs for registered accounts
* @param lower_bound_name Lower bound of the first name to return
* @param limit Maximum number of results to return -- must not exceed 1000
* @return Map of account names to corresponding IDs
*/
map<string,account_id_type> lookup_accounts(const string& lower_bound_name, uint32_t limit)const;
//////////////
// Balances //
//////////////
/**
* @brief Get an account's balances in various assets
* @param id ID of the account to get balances for
* @param assets IDs of the assets to get balances of; if empty, get all assets account has a balance in
* @return Balances of the account
*/
vector<asset> get_account_balances(account_id_type id, const flat_set<asset_id_type>& assets)const;
/// Semantically equivalent to @ref get_account_balances, but takes a name instead of an ID.
vector<asset> get_named_account_balances(const std::string& name, const flat_set<asset_id_type>& assets)const;
/** @return all unclaimed balance objects for a set of addresses */
vector<balance_object> get_balance_objects( const vector<address>& addrs )const;
vector<asset> get_vested_balances( const vector<balance_id_type>& objs )const;
vector<vesting_balance_object> get_vesting_balances( account_id_type account_id )const;
/**
* @brief Get the total number of accounts registered with the blockchain
*/
uint64_t get_account_count()const;
////////////
// Assets //
////////////
/**
* @brief Get a list of assets by ID
* @param asset_ids IDs of the assets to retrieve
* @return The assets corresponding to the provided IDs
*
* This function has semantics identical to @ref get_objects
*/
vector<optional<asset_object>> get_assets(const vector<asset_id_type>& asset_ids)const;
/**
* @brief Get assets alphabetically by symbol name
* @param lower_bound_symbol Lower bound of symbol names to retrieve
* @param limit Maximum number of assets to fetch (must not exceed 100)
* @return The assets found
*/
vector<asset_object> list_assets(const string& lower_bound_symbol, uint32_t limit)const;
/**
* @brief Get a list of assets by symbol
* @param asset_symbols Symbols or stringified IDs of the assets to retrieve
* @return The assets corresponding to the provided symbols or IDs
*
* This function has semantics identical to @ref get_objects
*/
vector<optional<asset_object>> lookup_asset_symbols(const vector<string>& symbols_or_ids)const;
/////////////////////
// Markets / feeds //
/////////////////////
/**
* @brief Get limit orders in a given market
* @param a ID of asset being sold
* @param b ID of asset being purchased
* @param limit Maximum number of orders to retrieve
* @return The limit orders, ordered from least price to greatest
*/
vector<limit_order_object> get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const;
/**
* @brief Get call orders in a given asset
* @param a ID of asset being called
* @param limit Maximum number of orders to retrieve
* @return The call orders, ordered from earliest to be called to latest
*/
vector<call_order_object> get_call_orders(asset_id_type a, uint32_t limit)const;
/**
* @brief Get forced settlement orders in a given asset
* @param a ID of asset being settled
* @param limit Maximum number of orders to retrieve
* @return The settle orders, ordered from earliest settlement date to latest
*/
vector<force_settlement_object> get_settle_orders(asset_id_type a, uint32_t limit)const;
/**
* @return all open margin positions for a given account id.
*/
vector<call_order_object> get_margin_positions( const account_id_type& id )const;
/**
* @brief Request notification when the active orders in the market between two assets changes
* @param callback Callback method which is called when the market changes
* @param a First asset ID
* @param b Second asset ID
*
* Callback will be passed a variant containing a vector<pair<operation, operation_result>>. The vector will
* contain, in order, the operations which changed the market, and their results.
*/
void subscribe_to_market(std::function<void(const variant&)> callback,
asset_id_type a, asset_id_type b);
/**
* @brief Unsubscribe from updates to a given market
* @param a First asset ID
* @param b Second asset ID
*/
void unsubscribe_from_market(asset_id_type a, asset_id_type b);
///////////////
// Witnesses //
///////////////
/**
* @brief Get a list of witnesses by ID
* @param witness_ids IDs of the witnesses to retrieve
* @return The witnesses corresponding to the provided IDs
*
* This function has semantics identical to @ref get_objects
*/
vector<optional<witness_object>> get_witnesses(const vector<witness_id_type>& witness_ids)const;
/**
* @brief Get the witness owned by a given account
* @param account The ID of the account whose witness should be retrieved
* @return The witness object, or null if the account does not have a witness
*/
fc::optional<witness_object> get_witness_by_account(account_id_type account)const;
/**
* @brief Get names and IDs for registered witnesses
* @param lower_bound_name Lower bound of the first name to return
* @param limit Maximum number of results to return -- must not exceed 1000
* @return Map of witness names to corresponding IDs
*/
map<string, witness_id_type> lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const;
/**
* @brief Get the total number of witnesses registered with the blockchain
*/
uint64_t get_witness_count()const;
///////////////////////
// Committee members //
///////////////////////
/**
* @brief Get a list of committee_members by ID
* @param committee_member_ids IDs of the committee_members to retrieve
* @return The committee_members corresponding to the provided IDs
*
* This function has semantics identical to @ref get_objects
*/
vector<optional<committee_member_object>> get_committee_members(const vector<committee_member_id_type>& committee_member_ids)const;
/**
* @brief Get the committee_member owned by a given account
* @param account The ID of the account whose committee_member should be retrieved
* @return The committee_member object, or null if the account does not have a committee_member
*/
fc::optional<committee_member_object> get_committee_member_by_account(account_id_type account)const;
/**
* @brief Get names and IDs for registered committee_members
* @param lower_bound_name Lower bound of the first name to return
* @param limit Maximum number of results to return -- must not exceed 1000
* @return Map of committee_member names to corresponding IDs
*/
map<string, committee_member_id_type> lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const;
/// WORKERS
/**
* Return the worker objects associated with this account.
*/
vector<worker_object> get_workers_by_account(account_id_type account)const;
///////////
// Votes //
///////////
/**
* @brief Given a set of votes, return the objects they are voting for.
*
* This will be a mixture of committee_member_object, witness_objects, and worker_objects
*
* The results will be in the same order as the votes. Null will be returned for
* any vote ids that are not found.
*/
vector<variant> lookup_vote_ids( const vector<vote_id_type>& votes )const;
////////////////////////////
// Authority / validation //
////////////////////////////
/// @brief Get a hexdump of the serialized binary form of a transaction
std::string get_transaction_hex(const signed_transaction& trx)const;
/**
* This API will take a partially signed transaction and a set of public keys that the owner has the ability to sign for
* and return the minimal subset of public keys that should add signatures to the transaction.
*/
set<public_key_type> get_required_signatures( const signed_transaction& trx, const flat_set<public_key_type>& available_keys )const;
/**
* This method will return the set of all public keys that could possibly sign for a given transaction. This call can
* be used by wallets to filter their set of public keys to just the relevant subset prior to calling @ref get_required_signatures
* to get the minimum subset.
*/
set<public_key_type> get_potential_signatures( const signed_transaction& trx )const;
/**
* @return true of the @ref trx has all of the required signatures, otherwise throws an exception
*/
bool verify_authority( const signed_transaction& trx )const;
/**
* @return true if the signers have enough authority to authorize an account
*/
bool verify_account_authority( const string& name_or_id, const flat_set<public_key_type>& signers )const;
/**
* Validates a transaction against the current state without broadcasting it on the network.
*/
processed_transaction validate_transaction( const signed_transaction& trx )const;
/**
* For each operation calculate the required fee in the specified asset type. If the asset type does
* not have a valid core_exchange_rate
*/
vector<asset> get_required_fees( const vector<operation>& ops, asset_id_type id )const;
///////////////////////////
// Proposed transactions //
///////////////////////////
/**
* @return the set of proposed transactions relevant to the specified account id.
*/
vector<proposal_object> get_proposed_transactions( account_id_type id )const;
//////////////////////
// Blinded balances //
//////////////////////
/**
* @return the set of blinded balance objects by commitment ID
*/
vector<blinded_balance_object> get_blinded_balances( const flat_set<commitment_type>& commitments )const;
private:
std::shared_ptr< database_api_impl > my;
};
} }
FC_API(graphene::app::database_api,
// Objects
(get_objects)
// Subscriptions
(set_subscribe_callback)
(set_pending_transaction_callback)
(set_block_applied_callback)
(cancel_all_subscriptions)
// Blocks and transactions
(get_block_header)
(get_block)
(get_transaction)
// Globals
(get_chain_properties)
(get_global_properties)
(get_config)
(get_chain_id)
(get_dynamic_global_properties)
// Keys
(get_key_references)
// Accounts
(get_accounts)
(get_full_accounts)
(get_account_by_name)
(get_account_references)
(lookup_account_names)
(lookup_accounts)
(get_account_count)
// Balances
(get_account_balances)
(get_named_account_balances)
(get_balance_objects)
(get_vested_balances)
(get_vesting_balances)
// Assets
(get_assets)
(list_assets)
(lookup_asset_symbols)
// Markets / feeds
(get_limit_orders)
(get_call_orders)
(get_settle_orders)
(get_margin_positions)
(subscribe_to_market)
(unsubscribe_from_market)
// Witnesses
(get_witnesses)
(get_witness_by_account)
(lookup_witness_accounts)
(get_witness_count)
// Committee members
(get_committee_members)
(get_committee_member_by_account)
(lookup_committee_member_accounts)
// workers
(get_workers_by_account)
// Votes
(lookup_vote_ids)
// Authority / validation
(get_transaction_hex)
(get_required_signatures)
(get_potential_signatures)
(verify_authority)
(verify_account_authority)
(validate_transaction)
(get_required_fees)
// Proposed transactions
(get_proposed_transactions)
// Blinded balances
(get_blinded_balances)
)

View file

@ -0,0 +1,41 @@
#pragma once
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/market_evaluator.hpp>
namespace graphene { namespace app {
using namespace graphene::chain;
struct full_account
{
account_object account;
account_statistics_object statistics;
string registrar_name;
string referrer_name;
string lifetime_referrer_name;
vector<variant> votes;
optional<vesting_balance_object> cashback_balance;
vector<account_balance_object> balances;
vector<vesting_balance_object> vesting_balances;
vector<limit_order_object> limit_orders;
vector<call_order_object> call_orders;
vector<proposal_object> proposals;
};
} }
FC_REFLECT( graphene::app::full_account,
(account)
(statistics)
(registrar_name)
(referrer_name)
(lifetime_referrer_name)
(votes)
(cashback_balance)
(balances)
(vesting_balances)
(limit_orders)
(call_orders)
(proposals)
)

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2015, Cryptonomex, Inc.
* All rights reserved.
*
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
* are permitted until September 8, 2015, provided that the following conditions are met:
*
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <fc/container/flat.hpp>
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/chain/protocol/transaction.hpp>
#include <graphene/chain/protocol/types.hpp>
namespace graphene { namespace app {
void operation_get_impacted_accounts(
const graphene::chain::operation& op,
fc::flat_set<graphene::chain::account_id_type>& result );
void transaction_get_impacted_accounts(
const graphene::chain::transaction& tx,
fc::flat_set<graphene::chain::account_id_type>& result
);
} } // graphene::app

View file

@ -2,8 +2,25 @@ file(GLOB HEADERS "include/graphene/chain/*.hpp")
## SORT .cpp by most likely to change / break compile
add_library( graphene_chain
# As database takes the longest to compile, start it first
database.cpp
fork_database.cpp
# db_balance.cpp
# db_block.cpp
# db_debug.cpp
# db_getter.cpp
# db_init.cpp
# db_maint.cpp
# db_management.cpp
# db_market.cpp
# db_update.cpp
# db_witness_schedule.cpp
protocol/types.cpp
protocol/address.cpp
protocol/authority.cpp
protocol/asset.cpp
protocol/assert.cpp
protocol/account.cpp
@ -21,6 +38,11 @@ add_library( graphene_chain
protocol/transaction.cpp
protocol/block.cpp
protocol/fee_schedule.cpp
protocol/confidential.cpp
protocol/vote.cpp
genesis_state.cpp
get_config.cpp
pts_address.cpp
@ -37,29 +59,15 @@ add_library( graphene_chain
vesting_balance_evaluator.cpp
withdraw_permission_evaluator.cpp
worker_evaluator.cpp
confidential_evaluator.cpp
account_object.cpp
asset_object.cpp
proposal_object.cpp
vesting_balance_object.cpp
transaction_evaluation_state.cpp
fork_database.cpp
block_database.cpp
database.cpp
# db_balance.cpp
# db_block.cpp
# db_debug.cpp
# db_getter.cpp
# db_init.cpp
# db_maint.cpp
# db_management.cpp
# db_market.cpp
# db_update.cpp
# db_witness_schedule.cpp
${HEADERS}
)
@ -68,5 +76,13 @@ target_include_directories( graphene_chain
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
if(MSVC)
set_source_files_properties( db_init.cpp db_block.cpp database.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
set_source_files_properties( db_init.cpp db_block.cpp database.cpp block_database.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
endif(MSVC)
INSTALL( TARGETS
graphene_chain
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)

View file

@ -15,12 +15,32 @@
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <fc/smart_ref_impl.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/internal_exceptions.hpp>
#include <graphene/chain/account_evaluator.hpp>
#include <algorithm>
namespace graphene { namespace chain {
void verify_authority_accounts( const database& db, const authority& a )
{
const auto& chain_params = db.get_global_properties().parameters;
GRAPHENE_ASSERT(
a.num_auths() <= chain_params.maximum_authority_membership,
internal_verify_auth_max_auth_exceeded,
"Maximum authority membership exceeded" );
for( const auto& acnt : a.account_auths )
{
GRAPHENE_ASSERT( db.find_object( acnt.first ) != nullptr,
internal_verify_auth_account_not_found,
"Account ${a} specified in authority does not exist",
("a", acnt.first) );
}
}
void_result account_create_evaluator::do_evaluate( const account_create_operation& op )
{ try {
database& d = db();
@ -31,8 +51,13 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio
const auto& global_props = d.get_global_properties();
const auto& chain_params = global_props.parameters;
verify_authority_accounts( op.owner );
verify_authority_accounts( op.active );
try
{
verify_authority_accounts( d, op.owner );
verify_authority_accounts( d, op.active );
}
GRAPHENE_RECODE_EXC( internal_verify_auth_max_auth_exceeded, account_create_max_auth_exceeded )
GRAPHENE_RECODE_EXC( internal_verify_auth_account_not_found, account_create_auth_account_not_found )
uint32_t max_vote_id = global_props.next_available_vote_id;
FC_ASSERT( op.options.num_witness <= chain_params.maximum_witness_count );
@ -62,9 +87,6 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio
object_id_type account_create_evaluator::do_apply( const account_create_operation& o )
{ try {
const auto& stats_obj = db().create<account_statistics_object>( [&]( account_statistics_object& ){
});
const auto& new_acnt_object = db().create<account_object>( [&]( account_object& obj ){
obj.registrar = o.registrar;
obj.referrer = o.referrer;
@ -78,8 +100,8 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio
obj.name = o.name;
obj.owner = o.owner;
obj.active = o.active;
obj.statistics = stats_obj.id;
obj.options = o.options;
obj.statistics = db().create<account_statistics_object>([&](account_statistics_object& s){s.owner = obj.id;}).id;
});
const auto& dynamic_properties = db().get_dynamic_global_properties();
@ -87,13 +109,12 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio
++p.accounts_registered_this_interval;
});
/** TODO: update fee scaling for account creation...
const auto& global_properties = db().get_global_properties();
if( dynamic_properties.accounts_registered_this_interval %
global_properties.parameters.accounts_per_fee_scale == 0 )
db().modify(global_properties, [&dynamic_properties](global_property_object& p) {
p.parameters.current_fees.account_create_fee <<= p.parameters.account_fee_scale_bitshifts;
p.parameters.current_fees->get<account_create_operation>().basic_fee <<= p.parameters.account_fee_scale_bitshifts;
});
*/
return new_acnt_object.id;
} FC_CAPTURE_AND_RETHROW((o)) }
@ -103,10 +124,15 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio
{ try {
database& d = db();
const auto& chain_params = db().get_global_properties().parameters;
const auto& chain_params = d.get_global_properties().parameters;
if( o.owner ) verify_authority_accounts( *o.owner );
if( o.active ) verify_authority_accounts( *o.active );
try
{
if( o.owner ) verify_authority_accounts( d, *o.owner );
if( o.active ) verify_authority_accounts( d, *o.active );
}
GRAPHENE_RECODE_EXC( internal_verify_auth_max_auth_exceeded, account_update_max_auth_exceeded )
GRAPHENE_RECODE_EXC( internal_verify_auth_account_not_found, account_update_auth_account_not_found )
acnt = &o.account(d);

View file

@ -50,25 +50,6 @@ void account_balance_object::adjust_balance(const asset& delta)
balance += delta.amount;
}
uint16_t account_statistics_object::calculate_bulk_discount_percent(const chain_parameters& params) const
{
uint64_t bulk_discount_percent = 0;
if( lifetime_fees_paid >= params.bulk_discount_threshold_max )
bulk_discount_percent = params.max_bulk_discount_percent_of_fee;
else if(params.bulk_discount_threshold_max.value !=
params.bulk_discount_threshold_min.value)
{
bulk_discount_percent =
(params.max_bulk_discount_percent_of_fee *
(lifetime_fees_paid.value -
params.bulk_discount_threshold_min.value)) /
(params.bulk_discount_threshold_max.value -
params.bulk_discount_threshold_min.value);
}
assert( bulk_discount_percent <= GRAPHENE_100_PERCENT );
return bulk_discount_percent;
}
void account_statistics_object::process_fees(const account_object& a, database& d) const
{
@ -117,17 +98,6 @@ void account_statistics_object::process_fees(const account_object& a, database&
share_type vested_fee_subtotal(pending_vested_fees);
share_type vesting_cashback, vested_cashback;
if( lifetime_fees_paid > props.parameters.bulk_discount_threshold_min &&
a.is_member(d.head_block_time()) )
{
auto bulk_discount_rate = calculate_bulk_discount_percent(props.parameters);
vesting_cashback = cut_fee(vesting_fee_subtotal, bulk_discount_rate);
vesting_fee_subtotal -= vesting_cashback;
vested_cashback = cut_fee(vested_fee_subtotal, bulk_discount_rate);
vested_fee_subtotal -= vested_cashback;
}
pay_out_fees(a, vesting_fee_subtotal, true);
d.deposit_cashback(a, vesting_cashback, true);
pay_out_fees(a, vested_fee_subtotal, false);
@ -170,6 +140,13 @@ set<public_key_type> account_member_index::get_key_members(const account_object&
result.insert(auth.first);
return result;
}
set<address> account_member_index::get_address_members(const account_object& a)const
{
set<address> result;
for( auto auth : a.owner.address_auths )
result.insert(auth.first);
return result;
}
void account_member_index::object_inserted(const object& obj)
{
@ -183,6 +160,10 @@ void account_member_index::object_inserted(const object& obj)
auto key_members = get_key_members(a);
for( auto item : key_members )
account_to_key_memberships[item].insert(obj.id);
auto address_members = get_address_members(a);
for( auto item : address_members )
account_to_address_memberships[item].insert(obj.id);
}
void account_member_index::object_removed(const object& obj)
@ -193,6 +174,11 @@ void account_member_index::object_removed(const object& obj)
auto key_members = get_key_members(a);
for( auto item : key_members )
account_to_key_memberships[item].erase( obj.id );
auto address_members = get_address_members(a);
for( auto item : address_members )
account_to_address_memberships[item].erase( obj.id );
auto account_members = get_account_members(a);
for( auto item : account_members )
account_to_account_memberships[item].erase( obj.id );
@ -205,6 +191,7 @@ void account_member_index::about_to_modify(const object& before)
assert( dynamic_cast<const account_object*>(&before) ); // for debug only
const account_object& a = static_cast<const account_object&>(before);
before_key_members = get_key_members(a);
before_address_members = get_address_members(a);
before_account_members = get_account_members(a);
}
@ -212,47 +199,65 @@ void account_member_index::object_modified(const object& after)
{
assert( dynamic_cast<const account_object*>(&after) ); // for debug only
const account_object& a = static_cast<const account_object&>(after);
set<account_id_type> after_account_members = get_account_members(a);
{
vector<account_id_type> removed; removed.reserve(before_account_members.size());
std::set_difference(before_account_members.begin(), before_account_members.end(),
after_account_members.begin(), after_account_members.end(),
std::inserter(removed, removed.end()));
set<account_id_type> after_account_members = get_account_members(a);
vector<account_id_type> removed; removed.reserve(before_account_members.size());
std::set_difference(before_account_members.begin(), before_account_members.end(),
after_account_members.begin(), after_account_members.end(),
std::inserter(removed, removed.end()));
for( auto itr = removed.begin(); itr != removed.end(); ++itr )
account_to_account_memberships[*itr].erase(after.id);
for( auto itr = removed.begin(); itr != removed.end(); ++itr )
account_to_account_memberships[*itr].erase(after.id);
vector<object_id_type> added; added.reserve(after_account_members.size());
std::set_difference(after_account_members.begin(), after_account_members.end(),
before_account_members.begin(), before_account_members.end(),
std::inserter(added, added.end()));
vector<object_id_type> added; added.reserve(after_account_members.size());
std::set_difference(after_account_members.begin(), after_account_members.end(),
before_account_members.begin(), before_account_members.end(),
std::inserter(added, added.end()));
for( auto itr = added.begin(); itr != added.end(); ++itr )
account_to_account_memberships[*itr].insert(after.id);
for( auto itr = added.begin(); itr != added.end(); ++itr )
account_to_account_memberships[*itr].insert(after.id);
}
{
set<public_key_type> after_key_members = get_key_members(a);
vector<public_key_type> removed; removed.reserve(before_key_members.size());
std::set_difference(before_key_members.begin(), before_key_members.end(),
after_key_members.begin(), after_key_members.end(),
std::inserter(removed, removed.end()));
for( auto itr = removed.begin(); itr != removed.end(); ++itr )
account_to_key_memberships[*itr].erase(after.id);
vector<public_key_type> added; added.reserve(after_key_members.size());
std::set_difference(after_key_members.begin(), after_key_members.end(),
before_key_members.begin(), before_key_members.end(),
std::inserter(added, added.end()));
for( auto itr = added.begin(); itr != added.end(); ++itr )
account_to_key_memberships[*itr].insert(after.id);
}
{
set<public_key_type> after_key_members = get_key_members(a);
set<address> after_address_members = get_address_members(a);
vector<public_key_type> removed; removed.reserve(before_key_members.size());
std::set_difference(before_key_members.begin(), before_key_members.end(),
after_key_members.begin(), after_key_members.end(),
std::inserter(removed, removed.end()));
vector<address> removed; removed.reserve(before_address_members.size());
std::set_difference(before_address_members.begin(), before_address_members.end(),
after_address_members.begin(), after_address_members.end(),
std::inserter(removed, removed.end()));
for( auto itr = removed.begin(); itr != removed.end(); ++itr )
account_to_key_memberships[*itr].erase(after.id);
for( auto itr = removed.begin(); itr != removed.end(); ++itr )
account_to_address_memberships[*itr].erase(after.id);
vector<public_key_type> added; added.reserve(after_key_members.size());
std::set_difference(after_key_members.begin(), after_key_members.end(),
before_key_members.begin(), before_key_members.end(),
std::inserter(added, added.end()));
vector<address> added; added.reserve(after_address_members.size());
std::set_difference(after_address_members.begin(), after_address_members.end(),
before_address_members.begin(), before_address_members.end(),
std::inserter(added, added.end()));
for( auto itr = added.begin(); itr != added.end(); ++itr )
account_to_key_memberships[*itr].insert(after.id);
for( auto itr = added.begin(); itr != added.end(); ++itr )
account_to_address_memberships[*itr].insert(after.id);
}
}

View file

@ -17,6 +17,7 @@
*/
#include <graphene/chain/assert_evaluator.hpp>
#include <graphene/chain/block_summary_object.hpp>
#include <graphene/chain/database.hpp>
#include <sstream>
@ -38,6 +39,10 @@ struct predicate_evaluator
{
FC_ASSERT( p.asset_id(db).symbol == p.symbol );
}
void operator()( const block_id_predicate& p )const
{
FC_ASSERT( block_summary_id_type( block_header::num_from_id( p.id ) & 0xffff )(db).block_id == p.id );
}
};
void_result assert_evaluator::do_evaluate( const assert_operation& o )

View file

@ -144,7 +144,12 @@ void_result asset_reserve_evaluator::do_evaluate( const asset_reserve_operation&
database& d = db();
const asset_object& a = o.amount_to_reserve.asset_id(d);
FC_ASSERT( !a.is_market_issued() );
GRAPHENE_ASSERT(
!a.is_market_issued(),
asset_reserve_invalid_on_mia,
"Cannot reserve ${sym} because it is a market-issued asset",
("sym", a.symbol)
);
from_account = &o.payer(d);
@ -218,10 +223,14 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o)
}
}
//There must be no bits set in o.permissions which are unset in a.issuer_permissions.
// new issuer_permissions must be subset of old issuer permissions
FC_ASSERT(!(o.new_options.issuer_permissions & ~a.options.issuer_permissions),
"Cannot reinstate previously revoked issuer permissions on an asset.");
// changed flags must be subset of old issuer permissions
FC_ASSERT(!((o.new_options.flags ^ a.options.flags) & ~a.options.issuer_permissions),
"Flag change is forbidden by issuer permissions");
asset_to_update = &a;
FC_ASSERT( o.issuer == a.issuer, "", ("o.issuer", o.issuer)("a.issuer", a.issuer) );
@ -448,7 +457,7 @@ void_result asset_publish_feeds_evaluator::do_evaluate(const asset_publish_feed_
FC_ASSERT( !bitasset.has_settlement(), "No further feeds may be published after a settlement event" );
FC_ASSERT(o.feed.settlement_price.quote.asset_id == bitasset.options.short_backing_asset);
//Verify that the publisher is authoritative to publish a feed
if( base.issuer == account_id_type() )
if( base.issuer == GRAPHENE_COMMITTEE_ACCOUNT )
{
//It's a committee_member-fed asset. Verify that publisher is an active committee_member or witness.
FC_ASSERT(d.get(GRAPHENE_COMMITTEE_ACCOUNT).active.account_auths.count(o.publisher) ||

View file

@ -110,9 +110,7 @@ asset asset_object::amount_from_string(string amount_string) const
share_type satoshis = 0;
share_type scaled_precision = 1;
for( uint8_t i = 0; i < precision; ++i )
scaled_precision *= 10;
share_type scaled_precision = asset::scaled_precision( precision );
const auto decimal_pos = amount_string.find( '.' );
const string lhs = amount_string.substr( negative_found, decimal_pos );

View file

@ -18,6 +18,7 @@
#include <graphene/chain/block_database.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <fc/io/raw.hpp>
#include <fc/smart_ref_impl.hpp>
namespace graphene { namespace chain {
@ -67,8 +68,14 @@ void block_database::flush()
_block_num_to_pos.flush();
}
void block_database::store( const block_id_type& id, const signed_block& b )
void block_database::store( const block_id_type& _id, const signed_block& b )
{
block_id_type id = _id;
if( id == block_id_type() )
{
id = b.id();
elog( "id argument of block_database::store() was not initialized for block ${id}", ("id", id) );
}
auto num = block_header::num_from_id(id);
_block_num_to_pos.seekp( sizeof( index_entry ) * num );
index_entry e;
@ -102,6 +109,9 @@ void block_database::remove( const block_id_type& id )
bool block_database::contains( const block_id_type& id )const
{
if( id == block_id_type() )
return false;
index_entry e;
auto index_pos = sizeof(e)*block_header::num_from_id(id);
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
@ -110,11 +120,12 @@ bool block_database::contains( const block_id_type& id )const
_block_num_to_pos.seekg( index_pos );
_block_num_to_pos.read( (char*)&e, sizeof(e) );
return e.block_id == id;
return e.block_id == id && e.block_size > 0;
}
block_id_type block_database::fetch_block_id( uint32_t block_num )const
{
assert( block_num != 0 );
index_entry e;
auto index_pos = sizeof(e)*block_num;
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
@ -124,6 +135,7 @@ block_id_type block_database::fetch_block_id( uint32_t block_num )const
_block_num_to_pos.seekg( index_pos );
_block_num_to_pos.read( (char*)&e, sizeof(e) );
FC_ASSERT( e.block_id != block_id_type(), "Empty block_id in block_database (maybe corrupt on disk?)" );
return e.block_id;
}
@ -144,7 +156,8 @@ optional<signed_block> block_database::fetch_optional( const block_id_type& id )
vector<char> data( e.block_size );
_blocks.seekg( e.block_pos );
_blocks.read( data.data(), e.block_size );
if (e.block_size)
_blocks.read( data.data(), e.block_size );
auto result = fc::raw::unpack<signed_block>(data);
FC_ASSERT( result.id() == e.block_id );
return result;
@ -199,9 +212,11 @@ optional<signed_block> block_database::last()const
_block_num_to_pos.seekg( -sizeof(index_entry), _block_num_to_pos.end );
_block_num_to_pos.read( (char*)&e, sizeof(e) );
while( e.block_size == 0 && _blocks.tellg() > 0 )
uint64_t pos = _block_num_to_pos.tellg();
while( e.block_size == 0 && pos > 0 )
{
_block_num_to_pos.seekg( -sizeof(index_entry), _block_num_to_pos.cur );
pos -= sizeof(index_entry);
_block_num_to_pos.seekg( pos );
_block_num_to_pos.read( (char*)&e, sizeof(e) );
}
@ -222,4 +237,40 @@ optional<signed_block> block_database::last()const
}
return optional<signed_block>();
}
optional<block_id_type> block_database::last_id()const
{
try
{
index_entry e;
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
if( _block_num_to_pos.tellp() < sizeof(index_entry) )
return optional<block_id_type>();
_block_num_to_pos.seekg( -sizeof(index_entry), _block_num_to_pos.end );
_block_num_to_pos.read( (char*)&e, sizeof(e) );
uint64_t pos = _block_num_to_pos.tellg();
while( e.block_size == 0 && pos > 0 )
{
pos -= sizeof(index_entry);
_block_num_to_pos.seekg( pos );
_block_num_to_pos.read( (char*)&e, sizeof(e) );
}
if( e.block_size == 0 )
return optional<block_id_type>();
return e.block_id;
}
catch (const fc::exception&)
{
}
catch (const std::exception&)
{
}
return optional<block_id_type>();
}
} }

View file

@ -20,8 +20,11 @@
#include <graphene/chain/database.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <graphene/chain/protocol/vote.hpp>
#include <graphene/chain/transaction_evaluation_state.hpp>
#include <fc/smart_ref_impl.hpp>
namespace graphene { namespace chain {
void_result committee_member_create_evaluator::do_evaluate( const committee_member_create_operation& op )
@ -34,7 +37,7 @@ object_id_type committee_member_create_evaluator::do_apply( const committee_memb
{ try {
vote_id_type vote_id;
db().modify(db().get_global_properties(), [&vote_id](global_property_object& p) {
vote_id = p.get_next_vote_id(vote_id_type::committee);
vote_id = get_next_vote_id(p, vote_id_type::committee);
});
const auto& new_del_object = db().create<committee_member_object>( [&]( committee_member_object& obj ){
@ -45,7 +48,24 @@ object_id_type committee_member_create_evaluator::do_apply( const committee_memb
return new_del_object.id;
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result committee_member_update_evaluator::do_evaluate( const committee_member_update_operation& op )
{ try {
FC_ASSERT(db().get(op.committee_member).committee_member_account == op.committee_member_account);
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result committee_member_update_evaluator::do_apply( const committee_member_update_operation& op )
{ try {
database& _db = db();
_db.modify(
_db.get(op.committee_member),
[&]( committee_member_object& com )
{
if( op.new_url.valid() )
com.url = *op.new_url;
});
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result committee_member_update_global_parameters_evaluator::do_evaluate(const committee_member_update_global_parameters_operation& o)
{ try {

View file

@ -0,0 +1,137 @@
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/protocol/confidential.hpp>
#include <graphene/chain/confidential_evaluator.hpp>
#include <graphene/chain/database.hpp>
namespace graphene { namespace chain {
void_result transfer_to_blind_evaluator::do_evaluate( const transfer_to_blind_operation& o )
{ try {
const auto& d = db();
const auto& atype = o.amount.asset_id(db());
FC_ASSERT( atype.allow_confidential() );
FC_ASSERT( !atype.is_transfer_restricted() );
FC_ASSERT( !atype.enforce_white_list() );
for( const auto& out : o.outputs )
{
for( const auto& a : out.owner.account_auths )
a.first(d); // verify all accounts exist and are valid
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result transfer_to_blind_evaluator::do_apply( const transfer_to_blind_operation& o )
{ try {
db().adjust_balance( o.from, -o.amount );
const auto& add = o.amount.asset_id(db()).dynamic_asset_data_id(db()); // verify fee is a legit asset
db().modify( add, [&]( asset_dynamic_data_object& obj ){
obj.confidential_supply += o.amount.amount;
FC_ASSERT( obj.confidential_supply >= 0 );
});
for( const auto& out : o.outputs )
{
db().create<blinded_balance_object>( [&]( blinded_balance_object& obj ){
obj.asset_id = o.amount.asset_id;
obj.owner = out.owner;
obj.commitment = out.commitment;
});
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result transfer_from_blind_evaluator::do_evaluate( const transfer_from_blind_operation& o )
{ try {
const auto& d = db();
o.fee.asset_id(d); // verify fee is a legit asset
const auto& bbi = d.get_index_type<blinded_balance_index>();
const auto& cidx = bbi.indices().get<by_commitment>();
for( const auto& in : o.inputs )
{
auto itr = cidx.find( in.commitment );
FC_ASSERT( itr != cidx.end() );
FC_ASSERT( itr->asset_id == o.fee.asset_id );
FC_ASSERT( itr->owner == in.owner );
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result transfer_from_blind_evaluator::do_apply( const transfer_from_blind_operation& o )
{ try {
db().adjust_balance( o.fee_payer(), o.fee );
db().adjust_balance( o.to, o.amount );
const auto& bbi = db().get_index_type<blinded_balance_index>();
const auto& cidx = bbi.indices().get<by_commitment>();
for( const auto& in : o.inputs )
{
auto itr = cidx.find( in.commitment );
FC_ASSERT( itr != cidx.end() );
db().remove( *itr );
}
const auto& add = o.amount.asset_id(db()).dynamic_asset_data_id(db()); // verify fee is a legit asset
db().modify( add, [&]( asset_dynamic_data_object& obj ){
obj.confidential_supply -= o.amount.amount + o.fee.amount;
FC_ASSERT( obj.confidential_supply >= 0 );
});
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result blind_transfer_evaluator::do_evaluate( const blind_transfer_operation& o )
{ try {
const auto& d = db();
o.fee.asset_id(db()); // verify fee is a legit asset
const auto& bbi = db().get_index_type<blinded_balance_index>();
const auto& cidx = bbi.indices().get<by_commitment>();
for( const auto& out : o.outputs )
{
for( const auto& a : out.owner.account_auths )
a.first(d); // verify all accounts exist and are valid
}
for( const auto& in : o.inputs )
{
auto itr = cidx.find( in.commitment );
GRAPHENE_ASSERT( itr != cidx.end(), blind_transfer_unknown_commitment, "", ("commitment",in.commitment) );
FC_ASSERT( itr->asset_id == o.fee.asset_id );
FC_ASSERT( itr->owner == in.owner );
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result blind_transfer_evaluator::do_apply( const blind_transfer_operation& o )
{ try {
db().adjust_balance( o.fee_payer(), o.fee ); // deposit the fee to the temp account
const auto& bbi = db().get_index_type<blinded_balance_index>();
const auto& cidx = bbi.indices().get<by_commitment>();
for( const auto& in : o.inputs )
{
auto itr = cidx.find( in.commitment );
GRAPHENE_ASSERT( itr != cidx.end(), blind_transfer_unknown_commitment, "", ("commitment",in.commitment) );
db().remove( *itr );
}
for( const auto& out : o.outputs )
{
db().create<blinded_balance_object>( [&]( blinded_balance_object& obj ){
obj.asset_id = o.fee.asset_id;
obj.owner = out.owner;
obj.commitment = out.commitment;
});
}
const auto& add = o.fee.asset_id(db()).dynamic_asset_data_id(db());
db().modify( add, [&]( asset_dynamic_data_object& obj ){
obj.confidential_supply -= o.fee.amount;
FC_ASSERT( obj.confidential_supply >= 0 );
});
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
} } // graphene::chain

View file

@ -21,6 +21,7 @@
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/witness_object.hpp>
namespace graphene { namespace chain {
@ -71,19 +72,52 @@ void database::adjust_balance(account_id_type account, asset delta )
} FC_CAPTURE_AND_RETHROW( (account)(delta) ) }
void database::adjust_balance(const account_object& account, asset delta )
optional< vesting_balance_id_type > database::deposit_lazy_vesting(
const optional< vesting_balance_id_type >& ovbid,
share_type amount, uint32_t req_vesting_seconds,
account_id_type req_owner,
bool require_vesting )
{
adjust_balance( account.id, delta);
}
if( amount == 0 )
return optional< vesting_balance_id_type >();
void database::adjust_core_in_orders( const account_object& acnt, asset delta )
{
if( delta.asset_id == asset_id_type(0) && delta.amount != 0 )
fc::time_point_sec now = head_block_time();
while( true )
{
modify( acnt.statistics(*this), [&](account_statistics_object& stat){
stat.total_core_in_orders += delta.amount;
});
if( !ovbid.valid() )
break;
const vesting_balance_object& vbo = (*ovbid)(*this);
if( vbo.owner != req_owner )
break;
if( vbo.policy.which() != vesting_policy::tag< cdd_vesting_policy >::value )
break;
if( vbo.policy.get< cdd_vesting_policy >().vesting_seconds != req_vesting_seconds )
break;
modify( vbo, [&]( vesting_balance_object& _vbo )
{
if( require_vesting )
_vbo.deposit(now, amount);
else
_vbo.deposit_vested(now, amount);
} );
return optional< vesting_balance_id_type >();
}
const vesting_balance_object& vbo = create< vesting_balance_object >( [&]( vesting_balance_object& _vbo )
{
_vbo.owner = req_owner;
_vbo.balance = amount;
cdd_vesting_policy policy;
policy.vesting_seconds = req_vesting_seconds;
policy.coin_seconds_earned = require_vesting ? 0 : amount.value * policy.vesting_seconds;
policy.coin_seconds_earned_last_update = now;
_vbo.policy = policy;
} );
return vbo.id;
}
void database::deposit_cashback(const account_object& acct, share_type amount, bool require_vesting)
@ -105,46 +139,43 @@ void database::deposit_cashback(const account_object& acct, share_type amount, b
return;
}
uint32_t global_vesting_seconds = get_global_properties().parameters.cashback_vesting_period_seconds;
fc::time_point_sec now = head_block_time();
optional< vesting_balance_id_type > new_vbid = deposit_lazy_vesting(
acct.cashback_vb,
amount,
get_global_properties().parameters.cashback_vesting_period_seconds,
acct.id,
require_vesting );
while( true )
if( new_vbid.valid() )
{
if( !acct.cashback_vb.valid() )
break;
const vesting_balance_object& cashback_vb = (*acct.cashback_vb)(*this);
if( cashback_vb.policy.which() != vesting_policy::tag< cdd_vesting_policy >::value )
break;
if( cashback_vb.policy.get< cdd_vesting_policy >().vesting_seconds != global_vesting_seconds )
break;
modify( cashback_vb, [&]( vesting_balance_object& obj )
modify( acct, [&]( account_object& _acct )
{
if( require_vesting )
obj.deposit(now, amount);
else
obj.deposit_vested(now, amount);
_acct.cashback_vb = *new_vbid;
} );
return;
}
const vesting_balance_object& cashback_vb = create< vesting_balance_object >( [&]( vesting_balance_object& obj )
return;
}
void database::deposit_witness_pay(const witness_object& wit, share_type amount)
{
if( amount == 0 )
return;
optional< vesting_balance_id_type > new_vbid = deposit_lazy_vesting(
wit.pay_vb,
amount,
get_global_properties().parameters.witness_pay_vesting_seconds,
wit.witness_account,
true );
if( new_vbid.valid() )
{
obj.owner = acct.id;
obj.balance = amount;
cdd_vesting_policy policy;
policy.vesting_seconds = global_vesting_seconds;
policy.coin_seconds_earned = require_vesting? 0 : amount.value * policy.vesting_seconds;
policy.coin_seconds_earned_last_update = now;
obj.policy = policy;
} );
modify( acct, [&]( account_object& _acct )
{
_acct.cashback_vb = cashback_vb.id;
} );
modify( wit, [&]( witness_object& _wit )
{
_wit.pay_vb = *new_vbid;
} );
}
return;
}

View file

@ -17,6 +17,7 @@
*/
#include <graphene/chain/database.hpp>
#include <graphene/chain/db_with.hpp>
#include <graphene/chain/block_summary_object.hpp>
#include <graphene/chain/global_property_object.hpp>
@ -25,6 +26,7 @@
#include <graphene/chain/transaction_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <graphene/chain/exceptions.hpp>
namespace graphene { namespace chain {
@ -74,6 +76,24 @@ const signed_transaction& database::get_recent_transaction(const transaction_id_
return itr->trx;
}
std::vector<block_id_type> database::get_block_ids_on_fork(block_id_type head_of_fork) const
{
pair<fork_database::branch_type, fork_database::branch_type> branches = _fork_db.fetch_branch_from(head_block_id(), head_of_fork);
if( !((branches.first.back()->previous_id() == branches.second.back()->previous_id())) )
{
edump( (head_of_fork)
(head_block_id())
(branches.first.size())
(branches.second.size()) );
assert(branches.first.back()->previous_id() == branches.second.back()->previous_id());
}
std::vector<block_id_type> result;
for (const item_ptr& fork_block : branches.second)
result.emplace_back(fork_block->id);
result.emplace_back(branches.first.back()->previous_id());
return result;
}
/**
* Push block "may fail" in which case every partial change is unwound. After
* push block is successful the block is appended to the chain database on disk.
@ -82,12 +102,16 @@ const signed_transaction& database::get_recent_transaction(const transaction_id_
*/
bool database::push_block(const signed_block& new_block, uint32_t skip)
{
//idump((new_block.block_num())(new_block.id())(new_block.timestamp)(new_block.previous));
bool result;
with_skip_flags( skip, [&]()
detail::with_skip_flags( *this, skip, [&]()
{
result = _push_block( new_block );
} );
detail::without_pending_transactions( *this, std::move(_pending_tx),
[&]()
{
result = _push_block(new_block);
});
});
return result;
}
@ -96,7 +120,10 @@ bool database::_push_block(const signed_block& new_block)
uint32_t skip = get_node_properties().skip_flags;
if( !(skip&skip_fork_db) )
{
auto new_head = _fork_db.push_block(new_block);
/// TODO: if the block is greater than the head block and before the next maitenance interval
// verify that the block signer is in the current set of active witnesses.
shared_ptr<fork_item> new_head = _fork_db.push_block(new_block);
//If the head block from the longest chain does not build off of the current head, we need to switch forks.
if( new_head->data.previous != head_block_id() )
{
@ -104,7 +131,8 @@ bool database::_push_block(const signed_block& new_block)
//Only switch forks if new_head is actually higher than head
if( new_head->data.block_num() > head_block_num() )
{
auto branches = _fork_db.fetch_branch_from(new_head->data.id(), _pending_block.previous);
wlog( "Switching to fork: ${id}", ("id",new_head->data.id()) );
auto branches = _fork_db.fetch_branch_from(new_head->data.id(), head_block_id());
// pop blocks until we hit the forked block
while( head_block_id() != branches.second.back()->data.previous )
@ -115,7 +143,7 @@ bool database::_push_block(const signed_block& new_block)
{
optional<fc::exception> except;
try {
auto session = _undo_db.start_undo_session();
undo_database::session session = _undo_db.start_undo_session();
apply_block( (*ritr)->data, skip );
_block_id_to_block.store( (*ritr)->id, (*ritr)->data );
session.commit();
@ -152,11 +180,6 @@ bool database::_push_block(const signed_block& new_block)
}
}
// If there is a pending block session, then the database state is dirty with pending transactions.
// Drop the pending session to reset the database to a clean head block state.
// TODO: Preserve pending transactions, and re-apply any which weren't included in the new block.
clear_pending();
try {
auto session = _undo_db.start_undo_session();
apply_block(new_block, skip);
@ -183,7 +206,7 @@ bool database::_push_block(const signed_block& new_block)
processed_transaction database::push_transaction( const signed_transaction& trx, uint32_t skip )
{ try {
processed_transaction result;
with_skip_flags( skip, [&]()
detail::with_skip_flags( *this, skip, [&]()
{
result = _push_transaction( trx );
} );
@ -192,42 +215,40 @@ processed_transaction database::push_transaction( const signed_transaction& trx,
processed_transaction database::_push_transaction( const signed_transaction& trx )
{
uint32_t skip = get_node_properties().skip_flags;
// If this is the first transaction pushed after applying a block, start a new undo session.
// This allows us to quickly rewind to the clean state of the head block, in case a new block arrives.
if( !_pending_block_session ) _pending_block_session = _undo_db.start_undo_session();
auto session = _undo_db.start_undo_session();
auto processed_trx = _apply_transaction( trx );
_pending_block.transactions.push_back(processed_trx);
if( !_pending_tx_session.valid() )
_pending_tx_session = _undo_db.start_undo_session();
FC_ASSERT( (skip & skip_block_size_check) ||
fc::raw::pack_size(_pending_block) <= get_global_properties().parameters.maximum_block_size );
// Create a temporary undo session as a child of _pending_tx_session.
// The temporary session will be discarded by the destructor if
// _apply_transaction fails. If we make it to merge(), we
// apply the changes.
auto temp_session = _undo_db.start_undo_session();
auto processed_trx = _apply_transaction( trx );
_pending_tx.push_back(processed_trx);
notify_changed_objects();
// The transaction applied successfully. Merge its changes into the pending block session.
session.merge();
temp_session.merge();
// notify anyone listening to pending transactions
on_pending_transaction( trx );
return processed_trx;
}
processed_transaction database::validate_transaction( const signed_transaction& trx )
{
auto session = _undo_db.start_undo_session();
return _apply_transaction( trx );
}
processed_transaction database::push_proposal(const proposal_object& proposal)
{
transaction_evaluation_state eval_state(this);
eval_state._is_proposed_trx = true;
//Inject the approving authorities into the transaction eval state
std::transform(proposal.required_active_approvals.begin(),
proposal.required_active_approvals.end(),
std::inserter(eval_state.approved_by, eval_state.approved_by.begin()),
[]( account_id_type id ) {
return std::make_pair(id, authority::active);
});
std::transform(proposal.required_owner_approvals.begin(),
proposal.required_owner_approvals.end(),
std::inserter(eval_state.approved_by, eval_state.approved_by.begin()),
[]( account_id_type id ) {
return std::make_pair(id, authority::owner);
});
eval_state.operation_results.reserve(proposal.proposed_transaction.operations.size());
processed_transaction ptrx(proposal.proposed_transaction);
eval_state._trx = &ptrx;
@ -250,9 +271,9 @@ signed_block database::generate_block(
)
{
signed_block result;
with_skip_flags( skip, [&]()
detail::with_skip_flags( *this, skip, [&]()
{
result = _generate_block( when, witness_id, block_signing_private_key );
result = _generate_block( when, witness_id, block_signing_private_key, true );
} );
return result;
}
@ -260,13 +281,15 @@ signed_block database::generate_block(
signed_block database::_generate_block(
fc::time_point_sec when,
witness_id_type witness_id,
const fc::ecc::private_key& block_signing_private_key
const fc::ecc::private_key& block_signing_private_key,
bool retry_on_failure
)
{
try {
uint32_t skip = get_node_properties().skip_flags;
uint32_t slot_num = get_slot_at_time( when );
witness_id_type scheduled_witness = get_scheduled_witness( slot_num ).first;
FC_ASSERT( slot_num > 0 );
witness_id_type scheduled_witness = get_scheduled_witness( slot_num );
FC_ASSERT( scheduled_witness == witness_id );
const auto& witness_obj = witness_id(*this);
@ -274,51 +297,77 @@ signed_block database::_generate_block(
if( !(skip & skip_witness_signature) )
FC_ASSERT( witness_obj.signing_key == block_signing_private_key.get_public_key() );
_pending_block.timestamp = when;
static const size_t max_block_header_size = fc::raw::pack_size( signed_block_header() ) + 4;
auto maximum_block_size = get_global_properties().parameters.maximum_block_size;
size_t total_block_size = max_block_header_size;
// Genesis witnesses start with a default initial secret
if( witness_obj.next_secret_hash == secret_hash_type::hash( secret_hash_type() ) )
signed_block pending_block;
//
// The following code throws away existing pending_tx_session and
// rebuilds it by re-applying pending transactions.
//
// This rebuild is necessary because pending transactions' validity
// and semantics may have changed since they were received, because
// time-based semantics are evaluated based on the current block
// time. These changes can only be reflected in the database when
// the value of the "when" variable is known, which means we need to
// re-apply pending transactions in this method.
//
_pending_tx_session.reset();
_pending_tx_session = _undo_db.start_undo_session();
uint64_t postponed_tx_count = 0;
// pop pending state (reset to head block state)
for( const processed_transaction& tx : _pending_tx )
{
_pending_block.previous_secret = secret_hash_type();
}
else
{
secret_hash_type::encoder last_enc;
fc::raw::pack( last_enc, block_signing_private_key );
fc::raw::pack( last_enc, witness_obj.previous_secret );
_pending_block.previous_secret = last_enc.result();
}
size_t new_total_size = total_block_size + fc::raw::pack_size( tx );
secret_hash_type::encoder next_enc;
fc::raw::pack( next_enc, block_signing_private_key );
fc::raw::pack( next_enc, _pending_block.previous_secret );
_pending_block.next_secret_hash = secret_hash_type::hash(next_enc.result());
_pending_block.transaction_merkle_root = _pending_block.calculate_merkle_root();
_pending_block.witness = witness_id;
if( !(skip & skip_witness_signature) ) _pending_block.sign( block_signing_private_key );
FC_ASSERT( fc::raw::pack_size(_pending_block) <= get_global_properties().parameters.maximum_block_size );
signed_block tmp = _pending_block;
tmp.transaction_merkle_root = tmp.calculate_merkle_root();
_pending_block.transactions.clear();
bool failed = false;
try { push_block( tmp, skip ); } catch ( const fc::exception& e ) { failed = true; }
if( failed )
{
for( const auto& trx : tmp.transactions )
// postpone transaction if it would make block too big
if( new_total_size >= maximum_block_size )
{
try {
push_transaction( trx, skip );
} catch ( const fc::exception& e ) {
wlog( "Transaction is no longer valid: ${trx}", ("trx",trx) );
}
postponed_tx_count++;
continue;
}
try
{
auto temp_session = _undo_db.start_undo_session();
processed_transaction ptx = _apply_transaction( tx );
temp_session.merge();
// We have to recompute pack_size(ptx) because it may be different
// than pack_size(tx) (i.e. if one or more results increased
// their size)
total_block_size += fc::raw::pack_size( ptx );
pending_block.transactions.push_back( ptx );
}
catch ( const fc::exception& e )
{
// Do nothing, transaction will not be re-applied
wlog( "Transaction was not processed while generating block due to ${e}", ("e", e) );
wlog( "The transaction was ${t}", ("t", tx) );
}
return _generate_block( when, witness_id, block_signing_private_key );
}
return tmp;
if( postponed_tx_count > 0 )
{
wlog( "Postponed ${n} transactions due to block size limit", ("n", postponed_tx_count) );
}
_pending_tx_session.reset();
pending_block.previous = head_block_id();
pending_block.timestamp = when;
pending_block.transaction_merkle_root = pending_block.calculate_merkle_root();
pending_block.witness = witness_id;
if( !(skip & skip_witness_signature) )
pending_block.sign( block_signing_private_key );
FC_ASSERT( fc::raw::pack_size(pending_block) <= get_global_properties().parameters.maximum_block_size );
push_block( pending_block, skip );
return pending_block;
} FC_CAPTURE_AND_RETHROW( (witness_id) ) }
/**
@ -327,18 +376,23 @@ signed_block database::_generate_block(
*/
void database::pop_block()
{ try {
_pending_block_session.reset();
_block_id_to_block.remove( _pending_block.previous );
_pending_tx_session.reset();
auto head_id = head_block_id();
optional<signed_block> head_block = fetch_block_by_id( head_id );
GRAPHENE_ASSERT( head_block.valid(), pop_empty_chain, "there are no blocks to pop" );
pop_undo();
_pending_block.previous = head_block_id();
_pending_block.timestamp = head_block_time();
_block_id_to_block.remove( head_id );
_fork_db.pop_block();
_popped_tx.insert( _popped_tx.begin(), head_block->transactions.begin(), head_block->transactions.end() );
} FC_CAPTURE_AND_RETHROW() }
void database::clear_pending()
{ try {
_pending_block.transactions.clear();
_pending_block_session.reset();
assert( (_pending_tx.size() == 0) || _pending_tx_session.valid() );
_pending_tx.clear();
_pending_tx_session.reset();
} FC_CAPTURE_AND_RETHROW() }
uint32_t database::push_applied_operation( const operation& op )
@ -367,21 +421,17 @@ const vector<operation_history_object>& database::get_applied_operations() const
void database::apply_block( const signed_block& next_block, uint32_t skip )
{
auto block_num = next_block.block_num();
if( _checkpoints.size() )
if( _checkpoints.size() && _checkpoints.rbegin()->second != block_id_type() )
{
auto itr = _checkpoints.find( block_num );
if( itr != _checkpoints.end() )
FC_ASSERT( next_block.id() == itr->second, "Block did not match checkpoint", ("checkpoint",*itr)("block_id",next_block.id()) );
auto last = _checkpoints.rbegin();
if( last->first >= block_num )
{
// WE CAN SKIP ALMOST EVERYTHING
skip = ~0;
}
if( _checkpoints.rbegin()->first >= block_num )
skip = ~0;// WE CAN SKIP ALMOST EVERYTHING
}
with_skip_flags( skip, [&]()
detail::with_skip_flags( *this, skip, [&]()
{
_apply_block( next_block );
} );
@ -398,6 +448,7 @@ void database::_apply_block( const signed_block& next_block )
const witness_object& signing_witness = validate_block_header(skip, next_block);
const auto& global_props = get_global_properties();
const auto& dynamic_global_props = get<dynamic_global_property_object>(dynamic_global_property_id_type());
bool maint_needed = (dynamic_global_props.next_maintenance_time <= next_block.timestamp);
_current_block_num = next_block.block_num();
_current_trx_in_block = 0;
@ -414,15 +465,11 @@ void database::_apply_block( const signed_block& next_block )
++_current_trx_in_block;
}
update_witness_schedule(next_block);
update_global_dynamic_data(next_block);
update_signing_witness(signing_witness, next_block);
auto current_block_interval = global_props.parameters.block_interval;
// Are we at the maintenance interval?
if( dynamic_global_props.next_maintenance_time <= next_block.timestamp )
// This will update _pending_block.timestamp if the block interval has changed
if( maint_needed )
perform_chain_maintenance(next_block, global_props);
create_block_summary(next_block);
@ -432,27 +479,44 @@ void database::_apply_block( const signed_block& next_block )
update_expired_feeds();
update_withdraw_permissions();
// n.b., update_maintenance_flag() happens this late
// because get_slot_time() / get_slot_at_time() is needed above
// TODO: figure out if we could collapse this function into
// update_global_dynamic_data() as perhaps these methods only need
// to be called for header validation?
update_maintenance_flag( maint_needed );
update_witness_schedule();
// notify observers that the block has been applied
applied_block( next_block ); //emit
_applied_ops.clear();
notify_changed_objects();
update_pending_block(next_block, current_block_interval);
} FC_CAPTURE_AND_RETHROW( (next_block.block_num()) ) }
void database::notify_changed_objects()
{
const auto& head_undo = _undo_db.head();
vector<object_id_type> changed_ids; changed_ids.reserve(head_undo.old_values.size());
for( const auto& item : head_undo.old_values ) changed_ids.push_back(item.first);
changed_objects(changed_ids);
}
{ try {
if( _undo_db.enabled() )
{
const auto& head_undo = _undo_db.head();
vector<object_id_type> changed_ids; changed_ids.reserve(head_undo.old_values.size());
for( const auto& item : head_undo.old_values ) changed_ids.push_back(item.first);
for( const auto& item : head_undo.new_ids ) changed_ids.push_back(item);
vector<const object*> removed;
removed.reserve( head_undo.removed.size() );
for( const auto& item : head_undo.removed )
{
changed_ids.push_back( item.first );
removed.emplace_back( item.second.get() );
}
changed_objects(changed_ids);
}
} FC_CAPTURE_AND_RETHROW() }
processed_transaction database::apply_transaction(const signed_transaction& trx, uint32_t skip)
{
processed_transaction result;
with_skip_flags(skip, [&]()
detail::with_skip_flags( *this, skip, [&]()
{
result = _apply_transaction(trx);
});
@ -464,6 +528,7 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx
uint32_t skip = get_node_properties().skip_flags;
trx.validate();
auto& trx_idx = get_mutable_index_type<transaction_index>();
const chain_id_type& chain_id = get_chain_id();
auto trx_id = trx.id();
FC_ASSERT( (skip & skip_transaction_dupe_check) ||
trx_idx.indices().get<by_trx_id>().find(trx_id) == trx_idx.indices().get<by_trx_id>().end() );
@ -471,122 +536,36 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx
const chain_parameters& chain_parameters = get_global_properties().parameters;
eval_state._trx = &trx;
//This check is used only if this transaction has an absolute expiration time.
if( !(skip & skip_transaction_signatures) && trx.relative_expiration == 0 )
if( !(skip & (skip_transaction_signatures | skip_authority_check) ) )
{
eval_state._sigs.reserve(trx.signatures.size());
for( const auto& sig : trx.signatures )
{
FC_ASSERT( eval_state._sigs.insert(std::make_pair(public_key_type(fc::ecc::public_key(sig, trx.digest())),
false)).second,
"Multiple signatures by same key detected" );
}
auto get_active = [&]( account_id_type id ) { return &id(*this).active; };
auto get_owner = [&]( account_id_type id ) { return &id(*this).owner; };
trx.verify_authority( chain_id, get_active, get_owner, get_global_properties().parameters.max_authority_depth );
}
//If we're skipping tapos check, but not dupe check, assume all transactions have maximum expiration time.
fc::time_point_sec trx_expiration = _pending_block.timestamp + chain_parameters.maximum_time_until_expiration;
//Skip all manner of expiration and TaPoS checking if we're on block 1; It's impossible that the transaction is
//expired, and TaPoS makes no sense as no blocks exist.
if( BOOST_LIKELY(head_block_num() > 0) )
{
if( !(skip & skip_tapos_check) && trx.relative_expiration != 0 )
if( !(skip & skip_tapos_check) )
{
//Check the TaPoS reference and expiration time
//Remember that the TaPoS block number is abbreviated; it contains only the lower 16 bits.
//Lookup TaPoS block summary by block number (remember block summary instances are the block numbers)
// Let N = head_block_num(), a = N & 0xFFFF, and r = trx.ref_block_num
//
// We want to solve for the largest block height x such that
// these two conditions hold:
//
// (a) 0x10000 divides x-r
// (b) x <= N
//
// Let us define:
//
// x1 = N-a+r
// x0 = x1-2^16
// x2 = x1+2^16
//
// It is clear that x0, x1, x2 are consecutive solutions to (a).
//
// Since r < 2^16 and a < 2^16, it follows that
// -2^16 < r-a < 2^16. From this we know that x0 < N and x2 > N.
//
// Case (1): x1 <= N. In this case, x1 must be the greatest
// integer that satisfies (a) and (b); for x2, the next
// largest integer that satisfies (a), does not satisfy (b).
//
// Case (2): x1 > N. In this case, x0 must be the greatest
// integer that satisfies (a) and (b); for x1, the next
// largest integer that satisfies (a), does not satisfy (b).
//
int64_t N = head_block_num();
int64_t a = N & 0xFFFF;
int64_t r = trx.ref_block_num;
int64_t x1 = N-a+r;
int64_t x0 = x1 - 0x10000;
int64_t x2 = x1 + 0x10000;
assert( x0 < N );
assert( x1 >= 0 );
assert( x2 > N );
uint32_t ref_block_height;
if( x1 <= N )
{
FC_ASSERT( x1 > 0 );
ref_block_height = uint32_t( x1 );
}
else
{
ref_block_height = uint32_t( x0 );
}
const block_summary_object& tapos_block_summary =
static_cast<const block_summary_object&>(
get_index<block_summary_object>()
.get(block_summary_id_type(ref_block_height))
);
//This is the signature check for transactions with relative expiration.
if( !(skip & skip_transaction_signatures) )
{
eval_state._sigs.reserve(trx.signatures.size());
for( const auto& sig : trx.signatures )
{
FC_ASSERT(eval_state._sigs.insert(std::make_pair(
public_key_type(
fc::ecc::public_key(sig,
trx.digest(tapos_block_summary.block_id))),
false)).second,
"Multiple signatures by same key detected");
}
}
const auto& tapos_block_summary = block_summary_id_type( trx.ref_block_num )(*this);
//Verify TaPoS block summary has correct ID prefix, and that this block's time is not past the expiration
FC_ASSERT( trx.ref_block_prefix == tapos_block_summary.block_id._hash[1] );
trx_expiration = tapos_block_summary.timestamp + chain_parameters.block_interval*trx.relative_expiration;
} else if( trx.relative_expiration == 0 ) {
trx_expiration = fc::time_point_sec() + fc::seconds(trx.ref_block_prefix);
FC_ASSERT( trx_expiration <= _pending_block.timestamp + chain_parameters.maximum_time_until_expiration, "",
("trx_expiration",trx_expiration)("_pending_block.timestamp",_pending_block.timestamp)("max_til_exp",chain_parameters.maximum_time_until_expiration));
}
FC_ASSERT( _pending_block.timestamp <= trx_expiration, "", ("pending.timestamp",_pending_block.timestamp)("trx_exp",trx_expiration) );
} else if( !(skip & skip_transaction_signatures) ) {
FC_ASSERT(trx.relative_expiration == 0, "May not use transactions with a reference block in block 1!");
fc::time_point_sec now = head_block_time();
FC_ASSERT( trx.expiration <= now + chain_parameters.maximum_time_until_expiration, "",
("trx.expiration",trx.expiration)("now",now)("max_til_exp",chain_parameters.maximum_time_until_expiration));
FC_ASSERT( now <= trx.expiration, "", ("now",now)("trx.exp",trx.expiration) );
}
//Insert transaction into unique transactions database.
if( !(skip & skip_transaction_dupe_check) )
{
create<transaction_object>([&](transaction_object& transaction) {
transaction.expiration = trx_expiration;
transaction.trx_id = trx_id;
transaction.trx = trx;
});
@ -609,15 +588,6 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx
auto range = index.equal_range(GRAPHENE_TEMP_ACCOUNT);
std::for_each(range.first, range.second, [](const account_balance_object& b) { FC_ASSERT(b.balance == 0); });
//Make sure all signatures were needed to validate the transaction
if( !(skip & (skip_transaction_signatures|skip_authority_check)) )
{
for( const auto& item : eval_state._sigs )
{
FC_ASSERT( item.second, "All signatures must be used", ("item",item) );
}
}
return ptrx;
} FC_CAPTURE_AND_RETHROW( (trx) ) }
@ -636,33 +606,35 @@ operation_result database::apply_operation(transaction_evaluation_state& eval_st
auto result = eval->evaluate( eval_state, op, true );
set_applied_operation_result( op_id, result );
return result;
} FC_CAPTURE_AND_RETHROW( (eval_state._sigs) ) }
} FC_CAPTURE_AND_RETHROW( ) }
const witness_object& database::validate_block_header( uint32_t skip, const signed_block& next_block )const
{
FC_ASSERT( _pending_block.previous == next_block.previous, "", ("pending.prev",_pending_block.previous)("next.prev",next_block.previous) );
FC_ASSERT( _pending_block.timestamp <= next_block.timestamp, "", ("_pending_block.timestamp",_pending_block.timestamp)("next",next_block.timestamp)("blocknum",next_block.block_num()) );
FC_ASSERT( head_block_id() == next_block.previous, "", ("head_block_id",head_block_id())("next.prev",next_block.previous) );
FC_ASSERT( head_block_time() < next_block.timestamp, "", ("head_block_time",head_block_time())("next",next_block.timestamp)("blocknum",next_block.block_num()) );
const witness_object& witness = next_block.witness(*this);
FC_ASSERT( secret_hash_type::hash( next_block.previous_secret ) == witness.next_secret_hash, "",
("previous_secret", next_block.previous_secret)("next_secret_hash", witness.next_secret_hash));
if( !(skip&skip_witness_signature) ) FC_ASSERT( next_block.validate_signee( witness.signing_key ) );
uint32_t slot_num = get_slot_at_time( next_block.timestamp );
FC_ASSERT( slot_num > 0 );
if( !(skip&skip_witness_signature) )
FC_ASSERT( next_block.validate_signee( witness.signing_key ) );
witness_id_type scheduled_witness = get_scheduled_witness( slot_num ).first;
FC_ASSERT( next_block.witness == scheduled_witness );
if( !skip_witness_schedule_check )
{
uint32_t slot_num = get_slot_at_time( next_block.timestamp );
FC_ASSERT( slot_num > 0 );
witness_id_type scheduled_witness = get_scheduled_witness( slot_num );
FC_ASSERT( next_block.witness == scheduled_witness );
}
return witness;
}
void database::create_block_summary(const signed_block& next_block)
{
const auto& sum = create<block_summary_object>( [&](block_summary_object& p) {
block_summary_id_type sid(next_block.block_num() & 0xffff );
modify( sid(*this), [&](block_summary_object& p) {
p.block_id = next_block.id();
p.timestamp = next_block.timestamp;
});
FC_ASSERT( sum.id.instance() == next_block.block_num(), "", ("summary.id",sum.id)("next.block_num",next_block.block_num()) );
}
void database::add_checkpoints( const flat_map<uint32_t,block_id_type>& checkpts )

View file

@ -72,11 +72,6 @@ void database::debug_dump()
total_balances[asset_obj.id] += asset_obj.dynamic_asset_data_id(db).accumulated_fees;
total_balances[asset_id_type()] += asset_obj.dynamic_asset_data_id(db).fee_pool;
}
for( const witness_object& witness_obj : db.get_index_type<witness_index>().indices() )
{
//idump((witness_obj));
total_balances[asset_id_type()] += witness_obj.accumulated_income;
}
if( total_balances[asset_id_type()].value != core_asset_data.current_supply.value )
{
edump( (total_balances[asset_id_type()].value)(core_asset_data.current_supply.value ));

View file

@ -19,6 +19,7 @@
#include <graphene/chain/database.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/chain_property_object.hpp>
#include <graphene/chain/global_property_object.hpp>
namespace graphene { namespace chain {
@ -33,6 +34,11 @@ const global_property_object& database::get_global_properties()const
return get( global_property_id_type() );
}
const chain_property_object& database::get_chain_properties()const
{
return get( chain_property_id_type() );
}
const dynamic_global_property_object&database::get_dynamic_global_properties() const
{
return get( dynamic_global_property_id_type() );
@ -63,6 +69,11 @@ decltype( chain_parameters::block_interval ) database::block_interval( )const
return get_global_properties().parameters.block_interval;
}
const chain_id_type& database::get_chain_id( )const
{
return get_chain_properties().chain_id;
}
const node_property_object& database::get_node_properties()const
{
return _node_property_object;
@ -73,4 +84,10 @@ node_property_object& database::node_properties()
return _node_property_object;
}
uint32_t database::last_non_undoable_block_num() const
{
return head_block_num() - _undo_db.size();
}
} }

View file

@ -20,10 +20,11 @@
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/balance_object.hpp>
#include <graphene/chain/block_summary_object.hpp>
#include <graphene/chain/chain_property_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/balance_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/transaction_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
@ -44,6 +45,7 @@
#include <graphene/chain/witness_evaluator.hpp>
#include <graphene/chain/worker_evaluator.hpp>
#include <graphene/chain/balance_evaluator.hpp>
#include <graphene/chain/confidential_evaluator.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
@ -105,9 +107,6 @@ const uint8_t withdraw_permission_object::type_id;
const uint8_t witness_object::space_id;
const uint8_t witness_object::type_id;
const uint8_t witness_schedule_object::space_id;
const uint8_t witness_schedule_object::type_id;
const uint8_t worker_object::space_id;
const uint8_t worker_object::type_id;
@ -120,6 +119,7 @@ void database::initialize_evaluators()
register_evaluator<account_upgrade_evaluator>();
register_evaluator<account_whitelist_evaluator>();
register_evaluator<committee_member_create_evaluator>();
register_evaluator<committee_member_update_evaluator>();
register_evaluator<committee_member_update_global_parameters_evaluator>();
register_evaluator<custom_evaluator>();
register_evaluator<asset_create_evaluator>();
@ -141,21 +141,25 @@ void database::initialize_evaluators()
register_evaluator<proposal_create_evaluator>();
register_evaluator<proposal_update_evaluator>();
register_evaluator<proposal_delete_evaluator>();
register_evaluator<witness_create_evaluator>();
register_evaluator<witness_withdraw_pay_evaluator>();
register_evaluator<vesting_balance_create_evaluator>();
register_evaluator<vesting_balance_withdraw_evaluator>();
register_evaluator<witness_create_evaluator>();
register_evaluator<witness_update_evaluator>();
register_evaluator<withdraw_permission_create_evaluator>();
register_evaluator<withdraw_permission_claim_evaluator>();
register_evaluator<withdraw_permission_update_evaluator>();
register_evaluator<withdraw_permission_delete_evaluator>();
register_evaluator<worker_create_evaluator>();
register_evaluator<balance_claim_evaluator>();
register_evaluator<transfer_to_blind_evaluator>();
register_evaluator<transfer_from_blind_evaluator>();
register_evaluator<blind_transfer_evaluator>();
}
void database::initialize_indexes()
{
reset_indexes();
_undo_db.set_max_size( GRAPHENE_MIN_UNDO_HISTORY );
//Protocol object indexes
add_index< primary_index<asset_index> >();
@ -174,10 +178,10 @@ void database::initialize_indexes()
prop_index->add_secondary_index<required_approval_index>();
add_index< primary_index<withdraw_permission_index > >();
//add_index< primary_index<vesting_balance_index> >();
add_index< primary_index<simple_index<vesting_balance_object>> >();
add_index< primary_index<vesting_balance_index> >();
add_index< primary_index<worker_index> >();
add_index< primary_index<balance_index> >();
add_index< primary_index<blinded_balance_index> >();
//Implementation object indexes
add_index< primary_index<transaction_index > >();
@ -188,7 +192,8 @@ void database::initialize_indexes()
add_index< primary_index<simple_index<account_statistics_object >> >();
add_index< primary_index<simple_index<asset_dynamic_data_object >> >();
add_index< primary_index<flat_index< block_summary_object >> >();
add_index< primary_index<simple_index<witness_schedule_object >> >();
add_index< primary_index<simple_index<chain_property_object > > >();
add_index< primary_index<simple_index<witness_schedule_object > > >();
}
void database::init_genesis(const genesis_state_type& genesis_state)
@ -214,6 +219,9 @@ void database::init_genesis(const genesis_state_type& genesis_state)
transaction_evaluation_state genesis_eval_state(this);
flat_index<block_summary_object>& bsi = get_mutable_index_type< flat_index<block_summary_object> >();
bsi.resize(0xffff+1);
// Create blockchain accounts
fc::ecc::private_key null_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")));
create<account_balance_object>([](account_balance_object& b) {
@ -227,12 +235,12 @@ void database::init_genesis(const genesis_state_type& genesis_state)
n.owner.weight_threshold = 1;
n.active.weight_threshold = 1;
n.name = "committee-account";
n.statistics = create<account_statistics_object>( [&](account_statistics_object& b){}).id;
n.statistics = create<account_statistics_object>( [&](account_statistics_object& s){ s.owner = n.id; }).id;
});
FC_ASSERT(committee_account.get_id() == GRAPHENE_COMMITTEE_ACCOUNT);
FC_ASSERT(create<account_object>([this](account_object& a) {
a.name = "witness-account";
a.statistics = create<account_statistics_object>([](account_statistics_object&){}).id;
a.statistics = create<account_statistics_object>([&](account_statistics_object& s){s.owner = a.id;}).id;
a.owner.weight_threshold = 1;
a.active.weight_threshold = 1;
a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_WITNESS_ACCOUNT;
@ -242,7 +250,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
}).get_id() == GRAPHENE_WITNESS_ACCOUNT);
FC_ASSERT(create<account_object>([this](account_object& a) {
a.name = "relaxed-committee-account";
a.statistics = create<account_statistics_object>([](account_statistics_object&){}).id;
a.statistics = create<account_statistics_object>([&](account_statistics_object& s){s.owner = a.id;}).id;
a.owner.weight_threshold = 1;
a.active.weight_threshold = 1;
a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_RELAXED_COMMITTEE_ACCOUNT;
@ -252,7 +260,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
}).get_id() == GRAPHENE_RELAXED_COMMITTEE_ACCOUNT);
FC_ASSERT(create<account_object>([this](account_object& a) {
a.name = "null-account";
a.statistics = create<account_statistics_object>([](account_statistics_object&){}).id;
a.statistics = create<account_statistics_object>([&](account_statistics_object& s){s.owner = a.id;}).id;
a.owner.weight_threshold = 1;
a.active.weight_threshold = 1;
a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_NULL_ACCOUNT;
@ -262,7 +270,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
}).get_id() == GRAPHENE_NULL_ACCOUNT);
FC_ASSERT(create<account_object>([this](account_object& a) {
a.name = "temp-account";
a.statistics = create<account_statistics_object>([](account_statistics_object&){}).id;
a.statistics = create<account_statistics_object>([&](account_statistics_object& s){s.owner = a.id;}).id;
a.owner.weight_threshold = 0;
a.active.weight_threshold = 0;
a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_TEMP_ACCOUNT;
@ -270,6 +278,36 @@ void database::init_genesis(const genesis_state_type& genesis_state)
a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
}).get_id() == GRAPHENE_TEMP_ACCOUNT);
FC_ASSERT(create<account_object>([this](account_object& a) {
a.name = "proxy-to-self";
a.statistics = create<account_statistics_object>([&](account_statistics_object& s){s.owner = a.id;}).id;
a.owner.weight_threshold = 1;
a.active.weight_threshold = 1;
a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_NULL_ACCOUNT;
a.membership_expiration_date = time_point_sec::maximum();
a.network_fee_percentage = 0;
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT;
}).get_id() == GRAPHENE_PROXY_TO_SELF_ACCOUNT);
// Create more special accounts
while( true )
{
uint64_t id = get_index<account_object>().get_next_id().instance();
if( id >= genesis_state.immutable_parameters.num_special_accounts )
break;
const account_object& acct = create<account_object>([&](account_object& a) {
a.name = "special-account-" + std::to_string(id);
a.statistics = create<account_statistics_object>([&](account_statistics_object& s){s.owner = a.id;}).id;
a.owner.weight_threshold = 1;
a.active.weight_threshold = 1;
a.registrar = a.lifetime_referrer = a.referrer = id;
a.membership_expiration_date = time_point_sec::maximum();
a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
});
FC_ASSERT( acct.get_id() == account_id_type(id) );
remove( acct );
}
// Create core asset
const asset_dynamic_data_object& dyn_asset =
@ -279,11 +317,11 @@ void database::init_genesis(const genesis_state_type& genesis_state)
const asset_object& core_asset =
create<asset_object>( [&]( asset_object& a ) {
a.symbol = GRAPHENE_SYMBOL;
a.options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY;
a.options.max_supply = genesis_state.max_core_supply;
a.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS;
a.options.flags = 0;
a.options.issuer_permissions = 0;
a.issuer = committee_account.id;
a.issuer = GRAPHENE_NULL_ACCOUNT;
a.options.core_exchange_rate.base.amount = 1;
a.options.core_exchange_rate.base.asset_id = 0;
a.options.core_exchange_rate.quote.amount = 1;
@ -292,11 +330,37 @@ void database::init_genesis(const genesis_state_type& genesis_state)
});
assert( asset_id_type(core_asset.id) == asset().asset_id );
assert( get_balance(account_id_type(), asset_id_type()) == asset(dyn_asset.current_supply) );
(void)core_asset;
// Create more special assets
while( true )
{
uint64_t id = get_index<asset_object>().get_next_id().instance();
if( id >= genesis_state.immutable_parameters.num_special_assets )
break;
const asset_dynamic_data_object& dyn_asset =
create<asset_dynamic_data_object>([&](asset_dynamic_data_object& a) {
a.current_supply = 0;
});
const asset_object& asset_obj = create<asset_object>( [&]( asset_object& a ) {
a.symbol = "SPECIAL" + std::to_string( id );
a.options.max_supply = 0;
a.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS;
a.options.flags = 0;
a.options.issuer_permissions = 0;
a.issuer = GRAPHENE_NULL_ACCOUNT;
a.options.core_exchange_rate.base.amount = 1;
a.options.core_exchange_rate.base.asset_id = 0;
a.options.core_exchange_rate.quote.amount = 1;
a.options.core_exchange_rate.quote.asset_id = 0;
a.dynamic_asset_data_id = dyn_asset.id;
});
FC_ASSERT( asset_obj.get_id() == asset_id_type(id) );
remove( asset_obj );
}
chain_id_type chain_id = genesis_state.compute_chain_id();
// Create global properties
create<global_property_object>([&](global_property_object& p) {
p.chain_id = fc::digest(genesis_state);
p.parameters = genesis_state.initial_parameters;
// Set fees to zero initially, so that genesis initialization needs not pay them
// We'll fix it at the end of the function
@ -305,8 +369,19 @@ void database::init_genesis(const genesis_state_type& genesis_state)
});
create<dynamic_global_property_object>([&](dynamic_global_property_object& p) {
p.time = genesis_state.initial_timestamp;
p.dynamic_flags = 0;
p.witness_budget = 0;
p.recent_slots_filled = fc::uint128::max_value();
});
FC_ASSERT( (genesis_state.immutable_parameters.min_witness_count & 1) == 1, "min_witness_count must be odd" );
FC_ASSERT( (genesis_state.immutable_parameters.min_committee_member_count & 1) == 1, "min_committee_member_count must be odd" );
create<chain_property_object>([&](chain_property_object& p)
{
p.chain_id = chain_id;
p.immutable_parameters = genesis_state.immutable_parameters;
} );
create<block_summary_object>([&](block_summary_object&) {});
// Create initial accounts
@ -349,27 +424,31 @@ void database::init_genesis(const genesis_state_type& genesis_state)
// Helper function to get asset ID by symbol
const auto& assets_by_symbol = get_index_type<asset_index>().indices().get<by_symbol>();
auto get_asset_id = [&assets_by_symbol](const string& symbol) {
const auto get_asset_id = [&assets_by_symbol](const string& symbol) {
auto itr = assets_by_symbol.find(symbol);
// TODO: This is temporary for handling BTS snapshot
if( symbol == "BTS" )
itr = assets_by_symbol.find(GRAPHENE_SYMBOL);
FC_ASSERT(itr != assets_by_symbol.end(),
"Unable to find asset '${sym}'. Did you forget to add a record for it to initial_assets?",
("sym", symbol));
return itr->get_id();
};
map<asset_id_type, share_type> total_supplies;
// Create initial assets
for( const genesis_state_type::initial_asset_type& asset : genesis_state.initial_assets )
{
asset_id_type new_asset_id = get_index_type<asset_index>().get_next_id();
asset_dynamic_data_id_type dynamic_data_id;
optional<asset_bitasset_data_id_type> bitasset_data_id;
if( asset.bitasset_opts.valid() )
if( asset.is_bitasset )
{
share_type total_allocated;
asset_id_type new_asset_id = get_index_type<asset_index>().get_next_id();
asset_id_type collateral_asset_id = get_asset_id(asset.bitasset_opts->backing_asset_symbol);
int collateral_holder_number = 0;
for( const auto& collateral_rec : asset.bitasset_opts->collateral_records )
for( const auto& collateral_rec : asset.collateral_records )
{
account_create_operation cop;
cop.name = asset.symbol + "-collateral-holder-" + std::to_string(collateral_holder_number);
@ -384,30 +463,24 @@ void database::init_genesis(const genesis_state_type& genesis_state)
c.collateral = collateral_rec.collateral;
c.debt = collateral_rec.debt;
c.call_price = price::call_price(chain::asset(c.debt, new_asset_id),
chain::asset(c.collateral, collateral_asset_id),
asset.bitasset_opts->maintenance_collateral_ratio);
chain::asset(c.collateral, core_asset.id),
GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);
});
total_allocated += collateral_rec.debt;
total_supplies[ 0 ] += collateral_rec.collateral;
++collateral_holder_number;
}
bitasset_data_id = create<asset_bitasset_data_object>([&](asset_bitasset_data_object& b) {
b.options.feed_lifetime_sec = asset.bitasset_opts->feed_lifetime_sec;
b.options.minimum_feeds = asset.bitasset_opts->minimum_feeds;
b.options.force_settlement_delay_sec = asset.bitasset_opts->force_settlement_delay_sec;
b.options.force_settlement_offset_percent = asset.bitasset_opts->force_settlement_offset_percent;
b.options.maximum_force_settlement_volume = asset.bitasset_opts->maximum_force_settlement_volume;
b.options.short_backing_asset = get_asset_id(asset.bitasset_opts->backing_asset_symbol);
b.options.short_backing_asset = core_asset.id;
}).id;
}
dynamic_data_id = create<asset_dynamic_data_object>([&](asset_dynamic_data_object& d) {
d.current_supply = total_allocated;
d.accumulated_fees = asset.initial_accumulated_fees;
}).id;
} else
dynamic_data_id = create<asset_dynamic_data_object>([&](asset_dynamic_data_object& d) {
d.accumulated_fees = asset.initial_accumulated_fees;
}).id;
dynamic_data_id = create<asset_dynamic_data_object>([&](asset_dynamic_data_object& d) {
d.accumulated_fees = asset.accumulated_fees;
}).id;
total_supplies[ new_asset_id ] += asset.accumulated_fees;
create<asset_object>([&](asset_object& a) {
a.symbol = asset.symbol;
@ -415,10 +488,6 @@ void database::init_genesis(const genesis_state_type& genesis_state)
a.precision = asset.precision;
a.issuer = get_account_id(asset.issuer_name);
a.options.max_supply = asset.max_supply;
a.options.market_fee_percent = asset.market_fee_percent;
a.options.max_market_fee = asset.max_market_fee;
a.options.issuer_permissions = asset.issuer_permissions;
a.options.flags = asset.flags;
a.dynamic_asset_data_id = dynamic_data_id;
a.bitasset_data_id = bitasset_data_id;
@ -429,19 +498,22 @@ void database::init_genesis(const genesis_state_type& genesis_state)
share_type total_allocation;
for( const auto& handout : genesis_state.initial_balances )
{
create<balance_object>([&handout,&assets_by_symbol,total_allocation](balance_object& b) {
b.balance = asset(handout.amount, assets_by_symbol.find(handout.asset_symbol)->get_id());
const auto asset_id = get_asset_id(handout.asset_symbol);
create<balance_object>([&handout,&get_asset_id,total_allocation,asset_id](balance_object& b) {
b.balance = asset(handout.amount, asset_id);
b.owner = handout.owner;
});
total_allocation += handout.amount;
total_supplies[ asset_id ] += handout.amount;
}
// Create initial vesting balances
for( const genesis_state_type::initial_vesting_balance_type& vest : genesis_state.initial_vesting_balances )
{
const auto asset_id = get_asset_id(vest.asset_symbol);
create<balance_object>([&](balance_object& b) {
b.owner = vest.owner;
b.balance = asset(vest.amount, assets_by_symbol.find(vest.asset_symbol)->get_id());
b.balance = asset(vest.amount, asset_id);
linear_vesting_policy policy;
policy.begin_timestamp = vest.begin_timestamp;
@ -451,17 +523,38 @@ void database::init_genesis(const genesis_state_type& genesis_state)
b.vesting_policy = std::move(policy);
});
total_allocation += vest.amount;
total_supplies[ asset_id ] += vest.amount;
}
// Set current supply based on allocations, if they happened
if( total_allocation > 0 )
if( total_supplies[ 0 ] > 0 )
{
modify(dyn_asset, [total_allocation](asset_dynamic_data_object& d) {
d.current_supply = total_allocation;
});
adjust_balance(GRAPHENE_COMMITTEE_ACCOUNT, -get_balance(GRAPHENE_COMMITTEE_ACCOUNT,{}));
adjust_balance(GRAPHENE_COMMITTEE_ACCOUNT, -get_balance(GRAPHENE_COMMITTEE_ACCOUNT,{}));
}
else
{
total_supplies[ 0 ] = GRAPHENE_MAX_SHARE_SUPPLY;
}
// Save tallied supplies
for( const auto& item : total_supplies )
{
const auto asset_id = item.first;
const auto total_supply = item.second;
modify( get( asset_id ), [ & ]( asset_object& asset ) {
modify( get( asset.dynamic_asset_data_id ), [ & ]( asset_dynamic_data_object& asset_data ) {
asset_data.current_supply = total_supply;
} );
} );
}
// TODO: Assert that bitasset debt = supply
// Create special witness account
const witness_object& wit = create<witness_object>([&](witness_object& w) {});
FC_ASSERT( wit.id == GRAPHENE_NULL_WITNESS );
remove(wit);
// Create initial witnesses
std::for_each(genesis_state.initial_witness_candidates.begin(), genesis_state.initial_witness_candidates.end(),
@ -469,7 +562,6 @@ void database::init_genesis(const genesis_state_type& genesis_state)
witness_create_operation op;
op.witness_account = get_account_id(witness.owner_name);
op.block_signing_key = witness.block_signing_key;
op.initial_secret = secret_hash_type::hash( secret_hash_type() );
apply_operation(genesis_eval_state, op);
});
@ -498,43 +590,25 @@ void database::init_genesis(const genesis_state_type& genesis_state)
// Set active witnesses
modify(get_global_properties(), [&](global_property_object& p) {
for( int i = 0; i < genesis_state.initial_active_witnesses; ++i )
for( uint32_t i = 1; i <= genesis_state.initial_active_witnesses; ++i )
{
p.active_witnesses.insert(i);
p.witness_accounts.insert(get(witness_id_type(i)).witness_account);
}
});
// Initialize witness schedule
#ifndef NDEBUG
const witness_schedule_object& wso =
#endif
create<witness_schedule_object>([&](witness_schedule_object& _wso)
{
memset(_wso.rng_seed.begin(), 0, _wso.rng_seed.size());
witness_scheduler_rng rng(_wso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV);
auto init_witnesses = get_global_properties().active_witnesses;
_wso.scheduler = witness_scheduler();
_wso.scheduler._min_token_count = std::max(int(init_witnesses.size()) / 2, 1);
_wso.scheduler.update(init_witnesses);
for( size_t i=0; i<init_witnesses.size(); ++i )
_wso.scheduler.produce_schedule(rng);
_wso.last_scheduling_block = 0;
_wso.recent_slots_filled = fc::uint128::max_value();
});
assert( wso.id == witness_schedule_id_type() );
// Enable fees
modify(get_global_properties(), [&genesis_state](global_property_object& p) {
p.parameters.current_fees = genesis_state.initial_parameters.current_fees;
});
// Create witness scheduler
create<witness_schedule_object>([&]( witness_schedule_object& wso )
{
for( const witness_id_type& wid : get_global_properties().active_witnesses )
wso.current_shuffled_witnesses.push_back( wid );
});
_undo_db.enable();
} FC_CAPTURE_AND_RETHROW() }

View file

@ -16,6 +16,11 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <boost/multiprecision/integer.hpp>
#include <fc/smart_ref_impl.hpp>
#include <fc/uint128.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/account_object.hpp>
@ -24,11 +29,8 @@
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/witness_schedule_object.hpp>
#include <graphene/chain/worker_evaluator.hpp>
#include <fc/uint128.hpp>
namespace graphene { namespace chain {
template<class Index>
@ -81,6 +83,19 @@ struct worker_pay_visitor
worker.pay_worker(pay, db);
}
};
void database::update_worker_votes()
{
auto& idx = get_index_type<worker_index>();
auto itr = idx.indices().get<by_account>().begin();
while( itr != idx.indices().get<by_account>().end() )
{
modify( *itr, [&]( worker_object& obj ){
obj.total_votes_for = _vote_tally_buffer[obj.vote_for];
obj.total_votes_against = _vote_tally_buffer[obj.vote_against];
});
++itr;
}
}
void database::pay_workers( share_type& budget )
{
@ -88,7 +103,7 @@ void database::pay_workers( share_type& budget )
vector<std::reference_wrapper<const worker_object>> active_workers;
get_index_type<worker_index>().inspect_all_objects([this, &active_workers](const object& o) {
const worker_object& w = static_cast<const worker_object&>(o);
auto now = _pending_block.timestamp;
auto now = head_block_time();
if( w.is_active(now) && w.approving_stake(_vote_tally_buffer) > 0 )
active_workers.emplace_back(w);
});
@ -103,14 +118,14 @@ void database::pay_workers( share_type& budget )
return wa.id < wb.id;
});
for( int i = 0; i < active_workers.size() && budget > 0; ++i )
for( uint32_t i = 0; i < active_workers.size() && budget > 0; ++i )
{
const worker_object& active_worker = active_workers[i];
share_type requested_pay = active_worker.daily_pay;
if( _pending_block.timestamp - get_dynamic_global_properties().last_budget_time != fc::days(1) )
if( head_block_time() - get_dynamic_global_properties().last_budget_time != fc::days(1) )
{
fc::uint128 pay(requested_pay.value);
pay *= (_pending_block.timestamp - get_dynamic_global_properties().last_budget_time).count();
pay *= (head_block_time() - get_dynamic_global_properties().last_budget_time).count();
pay /= fc::days(1).count();
requested_pay = pay.to_uint64();
}
@ -136,9 +151,17 @@ void database::update_active_witnesses()
&& (stake_tally <= stake_target) )
stake_tally += _witness_count_histogram_buffer[++witness_count];
auto wits = sort_votable_objects<witness_index>(std::max(witness_count*2+1, (size_t)GRAPHENE_MIN_WITNESS_COUNT));
const chain_property_object& cpo = get_chain_properties();
auto wits = sort_votable_objects<witness_index>(std::max(witness_count*2+1, (size_t)cpo.immutable_parameters.min_witness_count));
const global_property_object& gpo = get_global_properties();
for( const witness_object& wit : wits )
{
modify( wit, [&]( witness_object& obj ){
obj.total_votes = _vote_tally_buffer[wit.vote_id];
});
}
// Update witness authority
modify( get(GRAPHENE_WITNESS_ACCOUNT), [&]( account_object& a ) {
uint64_t total_votes = 0;
@ -184,11 +207,6 @@ void database::update_active_witnesses()
});
});
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
modify(wso, [&](witness_schedule_object& _wso)
{
_wso.scheduler.update(gpo.active_witnesses);
});
} FC_CAPTURE_AND_RETHROW() }
void database::update_active_committee_members()
@ -202,7 +220,15 @@ void database::update_active_committee_members()
&& (stake_tally <= stake_target) )
stake_tally += _committee_count_histogram_buffer[++committee_member_count];
auto committee_members = sort_votable_objects<committee_member_index>(std::max(committee_member_count*2+1, (size_t)GRAPHENE_MIN_COMMITTEE_MEMBER_COUNT));
const chain_property_object& cpo = get_chain_properties();
auto committee_members = sort_votable_objects<committee_member_index>(std::max(committee_member_count*2+1, (size_t)cpo.immutable_parameters.min_committee_member_count));
for( const committee_member_object& del : committee_members )
{
modify( del, [&]( committee_member_object& obj ){
obj.total_votes = _vote_tally_buffer[del.vote_id];
});
}
// Update committee authorities
if( !committee_members.empty() )
@ -296,7 +322,7 @@ void database::process_budget()
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
const asset_dynamic_data_object& core =
asset_id_type(0)(*this).dynamic_asset_data_id(*this);
fc::time_point_sec now = _pending_block.timestamp;
fc::time_point_sec now = head_block_time();
int64_t time_to_maint = (dpo.next_maintenance_time - now).to_seconds();
//
@ -389,7 +415,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
// specifying the opinions.
const account_object& opinion_account =
(stake_account.options.voting_account ==
account_id_type())? stake_account
GRAPHENE_PROXY_TO_SELF_ACCOUNT)? stake_account
: d.get(stake_account.options.voting_account);
const auto& stats = stake_account.statistics(d);
@ -458,15 +484,13 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
update_active_witnesses();
update_active_committee_members();
update_worker_votes();
modify(gpo, [this](global_property_object& p) {
// Remove scaling of account registration fee
/*
/// TODO reimplement this
const auto& dgpo = get_dynamic_global_properties();
p.parameters.current_fees.account_create_fee >>= p.parameters.account_fee_scale_bitshifts *
p.parameters.current_fees->get<account_create_operation>().basic_fee >>= p.parameters.account_fee_scale_bitshifts *
(dgpo.accounts_registered_this_interval / p.parameters.accounts_per_fee_scale);
*/
if( p.pending_parameters )
{
@ -475,19 +499,6 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
}
});
auto new_block_interval = global_props.parameters.block_interval;
// if block interval CHANGED during this block *THEN* we cannot simply
// add the interval if we want to maintain the invariant that all timestamps are a multiple
// of the interval.
_pending_block.timestamp = next_block.timestamp + fc::seconds(new_block_interval);
uint32_t r = _pending_block.timestamp.sec_since_epoch()%new_block_interval;
if( !r )
{
_pending_block.timestamp -= r;
assert( (_pending_block.timestamp.sec_since_epoch() % new_block_interval) == 0 );
}
auto next_maintenance_time = get<dynamic_global_property_object>(dynamic_global_property_id_type()).next_maintenance_time;
auto maintenance_interval = gpo.parameters.maintenance_interval;
@ -497,10 +508,25 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
next_maintenance_time = time_point_sec() +
(((next_block.timestamp.sec_since_epoch() / maintenance_interval) + 1) * maintenance_interval);
else
// It's possible we have missed blocks for at least a maintenance interval.
// In this case, we'll need to bump the next maintenance time more than once.
do next_maintenance_time += maintenance_interval;
while( next_maintenance_time < head_block_time() );
{
// We want to find the smallest k such that next_maintenance_time + k * maintenance_interval > head_block_time()
// This implies k > ( head_block_time() - next_maintenance_time ) / maintenance_interval
//
// Let y be the right-hand side of this inequality, i.e.
// y = ( head_block_time() - next_maintenance_time ) / maintenance_interval
//
// and let the fractional part f be y-floor(y). Clearly 0 <= f < 1.
// We can rewrite f = y-floor(y) as floor(y) = y-f.
//
// Clearly k = floor(y)+1 has k > y as desired. Now we must
// show that this is the least such k, i.e. k-1 <= y.
//
// But k-1 = floor(y)+1-1 = floor(y) = y-f <= y.
// So this k suffices.
//
auto y = (head_block_time() - next_maintenance_time).to_seconds() / maintenance_interval;
next_maintenance_time += (y+1) * maintenance_interval;
}
}
modify(get_dynamic_global_properties(), [next_maintenance_time](dynamic_global_property_object& d) {

View file

@ -22,6 +22,7 @@
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <functional>
#include <iostream>
namespace graphene { namespace chain {
@ -31,37 +32,62 @@ database::database()
initialize_evaluators();
}
database::~database(){
if( _pending_block_session )
_pending_block_session->commit();
database::~database()
{
clear_pending();
}
void database::reindex(fc::path data_dir, const genesis_state_type& initial_allocation)
{ try {
ilog( "reindexing blockchain" );
wipe(data_dir, false);
open(data_dir, [&initial_allocation]{return initial_allocation;});
auto start = fc::time_point::now();
auto last_block = _block_id_to_block.last();
if( !last_block ) return;
if( !last_block ) {
elog( "!no last block" );
edump((last_block));
return;
}
const auto last_block_num = last_block->block_num();
// TODO: disable undo tracking durring reindex, this currently causes crashes in the benchmark test
//_undo_db.disable();
ilog( "Replaying blocks..." );
_undo_db.disable();
for( uint32_t i = 1; i <= last_block_num; ++i )
{
apply_block(*_block_id_to_block.fetch_by_number(i), skip_witness_signature |
skip_transaction_signatures |
skip_undo_block |
skip_undo_transaction |
skip_transaction_dupe_check |
skip_tapos_check |
skip_authority_check);
if( i % 2000 == 0 ) std::cerr << " " << double(i*100)/last_block_num << "% "<<i << " of " <<last_block_num<<" \n";
fc::optional< signed_block > block = _block_id_to_block.fetch_by_number(i);
if( !block.valid() )
{
wlog( "Reindexing terminated due to gap: Block ${i} does not exist!", ("i", i) );
uint32_t dropped_count = 0;
while( true )
{
fc::optional< block_id_type > last_id = _block_id_to_block.last_id();
// this can trigger if we attempt to e.g. read a file that has block #2 but no block #1
if( !last_id.valid() )
break;
// we've caught up to the gap
if( block_header::num_from_id( *last_id ) <= i )
break;
_block_id_to_block.remove( *last_id );
dropped_count++;
}
wlog( "Dropped ${n} blocks from after the gap", ("n", dropped_count) );
break;
}
apply_block(*block, skip_witness_signature |
skip_transaction_signatures |
skip_transaction_dupe_check |
skip_tapos_check |
skip_witness_schedule_check |
skip_authority_check);
}
//_undo_db.enable();
_undo_db.enable();
auto end = fc::time_point::now();
wdump( ((end-start).count()/1000000.0) );
ilog( "Done reindexing, elapsed time: ${t} sec", ("t",double((end-start).count())/1000000.0 ) );
} FC_CAPTURE_AND_RETHROW( (data_dir) ) }
void database::wipe(const fc::path& data_dir, bool include_blocks)
@ -73,12 +99,61 @@ void database::wipe(const fc::path& data_dir, bool include_blocks)
fc::remove_all( data_dir / "database" );
}
void database::open(
const fc::path& data_dir,
std::function<genesis_state_type()> genesis_loader )
{
try
{
object_database::open(data_dir);
_block_id_to_block.open(data_dir / "database" / "block_num_to_block");
if( !find(global_property_id_type()) )
init_genesis(genesis_loader());
fc::optional<signed_block> last_block = _block_id_to_block.last();
if( last_block.valid() )
{
_fork_db.start_block( *last_block );
idump((last_block->id())(last_block->block_num()));
if( last_block->id() != head_block_id() )
{
FC_ASSERT( head_block_num() == 0, "last block ID does not match current chain state" );
}
}
//idump((head_block_id())(head_block_num()));
}
FC_CAPTURE_AND_RETHROW( (data_dir) )
}
void database::close(uint32_t blocks_to_rewind)
{
_pending_block_session.reset();
// TODO: Save pending tx's on close()
clear_pending();
for(uint32_t i = 0; i < blocks_to_rewind && head_block_num() > 0; ++i)
pop_block();
// pop all of the blocks that we can given our undo history, this should
// throw when there is no more undo history to pop
try
{
while( true )
{
// elog("pop");
block_id_type popped_block_id = head_block_id();
pop_block();
_fork_db.remove(popped_block_id); // doesn't throw on missing
try
{
_block_id_to_block.remove(popped_block_id);
}
catch (const fc::key_not_found_exception&)
{
}
}
}
catch (...)
{
}
object_database::flush();
object_database::close();

View file

@ -93,7 +93,11 @@ void database::cancel_order(const force_settlement_object& order, bool create_vi
if( create_virtual_op )
{
// TODO: create virtual op
asset_settle_cancel_operation vop;
vop.settlement = order.id;
vop.account = order.owner;
vop.amount = order.balance;
push_applied_operation( vop );
}
}
@ -109,7 +113,10 @@ void database::cancel_order( const limit_order_object& order, bool create_virtua
if( create_virtual_op )
{
// TODO: create a virtual cancel operation
limit_order_cancel_operation vop;
vop.order = order.id;
vop.fee_paying_account = order.seller;
push_applied_operation( vop );
}
remove(order);

View file

@ -17,6 +17,7 @@
*/
#include <graphene/chain/database.hpp>
#include <graphene/chain/db_with.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/global_property_object.hpp>
@ -36,25 +37,64 @@ void database::update_global_dynamic_data( const signed_block& b )
const dynamic_global_property_object& _dgp =
dynamic_global_property_id_type(0)(*this);
//
uint32_t missed_blocks = get_slot_at_time( b.timestamp );
assert( missed_blocks != 0 );
missed_blocks--;
for( uint32_t i = 0; i < missed_blocks; ++i ) {
const auto& witness_missed = get_scheduled_witness( i+1 )(*this);
if( witness_missed.id != b.witness ) {
const auto& witness_account = witness_missed.witness_account(*this);
/*
if( (fc::time_point::now() - b.timestamp) < fc::seconds(30) )
wlog( "Witness ${name} missed block ${n} around ${t}", ("name",witness_account.name)("n",b.block_num())("t",b.timestamp) );
*/
modify( witness_missed, [&]( witness_object& w ) {
w.total_missed++;
});
}
}
// dynamic global properties updating
//
modify( _dgp, [&]( dynamic_global_property_object& dgp ){
secret_hash_type::encoder enc;
fc::raw::pack( enc, dgp.random );
fc::raw::pack( enc, b.previous_secret );
dgp.random = enc.result();
if( BOOST_UNLIKELY( b.block_num() == 1 ) )
dgp.recently_missed_count = 0;
else if( _checkpoints.size() && _checkpoints.rbegin()->first >= b.block_num() )
dgp.recently_missed_count = 0;
else if( missed_blocks )
dgp.recently_missed_count += GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT*missed_blocks;
else if( dgp.recently_missed_count > GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT )
dgp.recently_missed_count -= GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT;
else if( dgp.recently_missed_count > 0 )
dgp.recently_missed_count--;
dgp.head_block_number = b.block_num();
dgp.head_block_id = b.id();
dgp.time = b.timestamp;
dgp.current_witness = b.witness;
dgp.recent_slots_filled = (
(dgp.recent_slots_filled << 1)
+ 1) << missed_blocks;
dgp.current_aslot += missed_blocks+1;
});
if( !(get_node_properties().skip_flags & skip_undo_history_check) )
{
GRAPHENE_ASSERT( _dgp.recently_missed_count < GRAPHENE_MAX_UNDO_HISTORY, undo_database_exception,
"The database does not have enough undo history to support a blockchain with so many missed blocks. "
"Please add a checkpoint if you would like to continue applying blocks beyond this point.",
("recently_missed",_dgp.recently_missed_count)("max_undo",GRAPHENE_MAX_UNDO_HISTORY) );
}
_undo_db.set_max_size( _dgp.recently_missed_count + GRAPHENE_MIN_UNDO_HISTORY );
_fork_db.set_max_size( _dgp.recently_missed_count + GRAPHENE_MIN_UNDO_HISTORY );
}
void database::update_signing_witness(const witness_object& signing_witness, const signed_block& new_block)
{
const global_property_object& gpo = get_global_properties();
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
uint64_t new_block_aslot = dpo.current_aslot + get_slot_at_time( new_block.timestamp );
share_type witness_pay = std::min( gpo.parameters.witness_pay_per_block, dpo.witness_budget );
@ -63,34 +103,21 @@ void database::update_signing_witness(const witness_object& signing_witness, con
_dpo.witness_budget -= witness_pay;
} );
deposit_witness_pay( signing_witness, witness_pay );
modify( signing_witness, [&]( witness_object& _wit )
{
_wit.previous_secret = new_block.previous_secret;
_wit.next_secret_hash = new_block.next_secret_hash;
_wit.accumulated_income += witness_pay;
_wit.last_aslot = new_block_aslot;
} );
}
void database::update_pending_block(const signed_block& next_block, uint8_t current_block_interval)
{
_pending_block.timestamp = next_block.timestamp + current_block_interval;
_pending_block.previous = next_block.id();
auto old_pending_trx = std::move(_pending_block.transactions);
_pending_block.transactions.clear();
for( auto old_trx : old_pending_trx )
push_transaction( old_trx );
}
void database::clear_expired_transactions()
{
//Look for expired transactions in the deduplication list, and remove them.
//Transactions must have expired by at least two forking windows in order to be removed.
auto& transaction_idx = static_cast<transaction_index&>(get_mutable_index(implementation_ids, impl_transaction_object_type));
const auto& dedupe_index = transaction_idx.indices().get<by_expiration>();
const auto& global_parameters = get_global_properties().parameters;
auto forking_window_time = global_parameters.maximum_undo_history * global_parameters.block_interval;
while( !dedupe_index.empty()
&& head_block_time() - dedupe_index.rbegin()->expiration >= fc::seconds(forking_window_time) )
while( (!dedupe_index.empty()) && (head_block_time() > dedupe_index.rbegin()->trx.expiration) )
transaction_idx.remove(*dedupe_index.rbegin());
}
@ -118,7 +145,7 @@ void database::clear_expired_proposals()
void database::clear_expired_orders()
{
with_skip_flags(
detail::with_skip_flags( *this,
get_node_properties().skip_flags | skip_authority_check, [&](){
transaction_evaluation_state cancel_context(this);
@ -134,7 +161,6 @@ void database::clear_expired_orders()
}
});
//Process expired force settlement orders
auto& settlement_index = get_index_type<force_settlement_index>().indices().get<by_expiration>();
if( !settlement_index.empty() )
@ -241,6 +267,18 @@ void database::update_expired_feeds()
}
}
void database::update_maintenance_flag( bool new_maintenance_flag )
{
modify( get_dynamic_global_properties(), [&]( dynamic_global_property_object& dpo )
{
auto maintenance_flag = dynamic_global_property_object::maintenance_flag;
dpo.dynamic_flags =
(dpo.dynamic_flags & ~maintenance_flag)
| (new_maintenance_flag ? maintenance_flag : 0);
} );
return;
}
void database::update_withdraw_permissions()
{
auto& permit_index = get_index_type<withdraw_permission_index>().indices().get<by_expiration>();

View file

@ -15,45 +15,23 @@
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/database.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/witness_schedule_object.hpp>
namespace graphene { namespace chain {
pair<witness_id_type, bool> database::get_scheduled_witness(uint32_t slot_num)const
using boost::container::flat_set;
witness_id_type database::get_scheduled_witness( uint32_t slot_num )const
{
if( slot_num == 0 )
return pair<witness_id_type, bool>(witness_id_type(), false);
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
// ask the near scheduler who goes in the given slot
witness_id_type wid;
bool slot_is_near = wso.scheduler.get_slot(slot_num-1, wid);
if( ! slot_is_near )
{
// if the near scheduler doesn't know, we have to extend it to
// a far scheduler.
// n.b. instantiating it is slow, but block gaps long enough to
// need it are likely pretty rare.
witness_scheduler_rng far_rng(wso.rng_seed.begin(), GRAPHENE_FAR_SCHEDULE_CTR_IV);
far_future_witness_scheduler far_scheduler =
far_future_witness_scheduler(wso.scheduler, far_rng);
if( !far_scheduler.get_slot(slot_num-1, wid) )
{
// no scheduled witness -- somebody set up us the bomb
// n.b. this code path is impossible, the present
// implementation of far_future_witness_scheduler
// returns true unconditionally
assert( false );
}
}
return pair<witness_id_type, bool>(wid, slot_is_near);
uint64_t current_aslot = dpo.current_aslot + slot_num;
return wso.current_shuffled_witnesses[ current_aslot % wso.current_shuffled_witnesses.size() ];
}
fc::time_point_sec database::get_slot_time(uint32_t slot_num)const
@ -62,9 +40,32 @@ fc::time_point_sec database::get_slot_time(uint32_t slot_num)const
return fc::time_point_sec();
auto interval = block_interval();
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
if( head_block_num() == 0 )
{
// n.b. first block is at genesis_time plus one block interval
fc::time_point_sec genesis_time = dpo.time;
return genesis_time + slot_num * interval;
}
auto head_block_abs_slot = head_block_time().sec_since_epoch() / interval;
fc::time_point_sec first_slot_time(head_block_abs_slot * interval);
return first_slot_time + slot_num * interval;
fc::time_point_sec head_slot_time(head_block_abs_slot * interval);
const global_property_object& gpo = get_global_properties();
// "slot 0" is head_slot_time
// "slot 1" is head_slot_time,
// plus maint interval if head block is a maint block
// plus block interval if head block is not a maint block
return head_slot_time
+ (slot_num +
(
(dpo.dynamic_flags & dynamic_global_property_object::maintenance_flag)
? gpo.parameters.maintenance_skip_slots : 0
)
) * interval
;
}
uint32_t database::get_slot_at_time(fc::time_point_sec when)const
@ -75,82 +76,45 @@ uint32_t database::get_slot_at_time(fc::time_point_sec when)const
return (when - first_slot_time).to_seconds() / block_interval() + 1;
}
vector<witness_id_type> database::get_near_witness_schedule()const
{
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
vector<witness_id_type> result;
result.reserve(wso.scheduler.size());
uint32_t slot_num = 0;
witness_id_type wid;
while( wso.scheduler.get_slot(slot_num++, wid) )
result.emplace_back(wid);
return result;
}
void database::update_witness_schedule(const signed_block& next_block)
{
const global_property_object& gpo = get_global_properties();
const witness_schedule_object& wso = get(witness_schedule_id_type());
uint32_t schedule_needs_filled = gpo.active_witnesses.size();
uint32_t schedule_slot = get_slot_at_time(next_block.timestamp);
// We shouldn't be able to generate _pending_block with timestamp
// in the past, and incoming blocks from the network with timestamp
// in the past shouldn't be able to make it this far without
// triggering FC_ASSERT elsewhere
assert( schedule_slot > 0 );
witness_id_type first_witness;
bool slot_is_near = wso.scheduler.get_slot( schedule_slot-1, first_witness );
witness_id_type wit;
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
assert( dpo.random.data_size() == witness_scheduler_rng::seed_length );
assert( witness_scheduler_rng::seed_length == wso.rng_seed.size() );
modify(wso, [&](witness_schedule_object& _wso)
{
_wso.slots_since_genesis += schedule_slot;
witness_scheduler_rng rng(wso.rng_seed.data, _wso.slots_since_genesis);
_wso.scheduler._min_token_count = std::max(int(gpo.active_witnesses.size()) / 2, 1);
if( slot_is_near )
{
uint32_t drain = schedule_slot;
while( drain > 0 )
{
if( _wso.scheduler.size() == 0 )
break;
_wso.scheduler.consume_schedule();
--drain;
}
}
else
{
_wso.scheduler.reset_schedule( first_witness );
}
while( !_wso.scheduler.get_slot(schedule_needs_filled, wit) )
{
if( _wso.scheduler.produce_schedule(rng) & emit_turn )
memcpy(_wso.rng_seed.begin(), dpo.random.data(), dpo.random.data_size());
}
_wso.last_scheduling_block = next_block.block_num();
_wso.recent_slots_filled = (
(_wso.recent_slots_filled << 1)
+ 1) << (schedule_slot - 1);
});
}
uint32_t database::witness_participation_rate()const
{
const witness_schedule_object& wso = get(witness_schedule_id_type());
return uint64_t(GRAPHENE_100_PERCENT) * wso.recent_slots_filled.popcount() / 128;
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
return uint64_t(GRAPHENE_100_PERCENT) * dpo.recent_slots_filled.popcount() / 128;
}
void database::update_witness_schedule()
{
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
const global_property_object& gpo = get_global_properties();
if( head_block_num() % gpo.active_witnesses.size() == 0 )
{
modify( wso, [&]( witness_schedule_object& _wso )
{
_wso.current_shuffled_witnesses.clear();
_wso.current_shuffled_witnesses.reserve( gpo.active_witnesses.size() );
for( const witness_id_type& w : gpo.active_witnesses )
_wso.current_shuffled_witnesses.push_back( w );
auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32;
for( uint32_t i = 0; i < _wso.current_shuffled_witnesses.size(); ++i )
{
/// High performance random generator
/// http://xorshift.di.unimi.it/
uint64_t k = now_hi + uint64_t(i)*2685821657736338717ULL;
k ^= (k >> 12);
k ^= (k << 25);
k ^= (k >> 27);
k *= 2685821657736338717ULL;
uint32_t jmax = _wso.current_shuffled_witnesses.size() - i;
uint32_t j = i + k%jmax;
std::swap( _wso.current_shuffled_witnesses[i],
_wso.current_shuffled_witnesses[j] );
}
});
}
}
} }

View file

@ -34,7 +34,7 @@ database& generic_evaluator::db()const { return trx_state->db(); }
operation_result generic_evaluator::start_evaluate( transaction_evaluation_state& eval_state, const operation& op, bool apply )
{ try {
trx_state = &eval_state;
check_required_authorities(op);
//check_required_authorities(op);
auto result = evaluate( op );
if( apply ) result = this->apply( op );
@ -77,53 +77,4 @@ database& generic_evaluator::db()const { return trx_state->db(); }
});
} FC_CAPTURE_AND_RETHROW() }
bool generic_evaluator::verify_authority( const account_object& a, authority::classification c )
{ try {
return trx_state->check_authority( a, c );
} FC_CAPTURE_AND_RETHROW( (a)(c) ) }
void generic_evaluator::check_required_authorities(const operation& op)
{ try {
flat_set<account_id_type> active_auths;
flat_set<account_id_type> owner_auths;
vector<authority> other_auths;
operation_get_required_authorities( op, active_auths, owner_auths, other_auths );
for( auto id : active_auths )
{
GRAPHENE_ASSERT(
verify_authority(id(db()), authority::active) ||
verify_authority(id(db()), authority::owner),
tx_missing_active_auth,
"missing required active authority ${id}", ("id", id));
}
for( auto id : owner_auths )
{
GRAPHENE_ASSERT(
verify_authority(id(db()), authority::owner),
tx_missing_owner_auth,
"missing required owner authority ${id}", ("id", id));
}
for( const auto& auth : other_auths )
{
GRAPHENE_ASSERT(
trx_state->check_authority(auth),
tx_missing_other_auth,
"missing required authority ${auth}",
("auth",auth)("sigs",trx_state->_sigs));
}
} FC_CAPTURE_AND_RETHROW( (op) ) }
void generic_evaluator::verify_authority_accounts( const authority& a )const
{
const auto& chain_params = db().get_global_properties().parameters;
FC_ASSERT( a.num_auths() <= chain_params.maximum_authority_membership );
for( const auto& acnt : a.account_auths )
acnt.first(db());
}
} }

View file

@ -16,7 +16,9 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <graphene/chain/fork_database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <fc/smart_ref_impl.hpp>
namespace graphene { namespace chain {
fork_database::fork_database()
@ -33,48 +35,141 @@ void fork_database::pop_block()
if( _head ) _head = _head->prev.lock();
}
void fork_database::start_block( signed_block b )
void fork_database::start_block(signed_block b)
{
auto item = std::make_shared<fork_item>( std::move(b) );
_index.insert( item );
auto item = std::make_shared<fork_item>(std::move(b));
_index.insert(item);
_head = item;
}
shared_ptr<fork_item> fork_database::push_block( const signed_block& b )
/**
* Pushes the block into the fork database and caches it if it doesn't link
*
*/
shared_ptr<fork_item> fork_database::push_block(const signed_block& b)
{
auto item = std::make_shared<fork_item>( b );
if( _head && b.previous != block_id_type() )
auto item = std::make_shared<fork_item>(b);
try {
_push_block(item);
}
catch ( const unlinkable_block_exception& e )
{
auto itr = _index.get<block_id>().find( b.previous );
FC_ASSERT( itr != _index.get<block_id>().end() );
FC_ASSERT( !(*itr)->invalid );
wlog( "Pushing block to fork database that failed to link: ${id}, ${num}", ("id",b.id())("num",b.block_num()) );
wlog( "Head: ${num}, ${id}", ("num",_head->data.block_num())("id",_head->data.id()) );
throw;
_unlinked_index.insert( item );
}
return _head;
}
void fork_database::_push_block(const item_ptr& item)
{
if( _head ) // make sure the block is within the range that we are caching
{
FC_ASSERT( item->num > std::max<int64_t>( 0, int64_t(_head->num) - (_max_size) ),
"attempting to push a block that is too old",
("item->num",item->num)("head",_head->num)("max_size",_max_size));
FC_ASSERT( item->num < _head->num + MAX_BLOCK_REORDERING );
}
if( _head && item->previous_id() != block_id_type() )
{
auto& index = _index.get<block_id>();
auto itr = index.find(item->previous_id());
GRAPHENE_ASSERT(itr != index.end(), unlinkable_block_exception, "block does not link to known chain");
FC_ASSERT(!(*itr)->invalid);
item->prev = *itr;
}
_index.insert( item );
_index.insert(item);
if( !_head ) _head = item;
else if( item->num > _head->num )
{
_head = item;
_index.get<block_num>().erase( _head->num - 1024 );
_index.get<block_num>().erase(_head->num - _max_size);
_unlinked_index.get<block_num>().erase(_head->num - _max_size);
}
return _head;
_push_next( item );
}
bool fork_database::is_known_block( const block_id_type& id )const
/**
* Iterate through the unlinked cache and insert anything that
* links to the newly inserted item. This will start a recursive
* set of calls performing a depth-first insertion of pending blocks as
* _push_next(..) calls _push_block(...) which will in turn call _push_next
*/
void fork_database::_push_next( const item_ptr& new_item )
{
auto& prev_idx = _unlinked_index.get<by_previous>();
auto itr = prev_idx.find( new_item->id );
while( itr != prev_idx.end() )
{
auto tmp = *itr;
prev_idx.erase( itr );
_push_block( tmp );
itr = prev_idx.find( new_item->id );
}
}
void fork_database::set_max_size( uint32_t s )
{
_max_size = s;
if( !_head ) return;
{ /// index
auto& by_num_idx = _index.get<block_num>();
auto itr = by_num_idx.begin();
while( itr != by_num_idx.end() )
{
if( (*itr)->num < std::max(int64_t(0),int64_t(_head->num) - _max_size) )
by_num_idx.erase(itr);
else
break;
itr = by_num_idx.begin();
}
}
{ /// unlinked_index
auto& by_num_idx = _unlinked_index.get<block_num>();
auto itr = by_num_idx.begin();
while( itr != by_num_idx.end() )
{
if( (*itr)->num < std::max(int64_t(0),int64_t(_head->num) - _max_size) )
by_num_idx.erase(itr);
else
break;
itr = by_num_idx.begin();
}
}
}
bool fork_database::is_known_block(const block_id_type& id)const
{
auto& index = _index.get<block_id>();
auto itr = index.find(id);
return itr != index.end();
if( itr != index.end() )
return true;
auto& unlinked_index = _unlinked_index.get<block_id>();
auto unlinked_itr = unlinked_index.find(id);
return unlinked_itr != unlinked_index.end();
}
item_ptr fork_database::fetch_block( const block_id_type& id )const
item_ptr fork_database::fetch_block(const block_id_type& id)const
{
auto itr = _index.get<block_id>().find(id);
if( itr != _index.get<block_id>().end() )
auto& index = _index.get<block_id>();
auto itr = index.find(id);
if( itr != index.end() )
return *itr;
auto& unlinked_index = _unlinked_index.get<block_id>();
auto unlinked_itr = unlinked_index.find(id);
if( unlinked_itr != unlinked_index.end() )
return *unlinked_itr;
return item_ptr();
}
vector<item_ptr> fork_database::fetch_block_by_number( uint32_t num )const
vector<item_ptr> fork_database::fetch_block_by_number(uint32_t num)const
{
vector<item_ptr> result;
auto itr = _index.get<block_num>().find(num);
@ -90,48 +185,55 @@ vector<item_ptr> fork_database::fetch_block_by_number( uint32_t num )const
}
pair<fork_database::branch_type,fork_database::branch_type>
fork_database::fetch_branch_from( block_id_type first, block_id_type second )const
fork_database::fetch_branch_from(block_id_type first, block_id_type second)const
{ try {
// This function gets a branch (i.e. vector<fork_item>) leading
// back to the most recent common ancestor.
pair<branch_type,branch_type> result;
auto first_branch_itr = _index.get<block_id>().find(first);
FC_ASSERT( first_branch_itr != _index.get<block_id>().end() );
FC_ASSERT(first_branch_itr != _index.get<block_id>().end());
auto first_branch = *first_branch_itr;
auto second_branch_itr = _index.get<block_id>().find(second);
FC_ASSERT( second_branch_itr != _index.get<block_id>().end() );
FC_ASSERT(second_branch_itr != _index.get<block_id>().end());
auto second_branch = *second_branch_itr;
while( first_branch->data.block_num() > second_branch->data.block_num() )
{
result.first.push_back( first_branch );
first_branch = first_branch->prev.lock(); FC_ASSERT( first_branch );
result.first.push_back(first_branch);
first_branch = first_branch->prev.lock();
FC_ASSERT(first_branch);
}
while( second_branch->data.block_num() > first_branch->data.block_num() )
{
result.second.push_back( second_branch );
second_branch = second_branch->prev.lock(); FC_ASSERT( second_branch );
second_branch = second_branch->prev.lock();
FC_ASSERT(second_branch);
}
while( first_branch->data.previous != second_branch->data.previous )
{
result.first.push_back( first_branch );
result.second.push_back( second_branch );
first_branch = first_branch->prev.lock(); FC_ASSERT( first_branch );
second_branch = second_branch->prev.lock(); FC_ASSERT( second_branch );
result.first.push_back(first_branch);
result.second.push_back(second_branch);
first_branch = first_branch->prev.lock();
FC_ASSERT(first_branch);
second_branch = second_branch->prev.lock();
FC_ASSERT(second_branch);
}
if( first_branch && second_branch )
{
result.first.push_back( first_branch );
result.second.push_back( second_branch );
result.first.push_back(first_branch);
result.second.push_back(second_branch);
}
return result;
} FC_CAPTURE_AND_RETHROW( (first)(second) ) }
void fork_database::set_head( shared_ptr<fork_item> h )
void fork_database::set_head(shared_ptr<fork_item> h)
{
_head = h;
}
void fork_database::remove( block_id_type id )
void fork_database::remove(block_id_type id)
{
_index.get<block_id>().erase(id);
}

View file

@ -0,0 +1,15 @@
#include <graphene/chain/genesis_state.hpp>
// these are required to serialize a genesis_state
#include <fc/smart_ref_impl.hpp> // required for gcc in release mode
#include <graphene/chain/protocol/fee_schedule.hpp>
namespace graphene { namespace chain {
chain_id_type genesis_state_type::compute_chain_id() const
{
return fc::sha256::hash( *this );
}
} } // graphene::chain

View file

@ -0,0 +1,109 @@
/*
* Copyright (c) 2015, Cryptonomex, Inc.
* All rights reserved.
*
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
* are permitted until September 8, 2015, provided that the following conditions are met:
*
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <graphene/chain/get_config.hpp>
#include <graphene/chain/config.hpp>
#include <graphene/chain/protocol/types.hpp>
namespace graphene { namespace chain {
fc::variant_object get_config()
{
fc::mutable_variant_object result;
result[ "GRAPHENE_SYMBOL" ] = GRAPHENE_SYMBOL;
result[ "GRAPHENE_ADDRESS_PREFIX" ] = GRAPHENE_ADDRESS_PREFIX;
result[ "GRAPHENE_MIN_ACCOUNT_NAME_LENGTH" ] = GRAPHENE_MIN_ACCOUNT_NAME_LENGTH;
result[ "GRAPHENE_MAX_ACCOUNT_NAME_LENGTH" ] = GRAPHENE_MAX_ACCOUNT_NAME_LENGTH;
result[ "GRAPHENE_MIN_ASSET_SYMBOL_LENGTH" ] = GRAPHENE_MIN_ASSET_SYMBOL_LENGTH;
result[ "GRAPHENE_MAX_ASSET_SYMBOL_LENGTH" ] = GRAPHENE_MAX_ASSET_SYMBOL_LENGTH;
result[ "GRAPHENE_MAX_ASSET_NAME_LENGTH" ] = GRAPHENE_MAX_ASSET_NAME_LENGTH;
result[ "GRAPHENE_MAX_SHARE_SUPPLY" ] = GRAPHENE_MAX_SHARE_SUPPLY;
result[ "GRAPHENE_MAX_PAY_RATE" ] = GRAPHENE_MAX_PAY_RATE;
result[ "GRAPHENE_MAX_SIG_CHECK_DEPTH" ] = GRAPHENE_MAX_SIG_CHECK_DEPTH;
result[ "GRAPHENE_MIN_TRANSACTION_SIZE_LIMIT" ] = GRAPHENE_MIN_TRANSACTION_SIZE_LIMIT;
result[ "GRAPHENE_MIN_BLOCK_INTERVAL" ] = GRAPHENE_MIN_BLOCK_INTERVAL;
result[ "GRAPHENE_MAX_BLOCK_INTERVAL" ] = GRAPHENE_MAX_BLOCK_INTERVAL;
result[ "GRAPHENE_DEFAULT_BLOCK_INTERVAL" ] = GRAPHENE_DEFAULT_BLOCK_INTERVAL;
result[ "GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE" ] = GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE;
result[ "GRAPHENE_DEFAULT_MAX_BLOCK_SIZE" ] = GRAPHENE_DEFAULT_MAX_BLOCK_SIZE;
result[ "GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION" ] = GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION;
result[ "GRAPHENE_DEFAULT_MAINTENANCE_INTERVAL" ] = GRAPHENE_DEFAULT_MAINTENANCE_INTERVAL;
result[ "GRAPHENE_DEFAULT_MAINTENANCE_SKIP_SLOTS" ] = GRAPHENE_DEFAULT_MAINTENANCE_SKIP_SLOTS;
result[ "GRAPHENE_MIN_UNDO_HISTORY" ] = GRAPHENE_MIN_UNDO_HISTORY;
result[ "GRAPHENE_MAX_UNDO_HISTORY" ] = GRAPHENE_MAX_UNDO_HISTORY;
result[ "GRAPHENE_MIN_BLOCK_SIZE_LIMIT" ] = GRAPHENE_MIN_BLOCK_SIZE_LIMIT;
result[ "GRAPHENE_MIN_TRANSACTION_EXPIRATION_LIMIT" ] = GRAPHENE_MIN_TRANSACTION_EXPIRATION_LIMIT;
result[ "GRAPHENE_BLOCKCHAIN_PRECISION" ] = GRAPHENE_BLOCKCHAIN_PRECISION;
result[ "GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS" ] = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS;
result[ "GRAPHENE_DEFAULT_TRANSFER_FEE" ] = GRAPHENE_DEFAULT_TRANSFER_FEE;
result[ "GRAPHENE_MAX_INSTANCE_ID" ] = GRAPHENE_MAX_INSTANCE_ID;
result[ "GRAPHENE_100_PERCENT" ] = GRAPHENE_100_PERCENT;
result[ "GRAPHENE_1_PERCENT" ] = GRAPHENE_1_PERCENT;
result[ "GRAPHENE_MAX_MARKET_FEE_PERCENT" ] = GRAPHENE_MAX_MARKET_FEE_PERCENT;
result[ "GRAPHENE_DEFAULT_FORCE_SETTLEMENT_DELAY" ] = GRAPHENE_DEFAULT_FORCE_SETTLEMENT_DELAY;
result[ "GRAPHENE_DEFAULT_FORCE_SETTLEMENT_OFFSET" ] = GRAPHENE_DEFAULT_FORCE_SETTLEMENT_OFFSET;
result[ "GRAPHENE_DEFAULT_FORCE_SETTLEMENT_MAX_VOLUME" ] = GRAPHENE_DEFAULT_FORCE_SETTLEMENT_MAX_VOLUME;
result[ "GRAPHENE_DEFAULT_PRICE_FEED_LIFETIME" ] = GRAPHENE_DEFAULT_PRICE_FEED_LIFETIME;
result[ "GRAPHENE_MAX_FEED_PRODUCERS" ] = GRAPHENE_MAX_FEED_PRODUCERS;
result[ "GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP" ] = GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP;
result[ "GRAPHENE_DEFAULT_MAX_ASSET_WHITELIST_AUTHORITIES" ] = GRAPHENE_DEFAULT_MAX_ASSET_WHITELIST_AUTHORITIES;
result[ "GRAPHENE_DEFAULT_MAX_ASSET_FEED_PUBLISHERS" ] = GRAPHENE_DEFAULT_MAX_ASSET_FEED_PUBLISHERS;
result[ "GRAPHENE_COLLATERAL_RATIO_DENOM" ] = GRAPHENE_COLLATERAL_RATIO_DENOM;
result[ "GRAPHENE_MIN_COLLATERAL_RATIO" ] = GRAPHENE_MIN_COLLATERAL_RATIO;
result[ "GRAPHENE_MAX_COLLATERAL_RATIO" ] = GRAPHENE_MAX_COLLATERAL_RATIO;
result[ "GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO" ] = GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO;
result[ "GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO" ] = GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO;
result[ "GRAPHENE_DEFAULT_MARGIN_PERIOD_SEC" ] = GRAPHENE_DEFAULT_MARGIN_PERIOD_SEC;
result[ "GRAPHENE_DEFAULT_MAX_WITNESSES" ] = GRAPHENE_DEFAULT_MAX_WITNESSES;
result[ "GRAPHENE_DEFAULT_MAX_COMMITTEE" ] = GRAPHENE_DEFAULT_MAX_COMMITTEE;
result[ "GRAPHENE_DEFAULT_MAX_PROPOSAL_LIFETIME_SEC" ] = GRAPHENE_DEFAULT_MAX_PROPOSAL_LIFETIME_SEC;
result[ "GRAPHENE_DEFAULT_COMMITTEE_PROPOSAL_REVIEW_PERIOD_SEC" ] = GRAPHENE_DEFAULT_COMMITTEE_PROPOSAL_REVIEW_PERIOD_SEC;
result[ "GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE" ] = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
result[ "GRAPHENE_DEFAULT_LIFETIME_REFERRER_PERCENT_OF_FEE" ] = GRAPHENE_DEFAULT_LIFETIME_REFERRER_PERCENT_OF_FEE;
result[ "GRAPHENE_DEFAULT_MAX_BULK_DISCOUNT_PERCENT" ] = GRAPHENE_DEFAULT_MAX_BULK_DISCOUNT_PERCENT;
result[ "GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MIN" ] = GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MIN;
result[ "GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MAX" ] = GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MAX;
result[ "GRAPHENE_DEFAULT_CASHBACK_VESTING_PERIOD_SEC" ] = GRAPHENE_DEFAULT_CASHBACK_VESTING_PERIOD_SEC;
result[ "GRAPHENE_DEFAULT_CASHBACK_VESTING_THRESHOLD" ] = GRAPHENE_DEFAULT_CASHBACK_VESTING_THRESHOLD;
result[ "GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE" ] = GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE;
result[ "GRAPHENE_WITNESS_PAY_PERCENT_PRECISION" ] = GRAPHENE_WITNESS_PAY_PERCENT_PRECISION;
result[ "GRAPHENE_DEFAULT_MAX_ASSERT_OPCODE" ] = GRAPHENE_DEFAULT_MAX_ASSERT_OPCODE;
result[ "GRAPHENE_DEFAULT_FEE_LIQUIDATION_THRESHOLD" ] = GRAPHENE_DEFAULT_FEE_LIQUIDATION_THRESHOLD;
result[ "GRAPHENE_DEFAULT_ACCOUNTS_PER_FEE_SCALE" ] = GRAPHENE_DEFAULT_ACCOUNTS_PER_FEE_SCALE;
result[ "GRAPHENE_DEFAULT_ACCOUNT_FEE_SCALE_BITSHIFTS" ] = GRAPHENE_DEFAULT_ACCOUNT_FEE_SCALE_BITSHIFTS;
result[ "GRAPHENE_MAX_WORKER_NAME_LENGTH" ] = GRAPHENE_MAX_WORKER_NAME_LENGTH;
result[ "GRAPHENE_MAX_URL_LENGTH" ] = GRAPHENE_MAX_URL_LENGTH;
result[ "GRAPHENE_NEAR_SCHEDULE_CTR_IV" ] = GRAPHENE_NEAR_SCHEDULE_CTR_IV;
result[ "GRAPHENE_FAR_SCHEDULE_CTR_IV" ] = GRAPHENE_FAR_SCHEDULE_CTR_IV;
result[ "GRAPHENE_CORE_ASSET_CYCLE_RATE" ] = GRAPHENE_CORE_ASSET_CYCLE_RATE;
result[ "GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS" ] = GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS;
result[ "GRAPHENE_DEFAULT_WITNESS_PAY_PER_BLOCK" ] = GRAPHENE_DEFAULT_WITNESS_PAY_PER_BLOCK;
result[ "GRAPHENE_DEFAULT_WITNESS_PAY_VESTING_SECONDS" ] = GRAPHENE_DEFAULT_WITNESS_PAY_VESTING_SECONDS;
result[ "GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY" ] = GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY;
result[ "GRAPHENE_MAX_INTEREST_APR" ] = GRAPHENE_MAX_INTEREST_APR;
result[ "GRAPHENE_COMMITTEE_ACCOUNT" ] = GRAPHENE_COMMITTEE_ACCOUNT;
result[ "GRAPHENE_WITNESS_ACCOUNT" ] = GRAPHENE_WITNESS_ACCOUNT;
result[ "GRAPHENE_RELAXED_COMMITTEE_ACCOUNT" ] = GRAPHENE_RELAXED_COMMITTEE_ACCOUNT;
result[ "GRAPHENE_NULL_ACCOUNT" ] = GRAPHENE_NULL_ACCOUNT;
result[ "GRAPHENE_TEMP_ACCOUNT" ] = GRAPHENE_TEMP_ACCOUNT;
return result;
}
} } // graphene::chain

View file

@ -38,6 +38,8 @@ namespace graphene { namespace chain {
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_account_statistics_object_type;
account_id_type owner;
/**
* Keep the most recent operation as a root pointer to a linked list of the transaction history. This field is
* not required by core validation and could in theory be made an annotation on the account object, but
@ -74,8 +76,6 @@ namespace graphene { namespace chain {
*/
share_type pending_vested_fees;
/// @brief Calculate the percentage discount this user receives on his fees
uint16_t calculate_bulk_discount_percent(const chain_parameters& params)const;
/// @brief Split up and pay out @ref pending_fees and @ref pending_vested_fees
void process_fees(const account_object& a, database& d) const;
};
@ -250,14 +250,18 @@ namespace graphene { namespace chain {
/** given an account or key, map it to the set of accounts that reference it in an active or owner authority */
map< account_id_type, set<account_id_type> > account_to_account_memberships;
map< public_key_type, set<account_id_type> > account_to_key_memberships;
/** some accounts use address authorities in the genesis block */
map< address, set<account_id_type> > account_to_address_memberships;
protected:
set<account_id_type> get_account_members( const account_object& a )const;
set<public_key_type> get_key_members( const account_object& a )const;
set<address> get_address_members( const account_object& a )const;
set<account_id_type> before_account_members;
set<public_key_type> before_key_members;
set<address> before_address_members;
};
/**
@ -337,6 +341,7 @@ FC_REFLECT_DERIVED( graphene::chain::meta_account_object,
(memo_key)(committee_member_id) )
FC_REFLECT_DERIVED( graphene::chain::account_statistics_object, (graphene::chain::object),
(owner)
(most_recent_op)
(total_core_in_orders)
(lifetime_fees_paid)

View file

@ -56,6 +56,7 @@ namespace graphene { namespace chain {
/// The number of shares currently in existence
share_type current_supply;
share_type confidential_supply; ///< total asset held in confidential balances
share_type accumulated_fees; ///< fees accumulate to be paid out over time
share_type fee_pool; ///< in core asset
};
@ -91,6 +92,7 @@ namespace graphene { namespace chain {
/// @return true if this asset may only be transferred to/from the issuer or market orders
bool is_transfer_restricted()const { return options.flags & transfer_restricted; }
bool can_override()const { return options.flags & override_authority; }
bool allow_confidential()const { return !(options.flags & asset_issuer_permission_flags::disable_confidential); }
/// Helper function to get an asset object with the given amount in this asset's type
asset amount(share_type a)const { return asset(a, id); }
@ -112,7 +114,7 @@ namespace graphene { namespace chain {
/// Ticker symbol for this asset, i.e. "USD"
string symbol;
/// Maximum number of digits after the decimal point (must be <= 12)
uint8_t precision;
uint8_t precision = 0;
/// ID of the account which issued this asset.
account_id_type issuer;
@ -234,7 +236,7 @@ namespace graphene { namespace chain {
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::asset_dynamic_data_object, (graphene::db::object),
(current_supply)(accumulated_fees)(fee_pool) )
(current_supply)(confidential_supply)(accumulated_fees)(fee_pool) )
FC_REFLECT_DERIVED( graphene::chain::asset_bitasset_data_object, (graphene::db::object),
(feeds)

View file

@ -36,6 +36,7 @@ namespace graphene { namespace chain {
optional<signed_block> fetch_optional( const block_id_type& id )const;
optional<signed_block> fetch_by_number( uint32_t block_num )const;
optional<signed_block> last()const;
optional<block_id_type> last_id()const;
private:
mutable std::fstream _blocks;
mutable std::fstream _block_num_to_pos;

View file

@ -36,8 +36,7 @@ namespace graphene { namespace chain {
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_block_summary_object_type;
block_id_type block_id;
fc::time_point_sec timestamp;
block_id_type block_id;
};
} }

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2015, Cryptonomex, Inc.
* All rights reserved.
*
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
* are permitted until September 8, 2015, provided that the following conditions are met:
*
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/immutable_chain_parameters.hpp>
namespace graphene { namespace chain {
class chain_property_object;
/**
* Contains invariants which are set at genesis and never changed.
*/
class chain_property_object : public abstract_object<chain_property_object>
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_chain_property_object_type;
chain_id_type chain_id;
immutable_chain_parameters immutable_parameters;
};
} }
FC_REFLECT_DERIVED( graphene::chain::chain_property_object, (graphene::db::object),
(chain_id)
(immutable_parameters)
)

View file

@ -30,6 +30,15 @@ namespace graphene { namespace chain {
object_id_type do_apply( const committee_member_create_operation& o );
};
class committee_member_update_evaluator : public evaluator<committee_member_update_evaluator>
{
public:
typedef committee_member_update_operation operation_type;
void_result do_evaluate( const committee_member_update_operation& o );
void_result do_apply( const committee_member_update_operation& o );
};
class committee_member_update_global_parameters_evaluator : public evaluator<committee_member_update_global_parameters_evaluator>
{
public:
@ -39,5 +48,4 @@ namespace graphene { namespace chain {
void_result do_apply( const committee_member_update_global_parameters_operation& o );
};
} } // graphene::chain

View file

@ -44,10 +44,12 @@ namespace graphene { namespace chain {
account_id_type committee_member_account;
vote_id_type vote_id;
uint64_t total_votes = 0;
string url;
};
struct by_account;
struct by_vote_id;
using committee_member_multi_index_type = multi_index_container<
committee_member_object,
indexed_by<
@ -56,6 +58,9 @@ namespace graphene { namespace chain {
>,
hashed_unique< tag<by_account>,
member<committee_member_object, account_id_type, &committee_member_object::committee_member_account>
>,
hashed_unique< tag<by_vote_id>,
member<committee_member_object, vote_id_type, &committee_member_object::vote_id>
>
>
>;
@ -63,4 +68,4 @@ namespace graphene { namespace chain {
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::committee_member_object, (graphene::db::object),
(committee_member_account)(vote_id)(url) )
(committee_member_account)(vote_id)(total_votes)(url) )

View file

@ -0,0 +1,71 @@
#pragma once
#include <graphene/chain/evaluator.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
namespace graphene { namespace chain {
/**
* @class blinded_balance_object
* @brief tracks a blinded balance commitment
* @ingroup object
* @ingroup protocol
*/
class blinded_balance_object : public graphene::db::abstract_object<blinded_balance_object>
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_blinded_balance_object_type;
fc::ecc::commitment_type commitment;
asset_id_type asset_id;
authority owner;
};
struct by_asset;
struct by_owner;
struct by_commitment;
/**
* @ingroup object_index
*/
typedef multi_index_container<
blinded_balance_object,
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_unique< tag<by_commitment>, member<blinded_balance_object, commitment_type, &blinded_balance_object::commitment> >
>
> blinded_balance_object_multi_index_type;
typedef generic_index<blinded_balance_object, blinded_balance_object_multi_index_type> blinded_balance_index;
class transfer_to_blind_evaluator : public evaluator<transfer_to_blind_evaluator>
{
public:
typedef transfer_to_blind_operation operation_type;
void_result do_evaluate( const transfer_to_blind_operation& o );
void_result do_apply( const transfer_to_blind_operation& o ) ;
};
class transfer_from_blind_evaluator : public evaluator<transfer_from_blind_evaluator>
{
public:
typedef transfer_from_blind_operation operation_type;
void_result do_evaluate( const transfer_from_blind_operation& o );
void_result do_apply( const transfer_from_blind_operation& o ) ;
};
class blind_transfer_evaluator : public evaluator<blind_transfer_evaluator>
{
public:
typedef blind_transfer_operation operation_type;
void_result do_evaluate( const blind_transfer_operation& o );
void_result do_apply( const blind_transfer_operation& o ) ;
};
} } // namespace graphene::chain
FC_REFLECT( graphene::chain::blinded_balance_object, (commitment)(asset_id)(owner) )

View file

@ -20,7 +20,7 @@
#define GRAPHENE_SYMBOL "CORE"
#define GRAPHENE_ADDRESS_PREFIX "GPH"
#define GRAPHENE_MIN_ACCOUNT_NAME_LENGTH 1
#define GRAPHENE_MIN_ACCOUNT_NAME_LENGTH 3
#define GRAPHENE_MAX_ACCOUNT_NAME_LENGTH 63
#define GRAPHENE_MIN_ASSET_SYMBOL_LENGTH 3
@ -31,8 +31,6 @@
#define GRAPHENE_MAX_SHARE_SUPPLY int64_t(1000000000000000ll)
#define GRAPHENE_MAX_PAY_RATE 10000 /* 100% */
#define GRAPHENE_MAX_SIG_CHECK_DEPTH 2
#define GRAPHENE_MIN_WITNESS_COUNT 10
#define GRAPHENE_MIN_COMMITTEE_MEMBER_COUNT 10
/**
* Don't allow the committee_members to publish a limit that would
* make the network unable to operate.
@ -46,12 +44,14 @@
#define GRAPHENE_DEFAULT_MAX_BLOCK_SIZE (GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE*GRAPHENE_DEFAULT_BLOCK_INTERVAL*200000)
#define GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION (60*60*24) // seconds, aka: 1 day
#define GRAPHENE_DEFAULT_MAINTENANCE_INTERVAL (60*60*24) // seconds, aka: 1 day
#define GRAPHENE_DEFAULT_MAX_UNDO_HISTORY 1024
#define GRAPHENE_DEFAULT_MAINTENANCE_SKIP_SLOTS 3 // number of slots to skip for maintenance interval
#define GRAPHENE_MIN_UNDO_HISTORY 10
#define GRAPHENE_MAX_UNDO_HISTORY 1000
#define GRAPHENE_MIN_BLOCK_SIZE_LIMIT (GRAPHENE_MIN_TRANSACTION_SIZE_LIMIT*5) // 5 transactions per block
#define GRAPHENE_MIN_TRANSACTION_EXPIRATION_LIMIT (GRAPHENE_MAX_BLOCK_INTERVAL * 5) // 5 transactions per block
#define GRAPHENE_BLOCKCHAIN_PRECISION uint64_t( 100000 )
#define CORE GRAPHENE_BLOCKCHAIN_PRECISION
#define GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS 5
#define GRAPHENE_DEFAULT_TRANSFER_FEE (1*GRAPHENE_BLOCKCHAIN_PRECISION)
@ -84,8 +84,8 @@
///@}
#define GRAPHENE_DEFAULT_MARGIN_PERIOD_SEC (30*60*60*24)
#define GRAPHENE_DEFAULT_NUM_WITNESSES (101)
#define GRAPHENE_DEFAULT_NUM_COMMITTEE (11)
#define GRAPHENE_DEFAULT_MIN_WITNESS_COUNT (11)
#define GRAPHENE_DEFAULT_MIN_COMMITTEE_MEMBER_COUNT (11)
#define GRAPHENE_DEFAULT_MAX_WITNESSES (1001) // SHOULD BE ODD
#define GRAPHENE_DEFAULT_MAX_COMMITTEE (1001) // SHOULD BE ODD
#define GRAPHENE_DEFAULT_MAX_PROPOSAL_LIFETIME_SEC (60*60*24*7*4) // Four weeks
@ -121,10 +121,6 @@
| (uint64_t( 0x84ca ) << 0x10) \
| (uint64_t( 0xa73b ) ) )
// counter used to determine bits of entropy
// must be less than or equal to secret_hash_type::data_length()
#define GRAPHENE_RNG_SEED_LENGTH (160 / 8)
/**
* every second, the fraction of burned core asset which cycles is
* GRAPHENE_CORE_ASSET_CYCLE_RATE / (1 << GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS)
@ -133,10 +129,14 @@
#define GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS 32
#define GRAPHENE_DEFAULT_WITNESS_PAY_PER_BLOCK (GRAPHENE_BLOCKCHAIN_PRECISION * int64_t( 10) )
#define GRAPHENE_DEFAULT_WITNESS_PAY_VESTING_SECONDS (60*60*24)
#define GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY (GRAPHENE_BLOCKCHAIN_PRECISION * int64_t(500) * 1000 )
#define GRAPHENE_MAX_INTEREST_APR uint16_t( 10000 )
#define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4
#define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3
/**
* Reserved Account IDs with special meaning
*/
@ -151,4 +151,8 @@
#define GRAPHENE_NULL_ACCOUNT (graphene::chain::account_id_type(3))
/// Represents the canonical account with WILDCARD authority (anybody can access funds in temp account)
#define GRAPHENE_TEMP_ACCOUNT (graphene::chain::account_id_type(4))
/// Represents the canonical account for specifying you will vote directly (as opposed to a proxy)
#define GRAPHENE_PROXY_TO_SELF_ACCOUNT (graphene::chain::account_id_type(5))
/// Sentinel value used in the scheduler.
#define GRAPHENE_NULL_WITNESS (graphene::chain::witness_id_type(0))
///@}

View file

@ -40,31 +40,6 @@ namespace graphene { namespace chain {
using graphene::db::abstract_object;
using graphene::db::object;
namespace detail
{
/**
* Class used to help the with_skip_flags implementation.
* It must be defined in this header because it must be
* available to the with_skip_flags implementation,
* which is a template and therefore must also be defined
* in this header.
*/
struct skip_flags_restorer
{
skip_flags_restorer( node_property_object& npo, uint32_t old_skip_flags )
: _npo( npo ), _old_skip_flags( old_skip_flags )
{}
~skip_flags_restorer()
{
_npo.skip_flags = _old_skip_flags;
}
node_property_object& _npo;
uint32_t _old_skip_flags;
};
}
/**
* @class database
* @brief tracks the blockchain state in an extensible manner
@ -82,15 +57,15 @@ namespace graphene { namespace chain {
skip_nothing = 0,
skip_witness_signature = 1 << 0, ///< used while reindexing
skip_transaction_signatures = 1 << 1, ///< used by non-witness nodes
skip_undo_block = 1 << 2, ///< used while reindexing
skip_undo_transaction = 1 << 3, ///< used while applying block
skip_transaction_dupe_check = 1 << 4, ///< used while reindexing
skip_fork_db = 1 << 5, ///< used while reindexing
skip_block_size_check = 1 << 6, ///< used when applying locally generated transactions
skip_tapos_check = 1 << 7, ///< used while reindexing -- note this skips expiration check as well
skip_authority_check = 1 << 8, ///< used while reindexing -- disables any checking of authority on transactions
skip_merkle_check = 1 << 9, ///< used while reindexing
skip_assert_evaluation = 1 << 10 ///< used while reindexing
skip_transaction_dupe_check = 1 << 2, ///< used while reindexing
skip_fork_db = 1 << 3, ///< used while reindexing
skip_block_size_check = 1 << 4, ///< used when applying locally generated transactions
skip_tapos_check = 1 << 5, ///< used while reindexing -- note this skips expiration check as well
skip_authority_check = 1 << 6, ///< used while reindexing -- disables any checking of authority on transactions
skip_merkle_check = 1 << 7, ///< used while reindexing
skip_assert_evaluation = 1 << 8, ///< used while reindexing
skip_undo_history_check = 1 << 9, ///< used while reindexing
skip_witness_schedule_check = 1 << 10 ///< used whiel reindexing
};
/**
@ -104,8 +79,10 @@ namespace graphene { namespace chain {
* @param data_dir Path to open or create database in
* @param genesis_loader A callable object which returns the genesis state to initialize new databases on
*/
template<typename F>
void open(const fc::path& data_dir, F&& genesis_loader);
void open(
const fc::path& data_dir,
std::function<genesis_state_type()> genesis_loader );
/**
* @brief Rebuild object graph from block history and open detabase
*
@ -135,6 +112,7 @@ namespace graphene { namespace chain {
optional<signed_block> fetch_block_by_id( const block_id_type& id )const;
optional<signed_block> fetch_block_by_number( uint32_t num )const;
const signed_transaction& get_recent_transaction( const transaction_id_type& trx_id )const;
std::vector<block_id_type> get_block_ids_on_fork(block_id_type head_of_fork) const;
/**
* Calculate the percent of block production slots that were missed in the
@ -162,7 +140,8 @@ namespace graphene { namespace chain {
signed_block _generate_block(
const fc::time_point_sec when,
witness_id_type witness_id,
const fc::ecc::private_key& block_signing_private_key
const fc::ecc::private_key& block_signing_private_key,
bool retry_on_failure
);
void pop_block();
@ -193,12 +172,23 @@ namespace graphene { namespace chain {
*/
fc::signal<void(const signed_block&)> applied_block;
/**
* This signal is emitted any time a new transaction is added to the pending
* block state.
*/
fc::signal<void(const signed_transaction&)> on_pending_transaction;
/**
* Emitted After a block has been applied and committed. The callback
* should not yield and should execute quickly.
*/
fc::signal<void(const vector<object_id_type>&)> changed_objects;
/** this signal is emitted any time an object is removed and contains a
* pointer to the last value of every object that was removed.
*/
fc::signal<void(const vector<const object*>&)> removed_objects;
//////////////////// db_witness_schedule.cpp ////////////////////
/**
@ -213,11 +203,9 @@ namespace graphene { namespace chain {
* Use the get_slot_time() and get_slot_at_time() functions
* to convert between slot_num and timestamp.
*
* Passing slot_num == 0 returns (witness_id_type(), false)
*
* The bool value is true if near schedule, false if far schedule.
* Passing slot_num == 0 returns GRAPHENE_NULL_WITNESS
*/
pair<witness_id_type, bool> get_scheduled_witness(uint32_t slot_num)const;
witness_id_type get_scheduled_witness(uint32_t slot_num)const;
/**
* Get the time at which the given slot occurs.
@ -239,14 +227,13 @@ namespace graphene { namespace chain {
*/
uint32_t get_slot_at_time(fc::time_point_sec when)const;
/**
* Get the near schedule.
*/
vector<witness_id_type> get_near_witness_schedule()const;
void update_witness_schedule();
//////////////////// db_getter.cpp ////////////////////
const chain_id_type& get_chain_id()const;
const asset_object& get_core_asset()const;
const chain_property_object& get_chain_properties()const;
const global_property_object& get_global_properties()const;
const dynamic_global_property_object& get_dynamic_global_properties()const;
const node_property_object& get_node_properties()const;
@ -261,23 +248,8 @@ namespace graphene { namespace chain {
node_property_object& node_properties();
/**
* Set the skip_flags to the given value, call callback,
* then reset skip_flags to their previous value after
* callback is done.
*/
template< typename Lambda >
void with_skip_flags(
uint32_t skip_flags,
Lambda callback )
{
node_property_object& npo = node_properties();
detail::skip_flags_restorer restorer( npo, npo.skip_flags );
npo.skip_flags = skip_flags;
callback();
return;
}
uint32_t last_non_undoable_block_num() const;
//////////////////// db_init.cpp ////////////////////
void initialize_evaluators();
@ -318,16 +290,31 @@ namespace graphene { namespace chain {
* @param delta Asset ID and amount to adjust balance by
*/
void adjust_balance(account_id_type account, asset delta);
/// This is an overloaded method.
void adjust_balance(const account_object& account, asset delta);
/**
* If delta.asset_id is a core asset, adjusts account statistics
* @brief Helper to make lazy deposit to CDD VBO.
*
* If the given optional VBID is not valid(),
* or it does not have a CDD vesting policy,
* or the owner / vesting_seconds of the policy
* does not match the parameter, then credit amount
* to newly created VBID and return it.
*
* Otherwise, credit amount to ovbid.
*
* @return ID of newly created VBO, but only if VBO was created.
*/
void adjust_core_in_orders( const account_object& acnt, asset delta );
optional< vesting_balance_id_type > deposit_lazy_vesting(
const optional< vesting_balance_id_type >& ovbid,
share_type amount,
uint32_t req_vesting_seconds,
account_id_type req_owner,
bool require_vesting );
// helper to handle cashback rewards
void deposit_cashback(const account_object& acct, share_type amount, bool require_vesting = true);
// helper to handle witness pay
void deposit_witness_pay(const witness_object& wit, share_type amount);
//////////////////// db_debug.cpp ////////////////////
@ -386,7 +373,18 @@ namespace graphene { namespace chain {
asset calculate_market_fee(const asset_object& recv_asset, const asset& trade_amount);
asset pay_market_fees( const asset_object& recv_asset, const asset& receives );
///@}
/**
* This method validates transactions without adding it to the pending state.
* @return true if the transaction would validate
*/
processed_transaction validate_transaction( const signed_transaction& trx );
/** when popping a block, the transactions that were removed get cached here so they
* can be reapplied at the proper time */
std::deque< signed_transaction > _popped_tx;
/**
* @}
@ -397,7 +395,7 @@ namespace graphene { namespace chain {
void notify_changed_objects();
private:
optional<undo_database::session> _pending_block_session;
optional<undo_database::session> _pending_tx_session;
vector< unique_ptr<op_evaluator> > _operation_evaluators;
template<class Index>
@ -411,6 +409,7 @@ namespace graphene { namespace chain {
processed_transaction _apply_transaction( const signed_transaction& trx );
operation_result apply_operation( transaction_evaluation_state& eval_state, const operation& op );
///Steps involved in applying a new block
///@{
@ -421,16 +420,13 @@ namespace graphene { namespace chain {
//////////////////// db_update.cpp ////////////////////
void update_global_dynamic_data( const signed_block& b );
void update_signing_witness(const witness_object& signing_witness, const signed_block& new_block);
void update_pending_block(const signed_block& next_block, uint8_t current_block_interval);
void clear_expired_transactions();
void clear_expired_proposals();
void clear_expired_orders();
void update_expired_feeds();
void update_maintenance_flag( bool new_maintenance_flag );
void update_withdraw_permissions();
//////////////////// db_witness_schedule.cpp ////////////////////
void update_witness_schedule(const signed_block& next_block); /// no-op except for scheduling blocks
///Steps performed only at maintenance intervals
///@{
@ -442,13 +438,14 @@ namespace graphene { namespace chain {
void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props);
void update_active_witnesses();
void update_active_committee_members();
void update_worker_votes();
template<class... Types>
void perform_account_maintenance(std::tuple<Types...> helpers);
///@}
///@}
signed_block _pending_block;
vector< processed_transaction > _pending_tx;
fork_database _fork_db;
/**
@ -504,23 +501,4 @@ namespace graphene { namespace chain {
}
}
template<typename F>
void database::open(const fc::path& data_dir, F&& genesis_loader)
{ try {
object_database::open(data_dir);
_block_id_to_block.open(data_dir / "database" / "block_num_to_block");
if( !find(global_property_id_type()) )
init_genesis(genesis_loader());
_pending_block.previous = head_block_id();
_pending_block.timestamp = head_block_time();
auto last_block= _block_id_to_block.last();
if( last_block.valid() )
_fork_db.start_block( *last_block );
} FC_CAPTURE_AND_RETHROW( (data_dir) ) }
} }

View file

@ -0,0 +1,144 @@
/*
* Copyright (c) 2015, Cryptonomex, Inc.
* All rights reserved.
*
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
* are permitted until September 8, 2015, provided that the following conditions are met:
*
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/database.hpp>
/*
* This file provides with() functions which modify the database
* temporarily, then restore it. These functions are mostly internal
* implementation detail of the database.
*
* Essentially, we want to be able to use "finally" to restore the
* database regardless of whether an exception is thrown or not, but there
* is no "finally" in C++. Instead, C++ requires us to create a struct
* and put the finally block in a destructor. Aagh!
*/
namespace graphene { namespace chain { namespace detail {
/**
* Class used to help the with_skip_flags implementation.
* It must be defined in this header because it must be
* available to the with_skip_flags implementation,
* which is a template and therefore must also be defined
* in this header.
*/
struct skip_flags_restorer
{
skip_flags_restorer( node_property_object& npo, uint32_t old_skip_flags )
: _npo( npo ), _old_skip_flags( old_skip_flags )
{}
~skip_flags_restorer()
{
_npo.skip_flags = _old_skip_flags;
}
node_property_object& _npo;
uint32_t _old_skip_flags;
};
/**
* Class used to help the without_pending_transactions
* implementation.
*
* TODO: Change the name of this class to better reflect the fact
* that it restores popped transactions as well as pending transactions.
*/
struct pending_transactions_restorer
{
pending_transactions_restorer( database& db, std::vector<processed_transaction>&& pending_transactions )
: _db(db), _pending_transactions( std::move(pending_transactions) )
{
_db.clear_pending();
}
~pending_transactions_restorer()
{
for( const auto& tx : _db._popped_tx )
{
try {
if( !_db.is_known_transaction( tx.id() ) ) {
// since push_transaction() takes a signed_transaction,
// the operation_results field will be ignored.
_db._push_transaction( tx );
}
} catch ( const fc::exception& ) {
}
}
_db._popped_tx.clear();
for( const processed_transaction& tx : _pending_transactions )
{
try
{
if( !_db.is_known_transaction( tx.id() ) ) {
// since push_transaction() takes a signed_transaction,
// the operation_results field will be ignored.
_db._push_transaction( tx );
}
}
catch( const fc::exception& e )
{
/*
wlog( "Pending transaction became invalid after switching to block ${b} ${t}", ("b", _db.head_block_id())("t",_db.head_block_time()) );
wlog( "The invalid pending transaction caused exception ${e}", ("e", e.to_detail_string() ) );
*/
}
}
}
database& _db;
std::vector< processed_transaction > _pending_transactions;
};
/**
* Set the skip_flags to the given value, call callback,
* then reset skip_flags to their previous value after
* callback is done.
*/
template< typename Lambda >
void with_skip_flags(
database& db,
uint32_t skip_flags,
Lambda callback )
{
node_property_object& npo = db.node_properties();
skip_flags_restorer restorer( npo, npo.skip_flags );
npo.skip_flags = skip_flags;
callback();
return;
}
/**
* Empty pending_transactions, call callback,
* then reset pending_transactions after callback is done.
*
* Pending transactions which no longer validate will be culled.
*/
template< typename Lambda >
void without_pending_transactions(
database& db,
std::vector<processed_transaction>&& pending_transactions,
Lambda callback )
{
pending_transactions_restorer restorer( db, std::move(pending_transactions) );
callback();
return;
}
} } } // graphene::chain::detail

View file

@ -16,6 +16,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/protocol/operations.hpp>
namespace graphene { namespace chain {
@ -84,7 +85,7 @@ namespace graphene { namespace chain {
database& db()const;
void check_required_authorities(const operation& op);
//void check_required_authorities(const operation& op);
protected:
/**
* @brief Fetch objects relevant to fee payer and set pointer members
@ -98,11 +99,8 @@ namespace graphene { namespace chain {
/// Pays the fee and returns the number of CORE asset that were paid.
void pay_fee();
bool verify_authority(const account_object&, authority::classification);
object_id_type get_relative_id( object_id_type rel_id )const;
void verify_authority_accounts( const authority& a )const;
asset fee_from_account;
share_type core_fee_paid;
const account_object* fee_paying_account = nullptr;
@ -193,7 +191,8 @@ namespace graphene { namespace chain {
const auto& op = o.get<typename DerivedEvaluator::operation_type>();
prepare_fee(op.fee_payer(), op.fee);
FC_ASSERT( core_fee_paid >= db().current_fee_schedule().calculate_fee( op ).amount,
GRAPHENE_ASSERT( core_fee_paid >= db().current_fee_schedule().calculate_fee( op ).amount,
insufficient_fee,
"Insufficient Fee Paid",
("core_fee_paid",core_fee_paid)("required",db().current_fee_schedule().calculate_fee( op ).amount) );

View file

@ -68,25 +68,40 @@ namespace graphene { namespace chain {
FC_DECLARE_DERIVED_EXCEPTION( operation_validate_exception, graphene::chain::chain_exception, 3040000, "operation validation exception" )
FC_DECLARE_DERIVED_EXCEPTION( operation_evaluate_exception, graphene::chain::chain_exception, 3050000, "operation evaluation exception" )
FC_DECLARE_DERIVED_EXCEPTION( utility_exception, graphene::chain::chain_exception, 3060000, "utility method exception" )
FC_DECLARE_DERIVED_EXCEPTION( undo_database_exception, graphene::chain::chain_exception, 3070000, "undo database exception" )
FC_DECLARE_DERIVED_EXCEPTION( unlinkable_block_exception, graphene::chain::chain_exception, 3080000, "unlinkable block" )
FC_DECLARE_DERIVED_EXCEPTION( tx_missing_active_auth, graphene::chain::transaction_exception, 3030001, "missing required active authority" )
FC_DECLARE_DERIVED_EXCEPTION( tx_missing_owner_auth, graphene::chain::transaction_exception, 3030002, "missing required owner authority" )
FC_DECLARE_DERIVED_EXCEPTION( tx_missing_other_auth, graphene::chain::transaction_exception, 3030003, "missing required other authority" )
//FC_DECLARE_DERIVED_EXCEPTION( tx_irrelevant_authority, graphene::chain::transaction_exception, 3030004, "irrelevant authority" )
FC_DECLARE_DERIVED_EXCEPTION( tx_irrelevant_sig, graphene::chain::transaction_exception, 3030004, "irrelevant signature included" )
FC_DECLARE_DERIVED_EXCEPTION( tx_duplicate_sig, graphene::chain::transaction_exception, 3030005, "duplicate signature included" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_committee_approval, graphene::chain::transaction_exception, 3030006, "committee account cannot directly approve transaction" )
FC_DECLARE_DERIVED_EXCEPTION( insufficient_fee, graphene::chain::transaction_exception, 3030007, "insufficient fee" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_pts_address, graphene::chain::utility_exception, 3060001, "invalid pts address" )
FC_DECLARE_DERIVED_EXCEPTION( insufficient_feeds, graphene::chain::chain_exception, 37006, "insufficient feeds" )
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( transfer );
FC_DECLARE_DERIVED_EXCEPTION( pop_empty_chain, graphene::chain::undo_database_exception, 3070001, "there are no blocks to pop" )
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( transfer );
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( from_account_not_whitelisted, transfer, 1, "owner mismatch" )
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( to_account_not_whitelisted, transfer, 2, "owner mismatch" )
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( restricted_transfer_asset, transfer, 3, "restricted transfer asset" )
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( limit_order_create );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( limit_order_cancel );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( call_order_update );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_create );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_update );
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( call_order_update );
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( unfilled_margin_call, call_order_update, 1, "Updating call order would trigger a margin call that cannot be fully filled" )
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_create );
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( max_auth_exceeded, account_create, 1, "Exceeds max authority fan-out" )
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( auth_account_not_found, account_create, 2, "Auth account not found" )
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_update );
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( max_auth_exceeded, account_update, 1, "Exceeds max authority fan-out" )
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( auth_account_not_found, account_update, 2, "Auth account not found" )
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_whitelist );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_upgrade );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_transfer );
@ -95,17 +110,18 @@ namespace graphene { namespace chain {
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_update_bitasset );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_update_feed_producers );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_issue );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_reserve );
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_reserve );
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( invalid_on_mia, asset_reserve, 1, "invalid on mia" )
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_fund_fee_pool );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_settle );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_global_settle );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_publish_feed );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( committee_member_create );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( witness_create );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( witness_withdraw_pay );
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( proposal_create );
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( review_period_required, proposal_create, 1, "review_period required" )
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( review_period_insufficient, proposal_create, 2, "review_period insufficient" )
@ -124,15 +140,16 @@ namespace graphene { namespace chain {
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( assert );
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( balance_claim );
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( claimed_too_often, balance_claim, 1, "balance claimed too often" )
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( invalid_claim_amount, balance_claim, 2, "invalid claim amount" )
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( owner_mismatch, balance_claim, 3, "owner mismatch" )
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( override_transfer );
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( not_permitted, override_transfer, 1, "not permitted" )
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( blind_transfer );
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( unknown_commitment, blind_transfer, 1, "Attempting to claim an unknown prior commitment" );
/*
FC_DECLARE_DERIVED_EXCEPTION( addition_overflow, graphene::chain::chain_exception, 30002, "addition overflow" )
FC_DECLARE_DERIVED_EXCEPTION( subtraction_overflow, graphene::chain::chain_exception, 30003, "subtraction overflow" )
@ -221,4 +238,8 @@ namespace graphene { namespace chain {
FC_DECLARE_DERIVED_EXCEPTION( price_multiplication_undefined, graphene::chain::evaluation_error, 38003, "price multiplication undefined product 0*inf" )
*/
#define GRAPHENE_RECODE_EXC( cause_type, effect_type ) \
catch( const cause_type& e ) \
{ throw( effect_type( e.what(), e.get_log() ) ); }
} } // graphene::chain

View file

@ -22,6 +22,8 @@
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/mem_fun.hpp>
namespace graphene { namespace chain {
using boost::multi_index_container;
@ -32,6 +34,8 @@ namespace graphene { namespace chain {
fork_item( signed_block d )
:num(d.block_num()),id(d.id()),data( std::move(d) ){}
block_id_type previous_id()const { return data.previous; }
weak_ptr< fork_item > prev;
uint32_t num;
/**
@ -44,6 +48,7 @@ namespace graphene { namespace chain {
};
typedef shared_ptr<fork_item> item_ptr;
/**
* As long as blocks are pushed in order the fork
* database will maintain a linked tree of all blocks
@ -58,39 +63,55 @@ namespace graphene { namespace chain {
{
public:
typedef vector<item_ptr> branch_type;
/// The maximum number of blocks that may be skipped in an out-of-order push
const static int MAX_BLOCK_REORDERING = 1024;
fork_database();
void reset();
void start_block( signed_block b );
void remove( block_id_type b );
void set_head( shared_ptr<fork_item> h );
bool is_known_block( const block_id_type& id )const;
shared_ptr<fork_item> fetch_block( const block_id_type& id )const;
vector<item_ptr> fetch_block_by_number( uint32_t n )const;
shared_ptr<fork_item> push_block(const signed_block& b );
void start_block(signed_block b);
void remove(block_id_type b);
void set_head(shared_ptr<fork_item> h);
bool is_known_block(const block_id_type& id)const;
shared_ptr<fork_item> fetch_block(const block_id_type& id)const;
vector<item_ptr> fetch_block_by_number(uint32_t n)const;
/**
* @return the new head block ( the longest fork )
*/
shared_ptr<fork_item> push_block(const signed_block& b);
shared_ptr<fork_item> head()const { return _head; }
void pop_block();
/**
* Given two head blocks, return two branches of the fork graph that
* end with a common ancestor (same prior block)
*/
pair< branch_type, branch_type > fetch_branch_from( block_id_type first,
block_id_type second )const;
pair< branch_type, branch_type > fetch_branch_from(block_id_type first,
block_id_type second)const;
struct block_id;
struct block_num;
struct by_previous;
typedef multi_index_container<
item_ptr,
indexed_by<
hashed_unique< tag<block_id>, member< fork_item, block_id_type, &fork_item::id>, std::hash<fc::ripemd160> >,
ordered_non_unique< tag<block_num>, member<fork_item,uint32_t,&fork_item::num> >
hashed_unique<tag<block_id>, member<fork_item, block_id_type, &fork_item::id>, std::hash<fc::ripemd160>>,
hashed_non_unique<tag<by_previous>, const_mem_fun<fork_item, block_id_type, &fork_item::previous_id>, std::hash<fc::ripemd160>>,
ordered_non_unique<tag<block_num>, member<fork_item,uint32_t,&fork_item::num>>
>
> fork_multi_index_type;
void set_max_size( uint32_t s );
private:
/** @return a pointer to the newly pushed item */
void _push_block(const item_ptr& b );
void _push_next(const item_ptr& newly_inserted);
uint32_t _max_size = 1024;
fork_multi_index_type _unlinked_index;
fork_multi_index_type _index;
shared_ptr<fork_item> _head;
};

View file

@ -1,6 +1,10 @@
#pragma once
#include <graphene/chain/protocol/chain_parameters.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/immutable_chain_parameters.hpp>
#include <fc/crypto/sha256.hpp>
#include <string>
#include <vector>
@ -26,36 +30,23 @@ struct genesis_state_type {
bool is_lifetime_member = false;
};
struct initial_asset_type {
struct initial_collateral_position {
address owner;
share_type collateral;
share_type debt;
};
string symbol;
string issuer_name;
string description;
uint8_t precision;
string issuer_name;
share_type max_supply;
uint16_t market_fee_percent;
share_type max_market_fee;
uint16_t issuer_permissions;
uint16_t flags;
share_type accumulated_fees;
struct initial_bitasset_options {
uint32_t feed_lifetime_sec;
uint8_t minimum_feeds;
uint32_t force_settlement_delay_sec;
uint16_t force_settlement_offset_percent;
uint16_t maximum_force_settlement_volume;
string backing_asset_symbol;
struct initial_collateral_position {
address owner;
share_type collateral;
share_type debt;
};
uint16_t maintenance_collateral_ratio;
vector<initial_collateral_position> collateral_records;
};
optional<initial_bitasset_options> bitasset_opts;
share_type initial_accumulated_fees;
bool is_bitasset = false;
vector<initial_collateral_position> collateral_records;
};
struct initial_balance_type {
address owner;
@ -86,30 +77,35 @@ struct genesis_state_type {
};
time_point_sec initial_timestamp;
share_type max_core_supply = GRAPHENE_MAX_SHARE_SUPPLY;
chain_parameters initial_parameters;
immutable_chain_parameters immutable_parameters;
vector<initial_account_type> initial_accounts;
vector<initial_asset_type> initial_assets;
vector<initial_balance_type> initial_balances;
vector<initial_vesting_balance_type> initial_vesting_balances;
int initial_active_witnesses = GRAPHENE_DEFAULT_NUM_WITNESSES;
uint64_t initial_active_witnesses;
vector<initial_witness_type> initial_witness_candidates;
vector<initial_committee_member_type> initial_committee_candidates;
vector<initial_worker_type> initial_worker_candidates;
/**
* Get the chain_id corresponding to this genesis state.
*
* This is the SHA256 serialization of the genesis_state.
*/
chain_id_type compute_chain_id() const;
};
} } // namespace graphene::chain
FC_REFLECT(graphene::chain::genesis_state_type::initial_account_type, (name)(owner_key)(active_key)(is_lifetime_member))
FC_REFLECT(graphene::chain::genesis_state_type::initial_asset_type,
(symbol)(description)(precision)(issuer_name)(max_supply)(market_fee_percent)
(issuer_permissions)(flags)(bitasset_opts)(initial_accumulated_fees))
(symbol)(issuer_name)(description)(precision)(max_supply)(accumulated_fees)(is_bitasset)(collateral_records))
FC_REFLECT(graphene::chain::genesis_state_type::initial_asset_type::initial_bitasset_options,
(feed_lifetime_sec)(minimum_feeds)(force_settlement_delay_sec)(force_settlement_offset_percent)
(maximum_force_settlement_volume)(backing_asset_symbol)(maintenance_collateral_ratio)(collateral_records))
FC_REFLECT(graphene::chain::genesis_state_type::initial_asset_type::initial_bitasset_options::initial_collateral_position,
(collateral)(debt))
FC_REFLECT(graphene::chain::genesis_state_type::initial_asset_type::initial_collateral_position,
(owner)(collateral)(debt))
FC_REFLECT(graphene::chain::genesis_state_type::initial_balance_type,
(owner)(asset_symbol)(amount))
@ -124,6 +120,7 @@ FC_REFLECT(graphene::chain::genesis_state_type::initial_committee_member_type, (
FC_REFLECT(graphene::chain::genesis_state_type::initial_worker_type, (owner_name)(daily_pay))
FC_REFLECT(graphene::chain::genesis_state_type,
(initial_timestamp)(initial_parameters)(initial_accounts)(initial_assets)(initial_balances)
(initial_timestamp)(max_core_supply)(initial_parameters)(initial_accounts)(initial_assets)(initial_balances)
(initial_vesting_balances)(initial_active_witnesses)(initial_witness_candidates)
(initial_committee_candidates)(initial_worker_candidates))
(initial_committee_candidates)(initial_worker_candidates)
(immutable_parameters))

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2015, Cryptonomex, Inc.
* All rights reserved.
*
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
* are permitted until September 8, 2015, provided that the following conditions are met:
*
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <fc/variant_object.hpp>
namespace graphene { namespace chain {
fc::variant_object get_config();
} } // graphene::chain

View file

@ -19,6 +19,7 @@
#include <fc/uint128.hpp>
#include <graphene/chain/protocol/chain_parameters.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/db/object.hpp>
@ -41,17 +42,11 @@ namespace graphene { namespace chain {
chain_parameters parameters;
optional<chain_parameters> pending_parameters;
uint32_t next_available_vote_id = 0;
uint32_t next_available_vote_id = 0;
vector<committee_member_id_type> active_committee_members; // updated once per maintenance interval
flat_set<witness_id_type> active_witnesses; // updated once per maintenance interval
flat_set<witness_id_type> active_witnesses; // updated once per maintenance interval
// n.b. witness scheduling is done by witness_schedule object
flat_set<account_id_type> witness_accounts; // updated once per maintenance interval
fc::sha256 chain_id;
vote_id_type get_next_vote_id(vote_id_type::vote_type type) {
return vote_id_type(type, next_available_vote_id++);
}
flat_set<account_id_type> witness_accounts; // updated once per maintenance interval
};
/**
@ -69,7 +64,6 @@ namespace graphene { namespace chain {
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_dynamic_global_property_object_type;
secret_hash_type random;
uint32_t head_block_number = 0;
block_id_type head_block_id;
time_point_sec time;
@ -77,19 +71,53 @@ namespace graphene { namespace chain {
time_point_sec next_maintenance_time;
time_point_sec last_budget_time;
share_type witness_budget;
uint32_t accounts_registered_this_interval;
/** if the interval changes then how we calculate witness participation will
* also change. Normally witness participation is defined as % of blocks
* produced in the last round which is calculated by dividing the delta
* time between block N and N-NUM_WITNESSES by the block interval to calculate
* the number of blocks produced.
uint32_t accounts_registered_this_interval = 0;
/**
* Every time a block is missed this increases by
* RECENTLY_MISSED_COUNT_INCREMENT,
* every time a block is found it decreases by
* RECENTLY_MISSED_COUNT_DECREMENT. It is
* never less than 0.
*
* If the recently_missed_count hits 2*UNDO_HISTORY then no new blocks may be pushed.
*/
uint32_t first_maintenance_block_with_current_interval = 0;
uint32_t recently_missed_count = 0;
/**
* The current absolute slot number. Equal to the total
* number of slots since genesis. Also equal to the total
* number of missed slots plus head_block_number.
*/
uint64_t current_aslot = 0;
/**
* used to compute witness participation.
*/
fc::uint128_t recent_slots_filled;
/**
* dynamic_flags specifies chain state properties that can be
* expressed in one bit.
*/
uint32_t dynamic_flags = 0;
enum dynamic_flag_bits
{
/**
* If maintenance_flag is set, then the head block is a
* maintenance block. This means
* get_time_slot(1) - head_block_time() will have a gap
* due to maintenance duration.
*
* This flag answers the question, "Was maintenance
* performed in the last call to apply_block()?"
*/
maintenance_flag = 0x01
};
};
}}
FC_REFLECT_DERIVED( graphene::chain::dynamic_global_property_object, (graphene::db::object),
(random)
(head_block_number)
(head_block_id)
(time)
@ -97,7 +125,10 @@ FC_REFLECT_DERIVED( graphene::chain::dynamic_global_property_object, (graphene::
(next_maintenance_time)
(witness_budget)
(accounts_registered_this_interval)
(first_maintenance_block_with_current_interval)
(recently_missed_count)
(current_aslot)
(recent_slots_filled)
(dynamic_flags)
)
FC_REFLECT_DERIVED( graphene::chain::global_property_object, (graphene::db::object),
@ -106,5 +137,4 @@ FC_REFLECT_DERIVED( graphene::chain::global_property_object, (graphene::db::obje
(next_available_vote_id)
(active_committee_members)
(active_witnesses)
(chain_id)
)

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2015, Cryptonomex, Inc.
* All rights reserved.
*
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
* are permitted until September 8, 2015, provided that the following conditions are met:
*
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <fc/reflect/reflect.hpp>
#include <cstdint>
#include <graphene/chain/config.hpp>
namespace graphene { namespace chain {
struct immutable_chain_parameters
{
uint16_t min_committee_member_count = GRAPHENE_DEFAULT_MIN_COMMITTEE_MEMBER_COUNT;
uint16_t min_witness_count = GRAPHENE_DEFAULT_MIN_WITNESS_COUNT;
uint32_t num_special_accounts = 0;
uint32_t num_special_assets = 0;
};
} } // graphene::chain
FC_REFLECT( graphene::chain::immutable_chain_parameters,
(min_committee_member_count)
(min_witness_count)
(num_special_accounts)
(num_special_assets)
)

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2015, Cryptonomex, Inc.
* All rights reserved.
*
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
* are permitted until September 8, 2015, provided that the following conditions are met:
*
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <fc/exception/exception.hpp>
#include <graphene/chain/exceptions.hpp>
#define GRAPHENE_DECLARE_INTERNAL_EXCEPTION( exc_name, seqnum, msg ) \
FC_DECLARE_DERIVED_EXCEPTION( \
internal_ ## exc_name, \
graphene::chain::internal_exception, \
3990000 + seqnum, \
msg \
)
namespace graphene { namespace chain {
FC_DECLARE_DERIVED_EXCEPTION( internal_exception, graphene::chain::chain_exception, 3990000, "internal exception" )
GRAPHENE_DECLARE_INTERNAL_EXCEPTION( verify_auth_max_auth_exceeded, 1, "Exceeds max authority fan-out" )
GRAPHENE_DECLARE_INTERNAL_EXCEPTION( verify_auth_account_not_found, 2, "Auth account not found" )
} } // graphene::chain

View file

@ -27,6 +27,13 @@ namespace graphene { namespace chain {
share_type for_sale; ///< asset id is sell_price.base.asset_id
price sell_price;
pair<asset_id_type,asset_id_type> get_market()const
{
auto tmp = std::make_pair( sell_price.base.asset_id, sell_price.quote.asset_id );
if( tmp.first > tmp.second ) std::swap( tmp.first, tmp.second );
return tmp;
}
asset amount_for_sale()const { return asset( for_sale, sell_price.base.asset_id ); }
asset amount_to_receive()const { return amount_for_sale() * sell_price; }
};
@ -34,6 +41,7 @@ namespace graphene { namespace chain {
struct by_id;
struct by_price;
struct by_expiration;
struct by_account;
typedef multi_index_container<
limit_order_object,
indexed_by<
@ -46,7 +54,8 @@ namespace graphene { namespace chain {
member< object, object_id_type, &object::id>
>,
composite_key_compare< std::greater<price>, std::less<object_id_type> >
>
>,
ordered_non_unique< tag<by_account>, member<limit_order_object, account_id_type, &limit_order_object::seller>>
>
> limit_order_multi_index_type;
@ -74,7 +83,7 @@ namespace graphene { namespace chain {
account_id_type borrower;
share_type collateral; ///< call_price.base.asset_id, access via get_collateral
share_type debt; ///< call_price.quote.asset_id, access via get_collateral
price call_price; ///< Debt / Collateral
price call_price; ///< Debt / Collateral
};
/**

View file

@ -1,5 +1,6 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
#include <graphene/chain/protocol/vote.hpp>
namespace graphene { namespace chain {
@ -13,9 +14,10 @@ namespace graphene { namespace chain {
/// validated account activities. This field is here to prevent confusion if the active authority has zero or
/// multiple keys in it.
public_key_type memo_key;
/// If this field is set to an account ID other than 0, this account's votes will be ignored and its stake
/// If this field is set to an account ID other than GRAPHENE_PROXY_TO_SELF_ACCOUNT,
/// then this account's votes will be ignored; its stake
/// will be counted as voting for the referenced account's selected votes instead.
account_id_type voting_account;
account_id_type voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT;
/// The number of active witnesses this account votes the blockchain should appoint
/// Must not exceed the actual number of witnesses voted for in @ref votes
@ -62,14 +64,6 @@ namespace graphene { namespace chain {
account_id_type fee_payer()const { return registrar; }
void validate()const;
share_type calculate_fee(const fee_parameters_type& )const;
void get_impacted_accounts( flat_set<account_id_type>& i )const
{
i.insert(registrar);
i.insert(referrer);
add_authority_accounts( i, owner );
add_authority_accounts( i, active );
}
};
/**
@ -92,7 +86,7 @@ namespace graphene { namespace chain {
/// New owner authority. If set, this operation requires owner authority to execute.
optional<authority> owner;
/// New active authority. If set, this operation requires owner authority to execute: TODO: why?
/// New active authority. If set, this operation requires owner authority to execute.
optional<authority> active;
/// New account options
@ -104,17 +98,12 @@ namespace graphene { namespace chain {
share_type calculate_fee( const fee_parameters_type& k )const;
void get_required_owner_authorities( flat_set<account_id_type>& a )const
{ if( owner || active ) a.insert( account ); }
{ if( owner ) a.insert( account ); }
void get_impacted_accounts( flat_set<account_id_type>& i )const
{
i.insert(account);
if( owner ) add_authority_accounts( i, *owner );
if( active ) add_authority_accounts( i, *active );
}
void get_required_active_authorities( flat_set<account_id_type>& a )const
{ if( !owner ) a.insert( account ); }
};
/**
* @brief This operation is used to whitelist and blacklist accounts, primarily for transacting in whitelisted assets
* @ingroup operations
@ -157,13 +146,8 @@ namespace graphene { namespace chain {
account_id_type fee_payer()const { return authorizing_account; }
void validate()const { FC_ASSERT( fee.amount >= 0 ); FC_ASSERT(new_listing < 0x4); }
void get_impacted_accounts( flat_set<account_id_type>& i )const
{ i.insert(account_to_list); }
};
/**
* @brief Manage an account's membership status
* @ingroup operations
@ -220,15 +204,10 @@ namespace graphene { namespace chain {
account_id_type fee_payer()const { return account_id; }
void validate()const;
void get_impacted_accounts( flat_set<account_id_type>& i )const
{
i.insert(new_owner);
}
};
} }
} } // graphene::chain
FC_REFLECT(graphene::chain::account_options, (memo_key)(voting_account)(num_witness)(num_committee)(votes)(extensions))
FC_REFLECT_TYPENAME( graphene::chain::account_whitelist_operation::account_listing)
FC_REFLECT_ENUM( graphene::chain::account_whitelist_operation::account_listing,

View file

@ -56,7 +56,12 @@ namespace graphene { namespace chain {
explicit operator std::string()const; ///< converts to base58 + checksum
friend size_t hash_value( const address& v ) { return *((size_t*)&v.addr._hash[2]); }
friend size_t hash_value( const address& v ) {
const void* tmp = static_cast<const void*>(v.addr._hash+2);
const size_t* tmp2 = reinterpret_cast<const size_t*>(tmp);
return *tmp2;
}
fc::ripemd160 addr;
};
inline bool operator == ( const address& a, const address& b ) { return a.addr == b.addr; }

View file

@ -34,13 +34,27 @@ namespace graphene { namespace chain {
};
/**
* Used to verify that a specific block is part of the
* blockchain history. This helps protect some high-value
* transactions to newly created IDs
*
* The block ID must be within the last 2^16 blocks.
*/
struct block_id_predicate
{
block_id_type id;
bool validate()const{ return true; }
};
/**
* When defining predicates do not make the protocol dependent upon
* implementation details.
*/
typedef static_variant<
account_name_eq_lit_predicate,
asset_symbol_eq_lit_predicate
asset_symbol_eq_lit_predicate,
block_id_predicate
> predicate;
@ -71,5 +85,7 @@ namespace graphene { namespace chain {
FC_REFLECT( graphene::chain::assert_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::account_name_eq_lit_predicate, (account_id)(name) )
FC_REFLECT( graphene::chain::asset_symbol_eq_lit_predicate, (asset_id)(symbol) )
FC_REFLECT( graphene::chain::block_id_predicate, (id) )
FC_REFLECT_TYPENAME( graphene::chain::predicate )
FC_REFLECT( graphene::chain::assert_operation, (fee)(fee_paying_account)(predicates)(required_auths)(extensions) )

View file

@ -21,6 +21,8 @@
namespace graphene { namespace chain {
extern const int64_t scaled_precision_lut[];
struct asset
{
asset( share_type a = 0, asset_id_type id = asset_id_type() )
@ -80,6 +82,12 @@ namespace graphene { namespace chain {
FC_ASSERT( a.asset_id == b.asset_id );
return asset( a.amount + b.amount, a.asset_id );
}
static share_type scaled_precision( uint8_t precision )
{
FC_ASSERT( precision < 19 );
return scaled_precision_lut[ precision ];
}
};
/**
@ -203,4 +211,3 @@ FC_REFLECT( graphene::chain::price, (base)(quote) )
(core_exchange_rate)
FC_REFLECT( graphene::chain::price_feed, GRAPHENE_PRICE_FEED_FIELDS )

View file

@ -189,6 +189,29 @@ namespace graphene { namespace chain {
void validate()const;
};
/**
* Virtual op generated when force settlement is cancelled.
*/
struct asset_settle_cancel_operation : public base_operation
{
struct fee_parameters_type { };
asset fee;
force_settlement_id_type settlement;
/// Account requesting the force settlement. This account pays the fee
account_id_type account;
/// Amount of asset to force settle. This must be a market-issued asset
asset amount;
extensions_type extensions;
account_id_type fee_payer()const { return account; }
void validate()const {}
share_type calculate_fee(const fee_parameters_type& params)const
{ return 0; }
};
/**
* @ingroup operations
*/
@ -242,8 +265,6 @@ namespace graphene { namespace chain {
account_id_type fee_payer()const { return issuer; }
void validate()const;
share_type calculate_fee(const fee_parameters_type& k)const;
void get_impacted_accounts( flat_set<account_id_type>& i)const
{ if( new_issuer ) i.insert( *new_issuer ); }
};
/**
@ -358,15 +379,13 @@ namespace graphene { namespace chain {
account_id_type fee_payer()const { return issuer; }
void validate()const;
share_type calculate_fee(const fee_parameters_type& k)const;
void get_impacted_accounts( flat_set<account_id_type>& i)const
{ i.insert( issue_to_account ); }
};
/**
* @brief used to take an asset out of circulation, returning to the issuer
* @ingroup operations
*
* @note You cannot burn market-issued assets.
* @note You cannot use this operation on market-issued assets.
*/
struct asset_reserve_operation : public base_operation
{
@ -381,8 +400,8 @@ namespace graphene { namespace chain {
void validate()const;
};
} } // graphene::chain
FC_REFLECT( graphene::chain::asset_options,
(max_supply)
(market_fee_percent)
@ -411,6 +430,7 @@ FC_REFLECT( graphene::chain::bitasset_options,
FC_REFLECT( graphene::chain::asset_create_operation::fee_parameters_type, (symbol3)(symbol4)(long_symbol)(price_per_kbyte) )
FC_REFLECT( graphene::chain::asset_global_settle_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::asset_settle_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::asset_settle_cancel_operation::fee_parameters_type, )
FC_REFLECT( graphene::chain::asset_fund_fee_pool_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::asset_update_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::asset_update_bitasset_operation::fee_parameters_type, (fee) )
@ -451,6 +471,7 @@ FC_REFLECT( graphene::chain::asset_update_feed_producers_operation,
FC_REFLECT( graphene::chain::asset_publish_feed_operation,
(fee)(publisher)(asset_id)(feed)(extensions) )
FC_REFLECT( graphene::chain::asset_settle_operation, (fee)(account)(amount)(extensions) )
FC_REFLECT( graphene::chain::asset_settle_cancel_operation, (fee)(settlement)(account)(amount)(extensions) )
FC_REFLECT( graphene::chain::asset_global_settle_operation, (fee)(issuer)(asset_to_settle)(settle_price)(extensions) )
FC_REFLECT( graphene::chain::asset_issue_operation,
(fee)(issuer)(asset_to_issue)(issue_to_account)(memo)(extensions) )
@ -458,4 +479,3 @@ FC_REFLECT( graphene::chain::asset_reserve_operation,
(fee)(payer)(amount_to_reserve)(extensions) )
FC_REFLECT( graphene::chain::asset_fund_fee_pool_operation, (fee)(from_account)(asset_id)(amount)(extensions) );

View file

@ -54,6 +54,14 @@ namespace graphene { namespace chain {
{
account_auths[k] = w;
}
bool is_impossible()const
{
uint64_t auth_weights = 0;
for( const auto& item : account_auths ) auth_weights += item.second;
for( const auto& item : key_auths ) auth_weights += item.second;
for( const auto& item : address_auths ) auth_weights += item.second;
return auth_weights < weight_threshold;
}
template<typename AuthType>
void add_authorities(AuthType k, weight_type w)
@ -75,7 +83,15 @@ namespace graphene { namespace chain {
result.push_back(k.first);
return result;
}
uint32_t num_auths()const { return account_auths.size() + key_auths.size(); }
friend bool operator == ( const authority& a, const authority& b )
{
return (a.weight_threshold == b.weight_threshold) &&
(a.account_auths == b.account_auths) &&
(a.key_auths == b.key_auths) &&
(a.address_auths == b.address_auths);
}
uint32_t num_auths()const { return account_auths.size() + key_auths.size() + address_auths.size(); }
void clear() { account_auths.clear(); key_auths.clear(); }
uint32_t weight_threshold = 0;
@ -85,6 +101,14 @@ namespace graphene { namespace chain {
flat_map<address,weight_type> address_auths;
};
/**
* Add all account members of the given authority to the given flat_set.
*/
void add_authority_accounts(
flat_set<account_id_type>& result,
const authority& a
);
} } // namespace graphene::chain
FC_REFLECT( graphene::chain::authority, (weight_threshold)(account_auths)(key_auths)(address_auths) )

View file

@ -29,7 +29,7 @@ namespace graphene { namespace chain {
}
};
}} // graphene::chain
} } // graphene::chain
FC_REFLECT( graphene::chain::balance_claim_operation::fee_parameters_type, )
FC_REFLECT( graphene::chain::balance_claim_operation,

View file

@ -1,9 +1,9 @@
#pragma once
#include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/protocol/asset.hpp>
#include <graphene/chain/protocol/authority.hpp>
namespace graphene { namespace chain {
/**
@ -70,15 +70,9 @@ namespace graphene { namespace chain {
void get_required_authorities( vector<authority>& )const{}
void get_required_active_authorities( flat_set<account_id_type>& )const{}
void get_required_owner_authorities( flat_set<account_id_type>& )const{}
void get_impacted_accounts( flat_set<account_id_type>& )const{}
void validate()const{}
static uint64_t calculate_data_fee( uint64_t bytes, uint64_t price_per_kbyte );
static void add_authority_accounts( flat_set<account_id_type>& i, const authority& a )
{
for( auto& item : a.account_auths )
i.insert( item.first );
}
};
/**
@ -100,6 +94,7 @@ namespace graphene { namespace chain {
///@}
} }
} } // graphene::chain
FC_REFLECT_TYPENAME( graphene::chain::operation_result )
FC_REFLECT( graphene::chain::void_result, )

View file

@ -27,8 +27,6 @@ namespace graphene { namespace chain {
uint32_t block_num()const { return num_from_id(previous) + 1; }
fc::time_point_sec timestamp;
witness_id_type witness;
secret_hash_type next_secret_hash;
secret_hash_type previous_secret;
checksum_type transaction_merkle_root;
extensions_type extensions;
@ -53,7 +51,6 @@ namespace graphene { namespace chain {
} } // graphene::chain
FC_REFLECT( graphene::chain::block_header, (previous)(timestamp)(witness)
(next_secret_hash)(previous_secret)(transaction_merkle_root)(extensions) )
FC_REFLECT( graphene::chain::block_header, (previous)(timestamp)(witness)(transaction_merkle_root)(extensions) )
FC_REFLECT_DERIVED( graphene::chain::signed_block_header, (graphene::chain::block_header), (witness_signature) )
FC_REFLECT_DERIVED( graphene::chain::signed_block, (graphene::chain::signed_block_header), (transactions) )

View file

@ -16,6 +16,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <graphene/chain/protocol/base.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <fc/smart_ref_fwd.hpp>
@ -36,10 +37,10 @@ namespace graphene { namespace chain {
smart_ref<fee_schedule> current_fees; ///< current schedule of fees
uint8_t block_interval = GRAPHENE_DEFAULT_BLOCK_INTERVAL; ///< interval in seconds between blocks
uint32_t maintenance_interval = GRAPHENE_DEFAULT_MAINTENANCE_INTERVAL; ///< interval in sections between blockchain maintenance events
uint8_t maintenance_skip_slots = GRAPHENE_DEFAULT_MAINTENANCE_SKIP_SLOTS; ///< number of block_intervals to skip at maintenance time
uint32_t committee_proposal_review_period = GRAPHENE_DEFAULT_COMMITTEE_PROPOSAL_REVIEW_PERIOD_SEC; ///< minimum time in seconds that a proposed transaction requiring committee authority may not be signed, prior to expiration
uint32_t maximum_transaction_size = GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE; ///< maximum allowable size in bytes for a transaction
uint32_t maximum_block_size = GRAPHENE_DEFAULT_MAX_BLOCK_SIZE; ///< maximum allowable size in bytes for a block
uint32_t maximum_undo_history = GRAPHENE_DEFAULT_MAX_UNDO_HISTORY; ///< maximum number of undo states to keep in RAM
uint32_t maximum_time_until_expiration = GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION; ///< maximum lifetime in seconds for transactions to be valid, before expiring
uint32_t maximum_proposal_lifetime = GRAPHENE_DEFAULT_MAX_PROPOSAL_LIFETIME_SEC; ///< maximum lifetime in seconds for proposed transactions to be kept, before expiring
uint8_t maximum_asset_whitelist_authorities = GRAPHENE_DEFAULT_MAX_ASSET_WHITELIST_AUTHORITIES; ///< maximum number of accounts which an asset may list as authorities for its whitelist OR blacklist
@ -52,45 +53,20 @@ namespace graphene { namespace chain {
uint16_t lifetime_referrer_percent_of_fee = GRAPHENE_DEFAULT_LIFETIME_REFERRER_PERCENT_OF_FEE; ///< percent of transaction fees paid to network
uint32_t cashback_vesting_period_seconds = GRAPHENE_DEFAULT_CASHBACK_VESTING_PERIOD_SEC; ///< time after cashback rewards are accrued before they become liquid
share_type cashback_vesting_threshold = GRAPHENE_DEFAULT_CASHBACK_VESTING_THRESHOLD; ///< the maximum cashback that can be received without vesting
uint16_t max_bulk_discount_percent_of_fee = GRAPHENE_DEFAULT_MAX_BULK_DISCOUNT_PERCENT; ///< the maximum percentage discount for bulk discounts
share_type bulk_discount_threshold_min = GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MIN; ///< the minimum amount of fees paid to qualify for bulk discounts
share_type bulk_discount_threshold_max = GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MAX; ///< the amount of fees paid to qualify for the max bulk discount percent
bool count_non_member_votes = true; ///< set to false to restrict voting privlegages to member accounts
bool allow_non_member_whitelists = false; ///< true if non-member accounts may set whitelists and blacklists; false otherwise
share_type witness_pay_per_block = GRAPHENE_DEFAULT_WITNESS_PAY_PER_BLOCK; ///< CORE to be allocated to witnesses (per block)
uint32_t witness_pay_vesting_seconds = GRAPHENE_DEFAULT_WITNESS_PAY_VESTING_SECONDS; ///< vesting_seconds parameter for witness VBO's
share_type worker_budget_per_day = GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY; ///< CORE to be allocated to workers (per day)
uint16_t max_predicate_opcode = GRAPHENE_DEFAULT_MAX_ASSERT_OPCODE; ///< predicate_opcode must be less than this number
share_type fee_liquidation_threshold = GRAPHENE_DEFAULT_FEE_LIQUIDATION_THRESHOLD; ///< value in CORE at which accumulated fees in blockchain-issued market assets should be liquidated
uint16_t accounts_per_fee_scale = GRAPHENE_DEFAULT_ACCOUNTS_PER_FEE_SCALE; ///< number of accounts between fee scalings
uint8_t account_fee_scale_bitshifts = GRAPHENE_DEFAULT_ACCOUNT_FEE_SCALE_BITSHIFTS; ///< number of times to left bitshift account registration fee at each scaling
uint8_t max_authority_depth = GRAPHENE_MAX_SIG_CHECK_DEPTH;
extensions_type extensions;
void validate()const
{
FC_ASSERT( reserve_percent_of_fee <= GRAPHENE_100_PERCENT );
FC_ASSERT( network_percent_of_fee <= GRAPHENE_100_PERCENT );
FC_ASSERT( max_bulk_discount_percent_of_fee <= GRAPHENE_100_PERCENT );
FC_ASSERT( lifetime_referrer_percent_of_fee <= GRAPHENE_100_PERCENT );
FC_ASSERT( network_percent_of_fee + lifetime_referrer_percent_of_fee <= GRAPHENE_100_PERCENT );
FC_ASSERT( bulk_discount_threshold_min <= bulk_discount_threshold_max );
FC_ASSERT( bulk_discount_threshold_min > 0 );
FC_ASSERT( block_interval >= GRAPHENE_MIN_BLOCK_INTERVAL );
FC_ASSERT( block_interval <= GRAPHENE_MAX_BLOCK_INTERVAL );
FC_ASSERT( block_interval > 0 );
FC_ASSERT( maintenance_interval > block_interval,
"Maintenance interval must be longer than block interval" );
FC_ASSERT( maintenance_interval % block_interval == 0,
"Maintenance interval must be a multiple of block interval" );
FC_ASSERT( maximum_transaction_size >= GRAPHENE_MIN_TRANSACTION_SIZE_LIMIT,
"Transaction size limit is too low" );
FC_ASSERT( maximum_block_size >= GRAPHENE_MIN_BLOCK_SIZE_LIMIT,
"Block size limit is too low" );
FC_ASSERT( maximum_time_until_expiration > block_interval,
"Maximum transaction expiration time must be greater than a block interval" );
FC_ASSERT( maximum_proposal_lifetime - committee_proposal_review_period > block_interval,
"Committee proposal review period must be less than the maximum proposal lifetime" );
}
/** defined in fee_schedule.cpp */
void validate()const;
};
} } // graphene::chain
@ -99,10 +75,10 @@ FC_REFLECT( graphene::chain::chain_parameters,
(current_fees)
(block_interval)
(maintenance_interval)
(maintenance_skip_slots)
(committee_proposal_review_period)
(maximum_transaction_size)
(maximum_block_size)
(maximum_undo_history)
(maximum_time_until_expiration)
(maximum_proposal_lifetime)
(maximum_asset_whitelist_authorities)
@ -113,11 +89,8 @@ FC_REFLECT( graphene::chain::chain_parameters,
(reserve_percent_of_fee)
(network_percent_of_fee)
(lifetime_referrer_percent_of_fee)
(max_bulk_discount_percent_of_fee)
(cashback_vesting_period_seconds)
(cashback_vesting_threshold)
(bulk_discount_threshold_min)
(bulk_discount_threshold_max)
(count_non_member_votes)
(allow_non_member_whitelists)
(witness_pay_per_block)
@ -126,5 +99,6 @@ FC_REFLECT( graphene::chain::chain_parameters,
(fee_liquidation_threshold)
(accounts_per_fee_scale)
(account_fee_scale_bitshifts)
(max_authority_depth)
(extensions)
)

View file

@ -24,6 +24,28 @@ namespace graphene { namespace chain {
void validate()const;
};
/**
* @brief Update a committee_member object.
* @ingroup operations
*
* Currently the only field which can be updated is the `url`
* field.
*/
struct committee_member_update_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
/// The committee member to update.
committee_member_id_type committee_member;
/// The account which owns the committee_member. This account pays the fee for this operation.
account_id_type committee_member_account;
optional< string > new_url;
account_id_type fee_payer()const { return committee_member_account; }
void validate()const;
};
/**
* @brief Used by committee_members to update the global parameters of the blockchain.
* @ingroup operations
@ -50,10 +72,12 @@ namespace graphene { namespace chain {
} } // graphene::chain
FC_REFLECT( graphene::chain::committee_member_create_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::committee_member_update_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::committee_member_update_global_parameters_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::committee_member_create_operation,
(fee)(committee_member_account)(url) )
FC_REFLECT( graphene::chain::committee_member_update_operation,
(fee)(committee_member)(committee_member_account)(new_url) )
FC_REFLECT( graphene::chain::committee_member_update_global_parameters_operation, (fee)(new_parameters) );

View file

@ -0,0 +1,264 @@
/*
* Copyright (c) 2015, Cryptonomex, Inc.
* All rights reserved.
*/
#pragma once
#include <graphene/chain/protocol/base.hpp>
namespace graphene { namespace chain {
using fc::ecc::blind_factor_type;
/**
* @defgroup stealth Stealth Transfer
* @brief Operations related to stealth transfer of value
*
* Stealth Transfers enable users to maintain their finanical privacy against even
* though all transactions are public. Every account has three balances:
*
* 1. Public Balance - everyone can see the balance changes and the parties involved
* 2. Blinded Balance - everyone can see who is transacting but not the amounts involved
* 3. Stealth Balance - both the amounts and parties involved are obscured
*
* Account owners may set a flag that allows their account to receive(or not) transfers of these kinds
* Asset issuers can enable or disable the use of each of these types of accounts.
*
* Using the "temp account" which has no permissions required, users can transfer a
* stealth balance to the temp account and then use the temp account to register a new
* account. In this way users can use stealth funds to create anonymous accounts with which
* they can perform other actions that are not compatible with blinded balances (such as market orders)
*
* @section referral_program Referral Progam
*
* Stealth transfers that do not specify any account id cannot pay referral fees so 100% of the
* transaction fee is paid to the network.
*
* @section transaction_fees Fees
*
* Stealth transfers can have an arbitrarylly large size and therefore the transaction fee for
* stealth transfers is based purley on the data size of the transaction.
*/
///@{
/**
* @ingroup stealth
* This data is encrypted and stored in the
* encrypted memo portion of the blind output.
*/
struct blind_memo
{
account_id_type from;
share_type amount;
string message;
/** set to the first 4 bytes of the shared secret
* used to encrypt the memo. Used to verify that
* decryption was successful.
*/
uint32_t check= 0;
};
/**
* @ingroup stealth
*/
struct blind_input
{
fc::ecc::commitment_type commitment;
/** provided to maintain the invariant that all authority
* required by an operation is explicit in the operation. Must
* match blinded_balance_id->owner
*/
authority owner;
};
/**
* When sending a stealth tranfer we assume users are unable to scan
* the full blockchain; therefore, payments require confirmation data
* to be passed out of band. We assume this out-of-band channel is
* not secure and therefore the contents of the confirmation must be
* encrypted.
*/
struct stealth_confirmation
{
struct memo_data
{
optional<public_key_type> from;
asset amount;
fc::sha256 blinding_factor;
fc::ecc::commitment_type commitment;
uint32_t check = 0;
};
/**
* Packs *this then encodes as base58 encoded string.
*/
operator string()const;
/**
* Unpacks from a base58 string
*/
stealth_confirmation( const std::string& base58 );
stealth_confirmation(){}
public_key_type one_time_key;
optional<public_key_type> to;
vector<char> encrypted_memo;
};
/**
* @class blind_output
* @brief Defines data required to create a new blind commitment
* @ingroup stealth
*
* The blinded output that must be proven to be greater than 0
*/
struct blind_output
{
fc::ecc::commitment_type commitment;
/** only required if there is more than one blind output */
range_proof_type range_proof;
authority owner;
optional<stealth_confirmation> stealth_memo;
};
/**
* @class transfer_to_blind_operation
* @ingroup stealth
* @brief Converts public account balance to a blinded or stealth balance
*/
struct transfer_to_blind_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 5*GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to register the cheapest non-free account
uint32_t price_per_output = 5*GRAPHENE_BLOCKCHAIN_PRECISION;
};
asset fee;
asset amount;
account_id_type from;
blind_factor_type blinding_factor;
vector<blind_output> outputs;
account_id_type fee_payer()const { return from; }
void validate()const;
share_type calculate_fee(const fee_parameters_type& )const;
};
/**
* @ingroup stealth
* @brief Converts blinded/stealth balance to a public account balance
*/
struct transfer_from_blind_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 5*GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to register the cheapest non-free account
};
asset fee;
asset amount;
account_id_type to;
blind_factor_type blinding_factor;
vector<blind_input> inputs;
account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; }
void validate()const;
void get_required_authorities( vector<authority>& a )const
{
for( const auto& in : inputs )
a.push_back( in.owner );
}
};
/**
* @ingroup stealth
* @brief Transfers from blind to blind
*
* There are two ways to transfer value while maintaining privacy:
* 1. account to account with amount kept secret
* 2. stealth transfers with amount sender/receiver kept secret
*
* When doing account to account transfers, everyone with access to the
* memo key can see the amounts, but they will not have access to the funds.
*
* When using stealth transfers the same key is used for control and reading
* the memo.
*
* This operation is more expensive than a normal transfer and has
* a fee proportional to the size of the operation.
*
* All assets in a blind transfer must be of the same type: fee.asset_id
* The fee_payer is the temp account and can be funded from the blinded values.
*
* Using this operation you can transfer from an account and/or blinded balances
* to an account and/or blinded balances.
*
* Stealth Transfers:
*
* Assuming Receiver has key pair R,r and has shared public key R with Sender
* Assuming Sender has key pair S,s
* Generate one time key pair O,o as s.child(nonce) where nonce can be inferred from transaction
* Calculate secret V = o*R
* blinding_factor = sha256(V)
* memo is encrypted via aes of V
* owner = R.child(sha256(blinding_factor))
*
* Sender gives Receiver output ID to complete the payment.
*
* This process can also be used to send money to a cold wallet without having to
* pre-register any accounts.
*
* Outputs are assigned the same IDs as the inputs until no more input IDs are available,
* in which case a the return value will be the *first* ID allocated for an output. Additional
* output IDs are allocated sequentially thereafter. If there are fewer outputs than inputs
* then the input IDs are freed and never used again.
*/
struct blind_transfer_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 5*GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to register the cheapest non-free account
uint32_t price_per_output = 5*GRAPHENE_BLOCKCHAIN_PRECISION;
};
asset fee;
vector<blind_input> inputs;
vector<blind_output> outputs;
/** graphene TEMP account */
account_id_type fee_payer()const;
void validate()const;
share_type calculate_fee( const fee_parameters_type& k )const;
void get_required_authorities( vector<authority>& a )const
{
for( const auto& in : inputs )
a.push_back( in.owner );
}
};
///@} endgroup stealth
} } // graphene::chain
FC_REFLECT( graphene::chain::stealth_confirmation,
(one_time_key)(to)(encrypted_memo) )
FC_REFLECT( graphene::chain::stealth_confirmation::memo_data,
(from)(amount)(blinding_factor)(commitment)(check) );
FC_REFLECT( graphene::chain::blind_memo,
(from)(amount)(message)(check) )
FC_REFLECT( graphene::chain::blind_input,
(commitment)(owner) )
FC_REFLECT( graphene::chain::blind_output,
(commitment)(range_proof)(owner)(stealth_memo) )
FC_REFLECT( graphene::chain::transfer_to_blind_operation,
(fee)(amount)(from)(blinding_factor)(outputs) )
FC_REFLECT( graphene::chain::transfer_from_blind_operation,
(fee)(amount)(to)(blinding_factor)(inputs) )
FC_REFLECT( graphene::chain::blind_transfer_operation,
(fee)(inputs)(outputs) )
FC_REFLECT( graphene::chain::transfer_to_blind_operation::fee_parameters_type, (fee)(price_per_output) )
FC_REFLECT( graphene::chain::transfer_from_blind_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::blind_transfer_operation::fee_parameters_type, (fee)(price_per_output) )

View file

@ -29,7 +29,7 @@ namespace graphene { namespace chain {
share_type calculate_fee(const fee_parameters_type& k)const;
};
} } // namespace graphene::chain
FC_REFLECT( graphene::chain::custom_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::custom_operation, (fee)(payer)(required_auths)(id)(data) )

View file

@ -5,7 +5,7 @@ namespace graphene { namespace chain {
template<typename T> struct transform_to_fee_parameters;
template<typename ...T>
struct transform_to_fee_parameters<fc::static_variant<T...>>
struct transform_to_fee_parameters<fc::static_variant<T...>>
{
typedef fc::static_variant< typename T::fee_parameters_type... > type;
};
@ -21,7 +21,7 @@ namespace graphene { namespace chain {
static fee_schedule get_default();
/**
* Finds the appropriate fee parameter struct for the operation
* Finds the appropriate fee parameter struct for the operation
* and then calculates the appropriate fee.
*/
asset calculate_fee( const operation& op, const price& core_exchange_rate = price::unit_price() )const;
@ -41,6 +41,13 @@ namespace graphene { namespace chain {
FC_ASSERT( itr != parameters.end() );
return itr->template get<typename Operation::fee_parameters_type>();
}
template<typename Operation>
typename Operation::fee_parameters_type& get()
{
auto itr = parameters.find( typename Operation::fee_parameters_type() );
FC_ASSERT( itr != parameters.end() );
return itr->template get<typename Operation::fee_parameters_type>();
}
/**
* @note must be sorted by fee_parameters.which() and have no duplicates
@ -51,8 +58,7 @@ namespace graphene { namespace chain {
typedef fee_schedule fee_schedule_type;
} } // graphene::chain
} } // graphene::chain
FC_REFLECT_TYPENAME( graphene::chain::fee_parameters )
FC_REFLECT( graphene::chain::fee_schedule, (parameters)(scale) )

View file

@ -133,8 +133,6 @@ namespace graphene { namespace chain {
/// This is a virtual operation; there is no fee
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
void get_impacted_accounts( flat_set<account_id_type>& i)const
{ i.insert( account_id ); }
};
} } // graphene::chain

View file

@ -14,6 +14,7 @@
#include <graphene/chain/protocol/withdraw_permission.hpp>
#include <graphene/chain/protocol/witness.hpp>
#include <graphene/chain/protocol/worker.hpp>
#include <graphene/chain/protocol/confidential.hpp>
namespace graphene { namespace chain {
@ -27,7 +28,7 @@ namespace graphene { namespace chain {
limit_order_create_operation,
limit_order_cancel_operation,
call_order_update_operation,
fill_order_operation,
fill_order_operation, // VIRTUAL
account_create_operation,
account_update_operation,
account_whitelist_operation,
@ -44,7 +45,7 @@ namespace graphene { namespace chain {
asset_global_settle_operation,
asset_publish_feed_operation,
witness_create_operation,
witness_withdraw_pay_operation,
witness_update_operation,
proposal_create_operation,
proposal_update_operation,
proposal_delete_operation,
@ -53,6 +54,7 @@ namespace graphene { namespace chain {
withdraw_permission_claim_operation,
withdraw_permission_delete_operation,
committee_member_create_operation,
committee_member_update_operation,
committee_member_update_global_parameters_operation,
vesting_balance_create_operation,
vesting_balance_withdraw_operation,
@ -60,7 +62,12 @@ namespace graphene { namespace chain {
custom_operation,
assert_operation,
balance_claim_operation,
override_transfer_operation
override_transfer_operation,
transfer_to_blind_operation,
blind_transfer_operation,
transfer_from_blind_operation,
asset_settle_cancel_operation // VIRTUAL
> operation;
/// @} // operations group
@ -76,25 +83,8 @@ namespace graphene { namespace chain {
flat_set<account_id_type>& owner,
vector<authority>& other );
void operation_get_impacted_accounts( const operation& op,
flat_set<account_id_type>& accounts );
void operation_validate( const operation& op );
/**
* Used to track the result of applying an operation and when it was applied.
*
* TODO: this doesn't belong here.
*/
struct applied_operation
{
operation op;
operation_result result;
uint32_t block_num;
uint16_t transaction_num;
uint16_t op_num;
};
/**
* @brief necessary to support nested operations inside the proposal_create_operation
*/
@ -109,9 +99,3 @@ namespace graphene { namespace chain {
FC_REFLECT_TYPENAME( graphene::chain::operation )
FC_REFLECT( graphene::chain::op_wrapper, (op) )

View file

@ -57,15 +57,21 @@ namespace graphene { namespace chain {
optional<uint32_t> review_period_seconds;
extensions_type extensions;
/// Constructs a proposal_create_operation suitable for committee proposals, with fee, expiration time and review
/// period set appropriately.
/**
* Constructs a proposal_create_operation suitable for committee
* proposals, with expiration time and review period set
* appropriately. No proposed_ops are added. When used to
* create a proposal to change chain parameters, this method
* expects to receive the currently effective parameters, not
* the proposed parameters. (The proposed parameters will go
* in proposed_ops, and proposed_ops is untouched by this
* function.)
*/
static proposal_create_operation committee_proposal(const chain_parameters& param, fc::time_point_sec head_block_time );
account_id_type fee_payer()const { return fee_paying_account; }
void validate()const;
share_type calculate_fee(const fee_parameters_type& k)const;
void get_impacted_accounts( flat_set<account_id_type>& )const;
};
/**
@ -150,4 +156,3 @@ FC_REFLECT( graphene::chain::proposal_update_operation, (fee)(fee_paying_account
(active_approvals_to_add)(active_approvals_to_remove)(owner_approvals_to_add)(owner_approvals_to_remove)
(key_approvals_to_add)(key_approvals_to_remove)(extensions) )
FC_REFLECT( graphene::chain::proposal_delete_operation, (fee)(fee_paying_account)(using_owner_authority)(proposal)(extensions) )

View file

@ -17,6 +17,7 @@
*/
#pragma once
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <numeric>
@ -34,20 +35,8 @@ namespace graphene { namespace chain {
* hours with a 1 second interval.
*
* All transactions must expire so that the network does not have to maintain a permanent record of all transactions
* ever published. There are two accepted ways to specify the transaction's expiration time. The first is to choose
* a reference block, which is generally the most recent block the wallet is aware of when it signs the transaction,
* and specify a number of block intervals after the reference block until the transaction expires. The second
* expiration mechanism is to explicitly specify a timestamp of expiration.
*
* Note: The number of block intervals is different than the number of blocks. In effect the maximum period that a
* transaction is theoretically valid is 18 hours (1 sec interval) to 3.5 days (5 sec interval) if the reference
* block was the most recent block.
*
* If a transaction is to expire after a number of block intervals from a reference block, the reference block
* should be identified in the transaction header using the @ref ref_block_num, @ref ref_block_prefix, and @ref
* relative_expiration fields. If the transaction is instead to expire at an absolute timestamp, @ref
* ref_block_prefix should be treated as a 32-bit timestamp of the expiration time, and @ref ref_block_num and @ref
* relative_expiration must both be set to zero.
* ever published. A transaction may not have an expiration date too far in the future because this would require
* keeping too much transaction history in memory.
*
* The block prefix is the first 4 bytes of the block hash of the reference block number, which is the second 4
* bytes of the @ref block_id_type (the first 4 bytes of the block ID are the block number)
@ -58,7 +47,7 @@ namespace graphene { namespace chain {
* probably have a longer re-org window to ensure their transaction can still go through in the event of a momentary
* disruption in service.
*
* @note It is not recommended to set the @ref ref_block_num, @ref ref_block_prefix, and @ref relative_expiration
* @note It is not recommended to set the @ref ref_block_num, @ref ref_block_prefix, and @ref expiration
* fields manually. Call the appropriate overload of @ref set_expiration instead.
*
* @{
@ -80,45 +69,44 @@ namespace graphene { namespace chain {
* @ref block_id_type
*/
uint32_t ref_block_prefix = 0;
/**
* This field specifies the number of block intervals after the reference block until this transaction becomes
* invalid. If this field is set to zero, the @ref ref_block_prefix is interpreted as an absolute timestamp of
* the time the transaction becomes invalid.
* This field specifies the absolute expiration for this transaction.
*/
uint16_t relative_expiration = 1;
fc::time_point_sec expiration;
vector<operation> operations;
extensions_type extensions;
/// Calculate the digest for a transaction with a reference block
/// @param ref_block_id Full block ID of the reference block
digest_type digest(const block_id_type& ref_block_id)const;
/// Calculate the digest for a transaction with an absolute expiration time
digest_type digest()const;
/// Calculate the digest for a transaction
digest_type digest()const;
transaction_id_type id()const;
void validate() const;
void validate() const;
/// Calculate the digest used for signature validation
digest_type sig_digest( const chain_id_type& chain_id )const;
void set_expiration( fc::time_point_sec expiration_time );
void set_expiration( const block_id_type& reference_block, unsigned_int lifetime_intervals = 3 );
void set_reference_block( const block_id_type& reference_block );
/// visit all operations
template<typename Visitor>
void visit( Visitor&& visitor )
vector<typename Visitor::result_type> visit( Visitor&& visitor )
{
vector<typename Visitor::result_type> results;
for( auto& op : operations )
op.visit( std::forward<Visitor>( visitor ) );
results.push_back(op.visit( std::forward<Visitor>( visitor ) ));
return results;
}
template<typename Visitor>
void visit( Visitor&& visitor )const
vector<typename Visitor::result_type> visit( Visitor&& visitor )const
{
vector<typename Visitor::result_type> results;
for( auto& op : operations )
op.visit( std::forward<Visitor>( visitor ) );
results.push_back(op.visit( std::forward<Visitor>( visitor ) ));
return results;
}
void get_required_authorities( flat_set<account_id_type>& active, flat_set<account_id_type>& owner, vector<authority>& other )const;
protected:
// Intentionally unreflected: does not go on wire
optional<block_id_type> block_id_cache;
};
/**
@ -130,22 +118,48 @@ namespace graphene { namespace chain {
: transaction(trx){}
/** signs and appends to signatures */
const signature_type& sign( const private_key_type& key );
const signature_type& sign( const private_key_type& key, const chain_id_type& chain_id );
/** returns signature but does not append */
signature_type sign( const private_key_type& key )const;
signature_type sign( const private_key_type& key, const chain_id_type& chain_id )const;
/**
* Given a set of private keys sign this transaction with a minimial subset of required keys.
*
* @pram get_auth - used to fetch the active authority required for an account referenced by another authority
* The purpose of this method is to identify some subset of
* @ref available_keys that will produce sufficient signatures
* for a transaction. The result is not always a minimal set of
* signatures, but any non-minimal result will still pass
* validation.
*/
void sign( const vector<private_key_type>& keys,
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner );
bool verify( const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner )const;
set<public_key_type> get_required_signatures(
const chain_id_type& chain_id,
const flat_set<public_key_type>& available_keys,
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH
)const;
void verify_authority(
const chain_id_type& chain_id,
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const;
/**
* This is a slower replacement for get_required_signatures()
* which returns a minimal set in all cases, including
* some cases where get_required_signatures() returns a
* non-minimal set.
*/
set<public_key_type> minimize_required_signatures(
const chain_id_type& chain_id,
const flat_set<public_key_type>& available_keys,
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH
) const;
flat_set<public_key_type> get_signature_keys( const chain_id_type& chain_id )const;
vector<signature_type> signatures;
@ -153,6 +167,14 @@ namespace graphene { namespace chain {
void clear() { operations.clear(); signatures.clear(); }
};
void verify_authority( const vector<operation>& ops, const flat_set<public_key_type>& sigs,
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH,
bool allow_committe = false,
const flat_set<account_id_type>& active_aprovals = flat_set<account_id_type>(),
const flat_set<account_id_type>& owner_approvals = flat_set<account_id_type>());
/**
* @brief captures the result of evaluating the operations contained in the transaction
*
@ -178,9 +200,8 @@ namespace graphene { namespace chain {
/// @} transactions group
} } // graphene::chain
} }
FC_REFLECT( graphene::chain::transaction, (ref_block_num)(ref_block_prefix)(relative_expiration)(operations)(extensions) )
FC_REFLECT( graphene::chain::transaction, (ref_block_num)(ref_block_prefix)(expiration)(operations)(extensions) )
FC_REFLECT_DERIVED( graphene::chain::signed_transaction, (graphene::chain::transaction), (signatures) )
FC_REFLECT_DERIVED( graphene::chain::processed_transaction, (graphene::chain::signed_transaction), (operation_results) )

View file

@ -2,7 +2,7 @@
#include <graphene/chain/protocol/base.hpp>
#include <graphene/chain/protocol/memo.hpp>
namespace graphene { namespace chain {
namespace graphene { namespace chain {
/**
* @ingroup operations
@ -20,9 +20,9 @@ namespace graphene { namespace chain {
*/
struct transfer_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kbyte = 10; /// only required for large memos.
struct fee_parameters_type {
uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kbyte = 10 * GRAPHENE_BLOCKCHAIN_PRECISION; /// only required for large memos.
};
asset fee;
@ -40,8 +40,6 @@ namespace graphene { namespace chain {
account_id_type fee_payer()const { return from; }
void validate()const;
share_type calculate_fee(const fee_parameters_type& k)const;
void get_impacted_accounts( flat_set<account_id_type>& i )const
{ i.insert(to); }
};
/**
@ -54,7 +52,7 @@ namespace graphene { namespace chain {
*/
struct override_transfer_operation : public base_operation
{
struct fee_parameters_type {
struct fee_parameters_type {
uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kbyte = 10; /// only required for large memos.
};
@ -75,12 +73,6 @@ namespace graphene { namespace chain {
account_id_type fee_payer()const { return issuer; }
void validate()const;
share_type calculate_fee(const fee_parameters_type& k)const;
void get_impacted_accounts( flat_set<account_id_type>& i )const
{
i.insert(to);
i.insert(from);
i.insert(issuer);
}
};
}} // graphene::chain
@ -90,4 +82,3 @@ FC_REFLECT( graphene::chain::override_transfer_operation::fee_parameters_type, (
FC_REFLECT( graphene::chain::override_transfer_operation, (fee)(issuer)(from)(to)(amount)(memo)(extensions) )
FC_REFLECT( graphene::chain::transfer_operation, (fee)(from)(to)(amount)(memo)(extensions) )

View file

@ -76,18 +76,20 @@ namespace graphene { namespace chain {
struct void_t{};
typedef fc::ecc::private_key private_key_type;
typedef fc::sha256 chain_id_type;
enum asset_issuer_permission_flags
{
charge_market_fee = 0x01, /**< an issuer-specified percentage of all market trades in this asset is paid to the issuer */
white_list = 0x02, /**< accounts must be whitelisted in order to hold this asset */
override_authority = 0x04, /**< @todo issuer may transfer asset back to himself */
override_authority = 0x04, /**< issuer may transfer asset back to himself */
transfer_restricted = 0x08, /**< require the issuer to be one party to every transfer */
disable_force_settle = 0x10, /**< disable force settling */
global_settle = 0x20 /**< allow the bitasset issuer to force a global settling -- this may be set in permissions, but not flags */
global_settle = 0x20, /**< allow the bitasset issuer to force a global settling -- this may be set in permissions, but not flags */
disable_confidential = 0x40 /**< allow the asset to be used with confidential transactions */
};
const static uint32_t ASSET_ISSUER_PERMISSION_MASK = charge_market_fee|white_list|override_authority|transfer_restricted|disable_force_settle|global_settle;
const static uint32_t UIA_ASSET_ISSUER_PERMISSION_MASK = charge_market_fee|white_list|override_authority|transfer_restricted;
const static uint32_t ASSET_ISSUER_PERMISSION_MASK = charge_market_fee|white_list|override_authority|transfer_restricted|disable_force_settle|global_settle|disable_confidential;
const static uint32_t UIA_ASSET_ISSUER_PERMISSION_MASK = charge_market_fee|white_list|override_authority|transfer_restricted|disable_confidential;
enum reserved_spaces
{
@ -133,13 +135,13 @@ namespace graphene { namespace chain {
impl_index_meta_object_type,
impl_asset_dynamic_data_type,
impl_asset_bitasset_data_type,
impl_committee_member_feeds_object_type,
impl_account_balance_object_type,
impl_account_statistics_object_type,
impl_account_debt_object_type,
impl_transaction_object_type,
impl_block_summary_object_type,
impl_account_transaction_history_object_type,
impl_blinded_balance_object_type,
impl_chain_property_object_type,
impl_witness_schedule_object_type
};
@ -149,9 +151,6 @@ namespace graphene { namespace chain {
meta_account_object_type
};
struct chain_parameters;
//typedef fc::unsigned_int object_id_type;
//typedef uint64_t object_id_type;
class account_object;
@ -166,7 +165,6 @@ namespace graphene { namespace chain {
class operation_history_object;
class withdraw_permission_object;
class vesting_balance_object;
class witness_schedule_object;
class worker_object;
class balance_object;
@ -193,10 +191,11 @@ namespace graphene { namespace chain {
class asset_bitasset_data_object;
class account_balance_object;
class account_statistics_object;
class account_debt_object;
class transaction_object;
class block_summary_object;
class account_transaction_history_object;
class chain_property_object;
class witness_schedule_object;
typedef object_id< implementation_ids, impl_global_property_object_type, global_property_object> global_property_id_type;
typedef object_id< implementation_ids, impl_dynamic_global_property_object_type, dynamic_global_property_object> dynamic_global_property_id_type;
@ -204,14 +203,14 @@ namespace graphene { namespace chain {
typedef object_id< implementation_ids, impl_asset_bitasset_data_type, asset_bitasset_data_object> asset_bitasset_data_id_type;
typedef object_id< implementation_ids, impl_account_balance_object_type, account_balance_object> account_balance_id_type;
typedef object_id< implementation_ids, impl_account_statistics_object_type,account_statistics_object> account_statistics_id_type;
typedef object_id< implementation_ids, impl_account_debt_object_type, account_debt_object> account_debt_id_type;
typedef object_id< implementation_ids, impl_transaction_object_type, transaction_object> transaction_obj_id_type;
typedef object_id< implementation_ids, impl_block_summary_object_type, block_summary_object> block_summary_id_type;
typedef object_id< implementation_ids,
impl_account_transaction_history_object_type,
account_transaction_history_object> account_transaction_history_id_type;
typedef object_id< implementation_ids, impl_witness_schedule_object_type, witness_schedule_object > witness_schedule_id_type;
typedef object_id< implementation_ids, impl_chain_property_object_type, chain_property_object> chain_property_id_type;
typedef object_id< implementation_ids, impl_witness_schedule_object_type, witness_schedule_object> witness_schedule_id_type;
typedef fc::array<char, GRAPHENE_MAX_ASSET_SYMBOL_LENGTH> symbol_type;
typedef fc::ripemd160 block_id_type;
@ -220,104 +219,8 @@ namespace graphene { namespace chain {
typedef fc::sha256 digest_type;
typedef fc::ecc::compact_signature signature_type;
typedef safe<int64_t> share_type;
typedef fc::ripemd160 secret_hash_type;
typedef uint16_t weight_type;
/**
* @brief An ID for some votable object
*
* This class stores an ID for a votable object. The ID is comprised of two fields: a type, and an instance. The
* type field stores which kind of object is being voted on, and the instance stores which specific object of that
* type is being referenced by this ID.
*
* A value of vote_id_type is implicitly convertible to an unsigned 32-bit integer containing only the instance. It
* may also be implicitly assigned from a uint32_t, which will update the instance. It may not, however, be
* implicitly constructed from a uint32_t, as in this case, the type would be unknown.
*
* On the wire, a vote_id_type is represented as a 32-bit integer with the type in the lower 8 bits and the instance
* in the upper 24 bits. This means that types may never exceed 8 bits, and instances may never exceed 24 bits.
*
* In JSON, a vote_id_type is represented as a string "type:instance", i.e. "1:5" would be type 1 and instance 5.
*
* @note In the Graphene protocol, vote_id_type instances are unique across types; that is to say, if an object of
* type 1 has instance 4, an object of type 0 may not also have instance 4. In other words, the type is not a
* namespace for instances; it is only an informational field.
*/
struct vote_id_type
{
/// Lower 8 bits are type; upper 24 bits are instance
uint32_t content;
enum vote_type
{
committee,
witness,
worker,
VOTE_TYPE_COUNT
};
/// Default constructor. Sets type and instance to 0
vote_id_type():content(0){}
/// Construct this vote_id_type with provided type and instance
vote_id_type(vote_type type, uint32_t instance = 0)
: content(instance<<8 | type)
{}
/// Construct this vote_id_type from a serial string in the form "type:instance"
explicit vote_id_type(const std::string& serial)
{
auto colon = serial.find(':');
if( colon != string::npos )
*this = vote_id_type(vote_type(std::stoul(serial.substr(0, colon))), std::stoul(serial.substr(colon+1)));
}
/// Set the type of this vote_id_type
void set_type(vote_type type)
{
content &= 0xffffff00;
content |= type & 0xff;
}
/// Get the type of this vote_id_type
vote_type type()const
{
return vote_type(content & 0xff);
}
/// Set the instance of this vote_id_type
void set_instance(uint32_t instance)
{
assert(instance < 0x01000000);
content &= 0xff;
content |= instance << 8;
}
/// Get the instance of this vote_id_type
uint32_t instance()const
{
return content >> 8;
}
vote_id_type& operator =(vote_id_type other)
{
content = other.content;
return *this;
}
/// Set the instance of this vote_id_type
vote_id_type& operator =(uint32_t instance)
{
set_instance(instance);
return *this;
}
/// Get the instance of this vote_id_type
operator uint32_t()const
{
return instance();
}
/// Convert this vote_id_type to a serial string in the form "type:instance"
explicit operator std::string()const
{
return std::to_string(type()) + ":" + std::to_string(instance());
}
};
struct public_key_type
{
struct binary_key
@ -347,14 +250,7 @@ namespace fc
{
void to_variant( const graphene::chain::public_key_type& var, fc::variant& vo );
void from_variant( const fc::variant& var, graphene::chain::public_key_type& vo );
void to_variant( const graphene::chain::vote_id_type& var, fc::variant& vo );
void from_variant( const fc::variant& var, graphene::chain::vote_id_type& vo );
}
FC_REFLECT_TYPENAME( graphene::chain::vote_id_type::vote_type )
FC_REFLECT_TYPENAME( fc::flat_set<graphene::chain::vote_id_type> )
FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(VOTE_TYPE_COUNT) )
FC_REFLECT( graphene::chain::vote_id_type, (content) )
FC_REFLECT( graphene::chain::public_key_type, (key_data) )
FC_REFLECT( graphene::chain::public_key_type::binary_key, (data)(check) )
@ -384,13 +280,13 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type,
(impl_index_meta_object_type)
(impl_asset_dynamic_data_type)
(impl_asset_bitasset_data_type)
(impl_committee_member_feeds_object_type)
(impl_account_balance_object_type)
(impl_account_statistics_object_type)
(impl_account_debt_object_type)
(impl_transaction_object_type)
(impl_block_summary_object_type)
(impl_account_transaction_history_object_type)
(impl_blinded_balance_object_type)
(impl_chain_property_object_type)
(impl_witness_schedule_object_type)
)
@ -417,11 +313,9 @@ FC_REFLECT_TYPENAME( graphene::chain::asset_dynamic_data_id_type )
FC_REFLECT_TYPENAME( graphene::chain::asset_bitasset_data_id_type )
FC_REFLECT_TYPENAME( graphene::chain::account_balance_id_type )
FC_REFLECT_TYPENAME( graphene::chain::account_statistics_id_type )
FC_REFLECT_TYPENAME( graphene::chain::account_debt_id_type )
FC_REFLECT_TYPENAME( graphene::chain::transaction_obj_id_type )
FC_REFLECT_TYPENAME( graphene::chain::block_summary_id_type )
FC_REFLECT_TYPENAME( graphene::chain::account_transaction_history_id_type )
FC_REFLECT_TYPENAME( graphene::chain::witness_schedule_id_type )
FC_REFLECT( graphene::chain::void_t, )
FC_REFLECT_ENUM( graphene::chain::asset_issuer_permission_flags, (charge_market_fee)(white_list)(transfer_restricted)(override_authority)(disable_force_settle)(global_settle) )
FC_REFLECT_ENUM( graphene::chain::asset_issuer_permission_flags, (charge_market_fee)(white_list)(transfer_restricted)(override_authority)(disable_force_settle)(global_settle)(disable_confidential) )

View file

@ -56,9 +56,6 @@ namespace graphene { namespace chain {
FC_ASSERT( fee.amount >= 0 );
FC_ASSERT( amount.amount > 0 );
}
void get_impacted_accounts( flat_set<account_id_type>& i )const
{ i.insert(owner); }
};
/**
@ -87,7 +84,8 @@ namespace graphene { namespace chain {
}
};
}} // graphene::chain
} } // graphene::chain
FC_REFLECT( graphene::chain::vesting_balance_create_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation::fee_parameters_type, (fee) )

View file

@ -0,0 +1,147 @@
/*
* Copyright (c) 2015, Cryptonomex, Inc.
* All rights reserved.
*
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
* are permitted until September 8, 2015, provided that the following conditions are met:
*
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <cassert>
#include <cstdint>
#include <string>
#include <fc/container/flat.hpp>
#include <fc/reflect/reflect.hpp>
namespace graphene { namespace chain {
/**
* @brief An ID for some votable object
*
* This class stores an ID for a votable object. The ID is comprised of two fields: a type, and an instance. The
* type field stores which kind of object is being voted on, and the instance stores which specific object of that
* type is being referenced by this ID.
*
* A value of vote_id_type is implicitly convertible to an unsigned 32-bit integer containing only the instance. It
* may also be implicitly assigned from a uint32_t, which will update the instance. It may not, however, be
* implicitly constructed from a uint32_t, as in this case, the type would be unknown.
*
* On the wire, a vote_id_type is represented as a 32-bit integer with the type in the lower 8 bits and the instance
* in the upper 24 bits. This means that types may never exceed 8 bits, and instances may never exceed 24 bits.
*
* In JSON, a vote_id_type is represented as a string "type:instance", i.e. "1:5" would be type 1 and instance 5.
*
* @note In the Graphene protocol, vote_id_type instances are unique across types; that is to say, if an object of
* type 1 has instance 4, an object of type 0 may not also have instance 4. In other words, the type is not a
* namespace for instances; it is only an informational field.
*/
struct vote_id_type
{
/// Lower 8 bits are type; upper 24 bits are instance
uint32_t content;
friend size_t hash_value( vote_id_type v ) { return std::hash<uint32_t>()(v.content); }
enum vote_type
{
committee,
witness,
worker,
VOTE_TYPE_COUNT
};
/// Default constructor. Sets type and instance to 0
vote_id_type():content(0){}
/// Construct this vote_id_type with provided type and instance
vote_id_type(vote_type type, uint32_t instance = 0)
: content(instance<<8 | type)
{}
/// Construct this vote_id_type from a serial string in the form "type:instance"
explicit vote_id_type(const std::string& serial)
{
auto colon = serial.find(':');
if( colon != std::string::npos )
*this = vote_id_type(vote_type(std::stoul(serial.substr(0, colon))), std::stoul(serial.substr(colon+1)));
}
/// Set the type of this vote_id_type
void set_type(vote_type type)
{
content &= 0xffffff00;
content |= type & 0xff;
}
/// Get the type of this vote_id_type
vote_type type()const
{
return vote_type(content & 0xff);
}
/// Set the instance of this vote_id_type
void set_instance(uint32_t instance)
{
assert(instance < 0x01000000);
content &= 0xff;
content |= instance << 8;
}
/// Get the instance of this vote_id_type
uint32_t instance()const
{
return content >> 8;
}
vote_id_type& operator =(vote_id_type other)
{
content = other.content;
return *this;
}
/// Set the instance of this vote_id_type
vote_id_type& operator =(uint32_t instance)
{
set_instance(instance);
return *this;
}
/// Get the instance of this vote_id_type
operator uint32_t()const
{
return instance();
}
/// Convert this vote_id_type to a serial string in the form "type:instance"
explicit operator std::string()const
{
return std::to_string(type()) + ":" + std::to_string(instance());
}
};
class global_property_object;
vote_id_type get_next_vote_id( global_property_object& gpo, vote_id_type::vote_type type );
} } // graphene::chain
namespace fc
{
class variant;
void to_variant( const graphene::chain::vote_id_type& var, fc::variant& vo );
void from_variant( const fc::variant& var, graphene::chain::vote_id_type& vo );
} // fc
FC_REFLECT_TYPENAME( graphene::chain::vote_id_type::vote_type )
FC_REFLECT_TYPENAME( fc::flat_set<graphene::chain::vote_id_type> )
FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(VOTE_TYPE_COUNT) )
FC_REFLECT( graphene::chain::vote_id_type, (content) )

View file

@ -43,9 +43,6 @@ namespace graphene { namespace chain {
account_id_type fee_payer()const { return withdraw_from_account; }
void validate()const;
void get_impacted_accounts( flat_set<account_id_type>& i)const
{ i.insert( authorized_account ); }
};
/**
@ -81,8 +78,6 @@ namespace graphene { namespace chain {
account_id_type fee_payer()const { return withdraw_from_account; }
void validate()const;
void get_impacted_accounts( flat_set<account_id_type>& i)const
{ i.insert( authorized_account ); }
};
/**
@ -121,8 +116,6 @@ namespace graphene { namespace chain {
account_id_type fee_payer()const { return withdraw_to_account; }
void validate()const;
share_type calculate_fee(const fee_parameters_type& k)const;
void get_impacted_accounts( flat_set<account_id_type>& i)const
{ i.insert( withdraw_from_account ); }
};
/**
@ -147,11 +140,10 @@ namespace graphene { namespace chain {
account_id_type fee_payer()const { return withdraw_from_account; }
void validate()const;
void get_impacted_accounts( flat_set<account_id_type>& i)const
{ i.insert( authorized_account ); }
};
} } // graphene::chain
FC_REFLECT( graphene::chain::withdraw_permission_create_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::withdraw_permission_update_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::withdraw_permission_claim_operation::fee_parameters_type, (fee)(price_per_kbyte) )
@ -164,4 +156,3 @@ FC_REFLECT( graphene::chain::withdraw_permission_update_operation, (fee)(withdra
FC_REFLECT( graphene::chain::withdraw_permission_claim_operation, (fee)(withdraw_permission)(withdraw_from_account)(withdraw_to_account)(amount_to_withdraw)(memo) );
FC_REFLECT( graphene::chain::withdraw_permission_delete_operation, (fee)(withdraw_from_account)(authorized_account)
(withdrawal_permission) )

View file

@ -19,41 +19,42 @@ namespace graphene { namespace chain {
account_id_type witness_account;
string url;
public_key_type block_signing_key;
secret_hash_type initial_secret;
account_id_type fee_payer()const { return witness_account; }
void validate()const;
};
/**
/**
* @brief Update a witness object's URL and block signing key.
* @ingroup operations
* Used to move witness pay from accumulated_income to their account balance.
*
* TODO: remove this operation, send witness pay into a vesting balance object and
* have the witness claim the funds from there.
*/
struct witness_withdraw_pay_operation : public base_operation
struct witness_update_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION; };
struct fee_parameters_type
{
share_type fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
};
asset fee;
/// The account to pay. Must match from_witness->witness_account. This account pays the fee for this operation.
account_id_type to_account;
witness_id_type from_witness;
share_type amount;
asset fee;
/// The witness object to update.
witness_id_type witness;
/// The account which owns the witness. This account pays the fee for this operation.
account_id_type witness_account;
/// The new URL.
optional< string > new_url;
/// The new block signing key.
optional< public_key_type > new_signing_key;
account_id_type fee_payer()const { return to_account; }
account_id_type fee_payer()const { return witness_account; }
void validate()const;
};
/// TODO: witness_resign_operation : public base_operation
} } // graphene::chain
FC_REFLECT( graphene::chain::witness_create_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::witness_withdraw_pay_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::witness_create_operation, (fee)(witness_account)(url)(block_signing_key) )
FC_REFLECT( graphene::chain::witness_create_operation, (fee)(witness_account)(url)(block_signing_key)(initial_secret) )
FC_REFLECT( graphene::chain::witness_withdraw_pay_operation, (fee)(from_witness)(to_account)(amount) )
FC_REFLECT( graphene::chain::witness_update_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::witness_update_operation, (fee)(witness)(witness_account)(new_url)(new_signing_key) )

View file

@ -32,30 +32,10 @@ namespace graphene { namespace chain {
transaction_evaluation_state( database* db = nullptr )
:_db(db){}
bool check_authority(const account_object&,
authority::classification auth_class = authority::active,
int depth = 0);
bool check_authority(const authority&,
authority::classification auth_class = authority::active,
int depth = 0);
database& db()const { FC_ASSERT( _db ); return *_db; }
bool signed_by(const public_key_type& k);
bool signed_by(const address& k);
/// cached approval (accounts and keys)
flat_set<pair<object_id_type,authority::classification>> approved_by;
/// Used to look up new objects using transaction relative IDs
database& db()const { assert( _db ); return *_db; }
vector<operation_result> operation_results;
/**
* When an address is referenced via check authority it is flagged as being used, all addresses must be
* flagged as being used or the transaction will fail.
*/
flat_map<public_key_type, bool> _sigs;
const signed_transaction* _trx = nullptr;
database* _db = nullptr;
bool _is_proposed_trx = false;

View file

@ -45,8 +45,9 @@ namespace graphene { namespace chain {
static const uint8_t type_id = impl_transaction_object_type;
signed_transaction trx;
time_point_sec expiration;
transaction_id_type trx_id;
time_point_sec get_expiration()const { return trx.expiration; }
};
struct by_expiration;
@ -57,11 +58,11 @@ namespace graphene { namespace chain {
indexed_by<
hashed_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
hashed_unique< tag<by_trx_id>, BOOST_MULTI_INDEX_MEMBER(transaction_object, transaction_id_type, trx_id), std::hash<transaction_id_type> >,
ordered_non_unique< tag<by_expiration>, BOOST_MULTI_INDEX_MEMBER(transaction_object, time_point_sec, expiration)>
ordered_non_unique< tag<by_expiration>, const_mem_fun<transaction_object, time_point_sec, &transaction_object::get_expiration > >
>
> transaction_multi_index_type;
typedef generic_index<transaction_object, transaction_multi_index_type> transaction_index;
} }
FC_REFLECT_DERIVED( graphene::chain::transaction_object, (graphene::db::object), (trx)(expiration) )
FC_REFLECT_DERIVED( graphene::chain::transaction_object, (graphene::db::object), (trx)(trx_id) )

View file

@ -155,14 +155,23 @@ namespace graphene { namespace chain {
*/
void withdraw(const fc::time_point_sec& now, const asset& amount);
bool is_withdraw_allowed(const fc::time_point_sec& now, const asset& amount)const;
/**
* Get amount of allowed withdrawal.
*/
asset get_allowed_withdraw(const time_point_sec& now)const;
};
/**
* @ingroup object_index
*/
struct by_account;
typedef multi_index_container<
vesting_balance_object,
indexed_by<
hashed_unique< tag<by_id>, member< object, object_id_type, &object::id > >
hashed_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_non_unique< tag<by_account>,
member<vesting_balance_object, account_id_type, &vesting_balance_object::owner>
>
>
> vesting_balance_multi_index_type;
/**

View file

@ -30,15 +30,13 @@ namespace graphene { namespace chain {
object_id_type do_apply( const witness_create_operation& o );
};
class witness_withdraw_pay_evaluator : public evaluator<witness_withdraw_pay_evaluator>
class witness_update_evaluator : public evaluator<witness_update_evaluator>
{
public:
typedef witness_withdraw_pay_operation operation_type;
typedef witness_update_operation operation_type;
void_result do_evaluate( const operation_type& o );
void_result do_apply( const operation_type& o );
const witness_object* witness;
const account_object* to_account;
void_result do_evaluate( const witness_update_operation& o );
void_result do_apply( const witness_update_operation& o );
};
} } // graphene::chain

View file

@ -32,17 +32,20 @@ namespace graphene { namespace chain {
static const uint8_t type_id = witness_object_type;
account_id_type witness_account;
uint64_t last_aslot = 0;
public_key_type signing_key;
secret_hash_type next_secret_hash;
secret_hash_type previous_secret;
share_type accumulated_income;
optional< vesting_balance_id_type > pay_vb;
vote_id_type vote_id;
uint64_t total_votes = 0;
string url;
int64_t total_missed = 0;
witness_object() : vote_id(vote_id_type::witness) {}
};
struct by_account;
struct by_vote_id;
struct by_last_block;
using witness_multi_index_type = multi_index_container<
witness_object,
indexed_by<
@ -51,6 +54,9 @@ namespace graphene { namespace chain {
>,
hashed_unique< tag<by_account>,
member<witness_object, account_id_type, &witness_object::witness_account>
>,
hashed_unique< tag<by_vote_id>,
member<witness_object, vote_id_type, &witness_object::vote_id>
>
>
>;
@ -59,9 +65,11 @@ namespace graphene { namespace chain {
FC_REFLECT_DERIVED( graphene::chain::witness_object, (graphene::db::object),
(witness_account)
(last_aslot)
(signing_key)
(next_secret_hash)
(previous_secret)
(accumulated_income)
(pay_vb)
(vote_id)
(url) )
(total_votes)
(url)
(total_missed)
)

View file

@ -16,73 +16,27 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
// needed to serialize witness_scheduler
#include <fc/container/deque.hpp>
#include <fc/uint128.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/witness_scheduler.hpp>
#include <graphene/chain/witness_scheduler_rng.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
namespace graphene { namespace chain {
typedef hash_ctr_rng<
/* HashClass = */ fc::sha256,
/* SeedLength = */ GRAPHENE_RNG_SEED_LENGTH
> witness_scheduler_rng;
class witness_schedule_object;
typedef generic_witness_scheduler<
/* WitnessID = */ witness_id_type,
/* RNG = */ witness_scheduler_rng,
/* CountType = */ decltype( chain_parameters::maximum_witness_count ),
/* OffsetType = */ uint32_t,
/* debug = */ true
> witness_scheduler;
typedef generic_far_future_witness_scheduler<
/* WitnessID = */ witness_id_type,
/* RNG = */ witness_scheduler_rng,
/* CountType = */ decltype( chain_parameters::maximum_witness_count ),
/* OffsetType = */ uint32_t,
/* debug = */ true
> far_future_witness_scheduler;
class witness_schedule_object : public abstract_object<witness_schedule_object>
class witness_schedule_object : public graphene::db::abstract_object<witness_schedule_object>
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_witness_schedule_object_type;
witness_scheduler scheduler;
uint32_t last_scheduling_block;
uint64_t slots_since_genesis = 0;
fc::array< char, sizeof(secret_hash_type) > rng_seed;
/**
* Not necessary for consensus, but used for figuring out the participation rate.
* The nth bit is 0 if the nth slot was unfilled, else it is 1.
*/
fc::uint128 recent_slots_filled;
vector< witness_id_type > current_shuffled_witnesses;
};
} }
FC_REFLECT( graphene::chain::witness_scheduler,
(_turns)
(_tokens)
(_min_token_count)
(_ineligible_waiting_for_token)
(_ineligible_no_turn)
(_eligible)
(_schedule)
(_lame_duck)
)
FC_REFLECT_DERIVED( graphene::chain::witness_schedule_object, (graphene::chain::object),
(scheduler)
(last_scheduling_block)
(slots_since_genesis)
(rng_seed)
(recent_slots_filled)
)
FC_REFLECT_DERIVED(
graphene::chain::witness_schedule_object,
(graphene::db::object),
(current_shuffled_witnesses)
)

View file

@ -1,416 +0,0 @@
/*
* Copyright (c) 2015, Cryptonomex, Inc.
* All rights reserved.
*
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
* are permitted until September 8, 2015, provided that the following conditions are met:
*
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <algorithm>
#include <deque>
#include <memory>
#include <vector>
#include <boost/container/flat_set.hpp>
namespace graphene { namespace chain {
using boost::container::flat_set;
enum witness_scheduler_relax_flags
{
emit_turn = 0x01,
emit_token = 0x02
};
template< typename WitnessID, typename RNG, typename CountType, typename OffsetType, bool debug = true >
class generic_witness_scheduler
{
public:
void check_invariant() const
{
#ifndef NDEBUG
CountType tokens = _ineligible_no_turn.size() + _eligible.size();
CountType turns = _eligible.size();
for( const std::pair< WitnessID, bool >& item : _ineligible_waiting_for_token )
turns += (item.second ? 1 : 0 );
assert( _tokens == tokens );
assert( _turns == turns );
#endif
flat_set< WitnessID > witness_set;
// make sure each witness_id occurs only once among the three states
auto process_id = [&]( WitnessID item )
{
assert( witness_set.find( item ) == witness_set.end() );
witness_set.insert( item );
} ;
for( const std::pair< WitnessID, bool >& item : _ineligible_waiting_for_token )
process_id( item.first );
for( const WitnessID& item : _ineligible_no_turn )
process_id( item );
for( const WitnessID& item : _eligible )
process_id( item );
return;
}
/**
* Deterministically evolve over time
*/
uint32_t relax()
{
uint32_t relax_flags = 0;
if( debug ) check_invariant();
assert( _min_token_count > 0 );
// turn distribution
if( _turns == 0 )
{
relax_flags |= emit_turn;
for( const WitnessID& item : _ineligible_no_turn )
_eligible.push_back( item );
_turns += _ineligible_no_turn.size();
_ineligible_no_turn.clear();
if( debug ) check_invariant();
for( std::pair< WitnessID, bool >& item : _ineligible_waiting_for_token )
{
assert( item.second == false );
item.second = true;
}
_turns += _ineligible_waiting_for_token.size();
if( debug ) check_invariant();
}
// token distribution
while( true )
{
if( _ineligible_waiting_for_token.empty() )
{
// eligible must be non-empty
assert( !_eligible.empty() );
return relax_flags;
}
if( _tokens >= _min_token_count )
{
if( !_eligible.empty() )
return relax_flags;
}
const std::pair< WitnessID, bool >& item = _ineligible_waiting_for_token.front();
if( item.second )
_eligible.push_back( item.first );
else
_ineligible_no_turn.push_back( item.first );
_ineligible_waiting_for_token.pop_front();
relax_flags |= emit_token;
_tokens++;
if( debug ) check_invariant();
}
return relax_flags;
}
/**
* Add another element to _schedule
*/
uint32_t produce_schedule( RNG& rng )
{
uint32_t relax_flags = relax();
if( debug ) check_invariant();
if( _eligible.empty() )
return relax_flags;
decltype( rng( _eligible.size() ) ) pos = rng( _eligible.size() );
assert( (pos >= 0) && (pos < _eligible.size()) );
auto it = _eligible.begin() + pos;
_schedule.push_back( *it );
_ineligible_waiting_for_token.emplace_back( *it, false );
_eligible.erase( it );
_turns--;
_tokens--;
if( debug ) check_invariant();
return relax_flags;
}
/**
* Pull an element from _schedule
*/
WitnessID consume_schedule()
{
assert( _schedule.size() > 0 );
WitnessID result = _schedule.front();
_schedule.pop_front();
auto it = _lame_duck.find( result );
if( it != _lame_duck.end() )
_lame_duck.erase( it );
if( debug ) check_invariant();
return result;
}
/**
* Remove all witnesses in the removal_set from
* future scheduling (but not from the current schedule).
*/
template< typename T >
void remove_all( const T& removal_set )
{
if( debug ) check_invariant();
_ineligible_waiting_for_token.erase(
std::remove_if(
_ineligible_waiting_for_token.begin(),
_ineligible_waiting_for_token.end(),
[&]( const std::pair< WitnessID, bool >& item ) -> bool
{
bool found = removal_set.find( item.first ) != removal_set.end();
_turns -= (found & item.second) ? 1 : 0;
return found;
} ),
_ineligible_waiting_for_token.end() );
if( debug ) check_invariant();
_ineligible_no_turn.erase(
std::remove_if(
_ineligible_no_turn.begin(),
_ineligible_no_turn.end(),
[&]( WitnessID item ) -> bool
{
bool found = (removal_set.find( item ) != removal_set.end());
_tokens -= (found ? 1 : 0);
return found;
} ),
_ineligible_no_turn.end() );
if( debug ) check_invariant();
_eligible.erase(
std::remove_if(
_eligible.begin(),
_eligible.end(),
[&]( WitnessID item ) -> bool
{
bool found = (removal_set.find( item ) != removal_set.end());
_tokens -= (found ? 1 : 0);
_turns -= (found ? 1 : 0);
return found;
} ),
_eligible.end() );
if( debug ) check_invariant();
return;
}
/**
* Convenience function to call insert_all() and remove_all()
* as needed to update to the given revised_set.
*/
template< typename T >
void insert_all( const T& insertion_set )
{
if( debug ) check_invariant();
for( const WitnessID wid : insertion_set )
{
_eligible.push_back( wid );
}
_turns += insertion_set.size();
_tokens += insertion_set.size();
if( debug ) check_invariant();
return;
}
/**
* Convenience function to call insert_all() and remove_all()
* as needed to update to the given revised_set.
*
* This function calls find() on revised_set for all current
* witnesses. Running time is O(n*log(n)) if the revised_set
* implementation of find() is O(log(n)).
*
* TODO: Rewriting to use std::set_difference may marginally
* increase efficiency, but a benchmark is needed to justify this.
*/
template< typename T >
void update( const T& revised_set )
{
flat_set< WitnessID > current_set;
flat_set< WitnessID > schedule_set;
current_set.reserve(
_ineligible_waiting_for_token.size()
+ _ineligible_no_turn.size()
+ _eligible.size()
+ _schedule.size() );
for( const auto& item : _ineligible_waiting_for_token )
current_set.insert( item.first );
for( const WitnessID& item : _ineligible_no_turn )
current_set.insert( item );
for( const WitnessID& item : _eligible )
current_set.insert( item );
for( const WitnessID& item : _schedule )
{
current_set.insert( item );
schedule_set.insert( item );
}
flat_set< WitnessID > insertion_set;
insertion_set.reserve( revised_set.size() );
for( const WitnessID& item : revised_set )
{
if( current_set.find( item ) == current_set.end() )
insertion_set.insert( item );
}
flat_set< WitnessID > removal_set;
removal_set.reserve( current_set.size() );
for( const WitnessID& item : current_set )
{
if( revised_set.find( item ) == revised_set.end() )
{
if( schedule_set.find( item ) == schedule_set.end() )
removal_set.insert( item );
else
_lame_duck.insert( item );
}
}
insert_all( insertion_set );
remove_all( removal_set );
return;
}
/**
* Get the number of scheduled witnesses
*/
size_t size( )const
{
return _schedule.size();
}
bool get_slot( OffsetType offset, WitnessID& wit )const
{
if( offset >= _schedule.size() )
return false;
wit = _schedule[ offset ];
return true;
}
/**
* Reset the schedule, then re-schedule the given witness as the
* first witness.
*/
void reset_schedule( WitnessID first_witness )
{
_schedule.clear();
for( const WitnessID& wid : _ineligible_no_turn )
{
_eligible.push_back( wid );
}
_turns += _ineligible_no_turn.size();
_ineligible_no_turn.clear();
for( const auto& item : _ineligible_waiting_for_token )
{
_eligible.push_back( item.first );
_turns += (item.second ? 0 : 1);
}
_tokens += _ineligible_waiting_for_token.size();
_ineligible_waiting_for_token.clear();
if( debug ) check_invariant();
auto it = std::find( _eligible.begin(), _eligible.end(), first_witness );
assert( it != _eligible.end() );
_schedule.push_back( *it );
_ineligible_waiting_for_token.emplace_back( *it, false );
_eligible.erase( it );
_turns--;
_tokens--;
if( debug ) check_invariant();
return;
}
// keep track of total turns / tokens in existence
CountType _turns = 0;
CountType _tokens = 0;
// new tokens handed out when _tokens < _min_token_count
CountType _min_token_count;
// WitnessID appears in exactly one of the following:
// has no token; second indicates whether we have a turn or not:
std::deque < std::pair< WitnessID, bool > > _ineligible_waiting_for_token; // ".." | "T."
// has token, but no turn
std::vector< WitnessID > _ineligible_no_turn; // ".t"
// has token and turn
std::vector< WitnessID > _eligible; // "Tt"
// scheduled
std::deque < WitnessID > _schedule;
// in _schedule, but not to be replaced
flat_set< WitnessID > _lame_duck;
};
template< typename WitnessID, typename RNG, typename CountType, typename OffsetType, bool debug = true >
class generic_far_future_witness_scheduler
{
public:
generic_far_future_witness_scheduler(
const generic_witness_scheduler< WitnessID, RNG, CountType, OffsetType, debug >& base_scheduler,
RNG rng
)
{
generic_witness_scheduler< WitnessID, RNG, CountType, OffsetType, debug > extended_scheduler = base_scheduler;
_begin_offset = base_scheduler.size()+1;
while( (extended_scheduler.produce_schedule( rng ) & emit_turn) == 0 )
_begin_offset++;
assert( _begin_offset == extended_scheduler.size() );
_end_offset = _begin_offset;
while( (extended_scheduler.produce_schedule( rng ) & emit_turn) == 0 )
_end_offset++;
assert( _end_offset == extended_scheduler.size()-1 );
_schedule.resize( extended_scheduler._schedule.size() );
std::copy( extended_scheduler._schedule.begin(),
extended_scheduler._schedule.end(),
_schedule.begin() );
return;
}
bool get_slot( OffsetType offset, WitnessID& wit )const
{
if( offset <= _end_offset )
wit = _schedule[ offset ];
else
wit = _schedule[ _begin_offset +
(
(offset - _begin_offset) %
(_end_offset + 1 - _begin_offset)
) ];
return true;
}
std::vector< WitnessID > _schedule;
OffsetType _begin_offset;
OffsetType _end_offset;
};
} }

View file

@ -1,124 +0,0 @@
/*
* Copyright (c) 2015, Cryptonomex, Inc.
* All rights reserved.
*
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
* are permitted until September 8, 2015, provided that the following conditions are met:
*
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <boost/multiprecision/integer.hpp>
namespace graphene { namespace chain {
/**
* Always returns 0. Useful for testing.
*/
class nullary_rng
{
public:
nullary_rng() {}
virtual ~nullary_rng() {}
template< typename T > T operator()( T max )
{ return T(0); }
} ;
/**
* The sha256_ctr_rng generates bits using SHA256 in counter (CTR)
* mode.
*/
template< class HashClass, int SeedLength=sizeof(secret_hash_type) >
class hash_ctr_rng
{
public:
hash_ctr_rng( const char* seed, uint64_t counter = 0 )
: _counter( counter ), _current_offset( 0 )
{
memcpy( _seed, seed, SeedLength );
_reset_current_value();
return;
}
virtual ~hash_ctr_rng() {}
uint64_t get_bits( uint8_t count )
{
uint64_t result = 0;
uint64_t mask = 1;
// grab the requested number of bits
while( count > 0 )
{
result |=
(
(
(
_current_value.data()[ (_current_offset >> 3) & 0x1F ]
& ( 1 << (_current_offset & 0x07) )
)
!= 0
) ? mask : 0
);
mask += mask;
--count;
++_current_offset;
if( _current_offset == (_current_value.data_size() << 3) )
{
_counter++;
_current_offset = 0;
_reset_current_value();
}
}
return result;
}
uint64_t operator()( uint64_t bound )
{
if( bound <= 1 )
return 0;
uint8_t bitcount = boost::multiprecision::detail::find_msb( bound ) + 1;
// probability of loop exiting is >= 1/2, so probability of
// running N times is bounded above by (1/2)^N
while( true )
{
uint64_t result = get_bits( bitcount );
if( result < bound )
return result;
}
}
// convenience method which does casting for types other than uint64_t
template< typename T > T operator()( T bound )
{ return (T) ( (*this)(uint64_t( bound )) ); }
void _reset_current_value()
{
// internal implementation detail, called to update
// _current_value when _counter changes
typename HashClass::encoder enc;
enc.write( _seed , SeedLength );
enc.write( (char *) &_counter, 8 );
_current_value = enc.result();
return;
}
uint64_t _counter;
char _seed[ SeedLength ];
HashClass _current_value;
uint16_t _current_offset;
static const int seed_length = SeedLength;
} ;
} }

View file

@ -121,16 +121,35 @@ namespace graphene { namespace chain {
/// Voting ID which represents disapproval of this worker
vote_id_type vote_against;
uint64_t total_votes_for = 0;
uint64_t total_votes_against = 0;
bool is_active(fc::time_point_sec now)const {
return now >= work_begin_date && now <= work_end_date;
}
/// TODO: remove unused argument
share_type approving_stake(const vector<uint64_t>& stake_vote_tallies)const {
return stake_vote_tallies[vote_for] - stake_vote_tallies[vote_against];
return int64_t( total_votes_for ) - int64_t( total_votes_against );
}
};
typedef flat_index<worker_object> worker_index;
struct by_account;
struct by_vote_for;
struct by_vote_against;
typedef multi_index_container<
worker_object,
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_non_unique< tag<by_account>, member< worker_object, account_id_type, &worker_object::worker_account > >,
ordered_unique< tag<by_vote_for>, member< worker_object, vote_id_type, &worker_object::vote_for > >,
ordered_unique< tag<by_vote_against>, member< worker_object, vote_id_type, &worker_object::vote_against > >
>
> worker_object_multi_index_type;
//typedef flat_index<worker_object> worker_index;
using worker_index = generic_index<worker_object, worker_object_multi_index_type>;
class worker_create_evaluator : public evaluator<worker_create_evaluator>
{
@ -155,6 +174,8 @@ FC_REFLECT_DERIVED( graphene::chain::worker_object, (graphene::db::object),
(worker)
(vote_for)
(vote_against)
(total_votes_for)
(total_votes_against)
(name)
(url)
)

View file

@ -213,18 +213,31 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat
// then we must check for margin calls and other issues
if( !_bitasset_data->is_prediction_market )
{
// Check that the order's debt per collateral is less than the system's minimum debt per collateral.
FC_ASSERT( ~call_obj->call_price <= _bitasset_data->current_feed.settlement_price,
"Insufficient collateral for debt.",
("a", ~call_obj->call_price)("b", _bitasset_data->current_feed.settlement_price));
auto call_order_id = call_obj->id;
// check to see if the order needs to be margin called now, but don't allow black swans and require there to be
// limit orders available that could be used to fill the order.
if( d.check_call_orders( *_debt_asset, false ) )
{
FC_ASSERT( !d.find_object( call_order_id ), "If updating the call order triggers a margin call, then it must completely cover the order" );
// if we filled at least one call order, we are OK if we totally filled.
GRAPHENE_ASSERT(
!d.find_object( call_order_id ),
call_order_update_unfilled_margin_call,
"Updating call order would trigger a margin call that cannot be fully filled",
("a", ~call_obj->call_price)("b", _bitasset_data->current_feed.settlement_price)
);
}
else
{
// We didn't fill any call orders. This may be because we
// aren't in margin call territory, or it may be because there
// were no matching orders. In the latter case, we throw.
GRAPHENE_ASSERT(
~call_obj->call_price < _bitasset_data->current_feed.settlement_price,
call_order_update_unfilled_margin_call,
"Updating call order would trigger a margin call that cannot be fully filled",
("a", ~call_obj->call_price)("b", _bitasset_data->current_feed.settlement_price)
);
}
}

View file

@ -21,6 +21,8 @@
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <graphene/chain/exceptions.hpp>
#include <fc/smart_ref_impl.hpp>
namespace graphene { namespace chain {
void_result proposal_create_evaluator::do_evaluate(const proposal_create_operation& o)
@ -45,7 +47,7 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati
FC_ASSERT( other.size() == 0 ); // TODO: what about other???
if( auths.find(account_id_type()) != auths.end() )
if( auths.find(GRAPHENE_COMMITTEE_ACCOUNT) != auths.end() )
{
GRAPHENE_ASSERT(
o.review_period_seconds.valid(),
@ -56,7 +58,7 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati
GRAPHENE_ASSERT(
*o.review_period_seconds >= global_parameters.committee_proposal_review_period,
proposal_create_review_period_insufficient,
"Review period of ${t} required, but at least ${min} required",
"Review period of ${t} specified, but at least ${min} required",
("t", *o.review_period_seconds)
("min", global_parameters.committee_proposal_review_period)
);
@ -75,6 +77,7 @@ object_id_type proposal_create_evaluator::do_apply(const proposal_create_operati
database& d = db();
const proposal_object& proposal = d.create<proposal_object>([&](proposal_object& proposal) {
_proposed_trx.expiration = o.expiration_time;
proposal.proposed_transaction = _proposed_trx;
proposal.expiration_time = o.expiration_time;
if( o.review_period_seconds )
@ -119,8 +122,7 @@ void_result proposal_update_evaluator::do_evaluate(const proposal_update_operati
"", ("id", id)("available", _proposal->available_owner_approvals) );
}
/* All authority checks happen outside of evaluators, TODO: verify this is checked elsewhere
*/
/* All authority checks happen outside of evaluators
if( (d.get_node_properties().skip_flags & database::skip_authority_check) == 0 )
{
for( const auto& id : o.key_approvals_to_add )
@ -132,6 +134,7 @@ void_result proposal_update_evaluator::do_evaluate(const proposal_update_operati
FC_ASSERT( trx_state->signed_by(id) );
}
}
*/
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }

View file

@ -24,33 +24,23 @@ namespace graphene { namespace chain {
bool proposal_object::is_authorized_to_execute(database& db) const
{
transaction_evaluation_state dry_run_eval(&db);
dry_run_eval._is_proposed_trx = true;
std::transform(available_active_approvals.begin(), available_active_approvals.end(),
std::inserter(dry_run_eval.approved_by, dry_run_eval.approved_by.end()), [](object_id_type id) {
return make_pair(id, authority::active);
});
std::transform(available_owner_approvals.begin(), available_owner_approvals.end(),
std::inserter(dry_run_eval.approved_by, dry_run_eval.approved_by.end()), [](object_id_type id) {
return make_pair(id, authority::owner);
});
signed_transaction tmp;
dry_run_eval._trx = &tmp;
for( auto key_id : available_key_approvals )
dry_run_eval._sigs.insert( std::make_pair(key_id,true) );
//insert into dry_run_eval->_trx.signatures
//dry_run_eval.signed_by.insert(available_key_approvals.begin(), available_key_approvals.end());
// Check all required approvals. If any of them are unsatisfied, return false.
for( const auto& id : required_active_approvals )
if( !dry_run_eval.check_authority(id(db), authority::active) )
return false;
for( const auto& id : required_owner_approvals )
if( !dry_run_eval.check_authority(id(db), authority::owner) )
return false;
try {
verify_authority( proposed_transaction.operations,
available_key_approvals,
[&]( account_id_type id ){ return &id(db).active; },
[&]( account_id_type id ){ return &id(db).owner; },
db.get_global_properties().parameters.max_authority_depth,
true, /* allow committeee */
available_active_approvals,
available_owner_approvals );
}
catch ( const fc::exception& e )
{
//idump((available_active_approvals));
//wlog((e.to_detail_string()));
return false;
}
return true;
}

View file

@ -34,6 +34,10 @@ namespace graphene { namespace chain {
*/
bool is_valid_name( const string& name )
{
#if GRAPHENE_MIN_ACCOUNT_NAME_LENGTH < 3
#error This is_valid_name implementation implicitly enforces minimum name length of 3.
#endif
const size_t len = name.size();
if( len < GRAPHENE_MIN_ACCOUNT_NAME_LENGTH )
return false;
@ -139,9 +143,10 @@ void account_create_operation::validate()const
FC_ASSERT( referrer_percent <= GRAPHENE_100_PERCENT );
FC_ASSERT( owner.num_auths() != 0 );
FC_ASSERT( owner.address_auths.size() == 0 );
// TODO: this asset causes many tests to fail, those tests should probably be updated
//FC_ASSERT( active.num_auths() != 0 );
FC_ASSERT( active.num_auths() != 0 );
FC_ASSERT( active.address_auths.size() == 0 );
FC_ASSERT( !owner.is_impossible(), "cannot create an account with an imposible owner authority threshold" );
FC_ASSERT( !active.is_impossible(), "cannot create an account with an imposible active authority threshold" );
options.validate();
}
@ -158,6 +163,7 @@ share_type account_update_operation::calculate_fee( const fee_parameters_type& k
void account_update_operation::validate()const
{
FC_ASSERT( account != GRAPHENE_TEMP_ACCOUNT );
FC_ASSERT( fee.amount >= 0 );
FC_ASSERT( account != account_id_type() );
FC_ASSERT( owner || active || new_options );
@ -165,11 +171,13 @@ void account_update_operation::validate()const
{
FC_ASSERT( owner->num_auths() != 0 );
FC_ASSERT( owner->address_auths.size() == 0 );
FC_ASSERT( !owner->is_impossible(), "cannot update an account with an imposible owner authority threshold" );
}
if( active )
{
FC_ASSERT( active->num_auths() != 0 );
FC_ASSERT( active->address_auths.size() == 0 );
FC_ASSERT( !active->is_impossible(), "cannot update an account with an imposible active authority threshold" );
}
if( new_options )

View file

@ -146,4 +146,27 @@ namespace graphene { namespace chain {
return (asset( cp.numerator(), settlement_price.base.asset_id ) / asset( cp.denominator(), settlement_price.quote.asset_id ));
}
// compile-time table of powers of 10 using template metaprogramming
template< int N >
struct p10
{
static const int64_t v = 10 * p10<N-1>::v;
};
template<>
struct p10<0>
{
static const int64_t v = 1;
};
const int64_t scaled_precision_lut[19] =
{
p10< 0 >::v, p10< 1 >::v, p10< 2 >::v, p10< 3 >::v,
p10< 4 >::v, p10< 5 >::v, p10< 6 >::v, p10< 7 >::v,
p10< 8 >::v, p10< 9 >::v, p10< 10 >::v, p10< 11 >::v,
p10< 12 >::v, p10< 13 >::v, p10< 14 >::v, p10< 15 >::v,
p10< 16 >::v, p10< 17 >::v, p10< 18 >::v
};
} } // graphene::chain

View file

@ -168,8 +168,6 @@ void asset_options::validate()const
FC_ASSERT( max_market_fee >= 0 && max_market_fee <= GRAPHENE_MAX_SHARE_SUPPLY );
// There must be no high bits in permissions whose meaning is not known.
FC_ASSERT( !(issuer_permissions & ~ASSET_ISSUER_PERMISSION_MASK) );
// There must be no high bits in flags which are not also high in permissions.
FC_ASSERT( !(flags & ~issuer_permissions ) );
// The global_settle flag may never be set (this is a permission only)
FC_ASSERT( !(flags & global_settle) );
core_exchange_rate.validate();

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 2015, Cryptonomex, Inc.
* All rights reserved.
*
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
* are permitted until September 8, 2015, provided that the following conditions are met:
*
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <graphene/chain/protocol/authority.hpp>
namespace graphene { namespace chain {
void add_authority_accounts(
flat_set<account_id_type>& result,
const authority& a
)
{
for( auto& item : a.account_auths )
result.insert( item.first );
}
} } // graphene::chain

View file

@ -58,18 +58,27 @@ namespace graphene { namespace chain {
checksum_type signed_block::calculate_merkle_root()const
{
if( transactions.size() == 0 ) return checksum_type();
if( transactions.size() == 0 )
return checksum_type();
vector<digest_type> ids;
ids.resize( ((transactions.size() + 1)/2)*2 );
vector<digest_type> ids;
ids.resize( transactions.size() );
for( uint32_t i = 0; i < transactions.size(); ++i )
ids[i] = transactions[i].merkle_digest();
while( ids.size() > 1 )
vector<digest_type>::size_type current_number_of_hashes = ids.size();
while( current_number_of_hashes > 1 )
{
for( uint32_t i = 0; i < transactions.size(); i += 2 )
ids[i/2] = digest_type::hash( std::make_pair( ids[i], ids[i+1] ) );
ids.resize( ids.size() / 2 );
// hash ID's in pairs
uint32_t i_max = current_number_of_hashes - (current_number_of_hashes&1);
uint32_t k = 0;
for( uint32_t i = 0; i < i_max; i += 2 )
ids[k++] = digest_type::hash( std::make_pair( ids[i], ids[i+1] ) );
if( current_number_of_hashes&1 )
ids[k++] = ids[i_max];
current_number_of_hashes = k;
}
return checksum_type::hash( ids[0] );
}

View file

@ -9,11 +9,17 @@ void committee_member_create_operation::validate()const
FC_ASSERT(url.size() < GRAPHENE_MAX_URL_LENGTH );
}
void committee_member_update_operation::validate()const
{
FC_ASSERT( fee.amount >= 0 );
if( new_url.valid() )
FC_ASSERT(new_url->size() < GRAPHENE_MAX_URL_LENGTH );
}
void committee_member_update_global_parameters_operation::validate() const
{
FC_ASSERT( fee.amount >= 0 );
new_parameters.validate();
}
} } // graphene::chain

View file

@ -0,0 +1,140 @@
#include <graphene/chain/protocol/confidential.hpp>
#include <graphene/chain/confidential_evaluator.hpp>
#include <graphene/chain/database.hpp>
#include <fc/crypto/base58.hpp>
#include <fc/io/raw.hpp>
#include <fc/crypto/elliptic.hpp>
namespace graphene { namespace chain {
void transfer_to_blind_operation::validate()const
{
FC_ASSERT( fee.amount >= 0 );
FC_ASSERT( amount.amount > 0 );
vector<commitment_type> in;
vector<commitment_type> out(outputs.size());
int64_t net_public = amount.amount.value;
for( uint32_t i = 0; i < out.size(); ++i )
{
out[i] = outputs[i].commitment;
/// require all outputs to be sorted prevents duplicates AND prevents implementations
/// from accidentally leaking information by how they arrange commitments.
if( i > 0 ) FC_ASSERT( out[i-1] < out[i], "all outputs must be sorted by commitment id" );
FC_ASSERT( !outputs[i].owner.is_impossible() );
}
FC_ASSERT( out.size(), "there must be at least one output" );
auto public_c = fc::ecc::blind(blinding_factor,net_public);
FC_ASSERT( fc::ecc::verify_sum( {public_c}, out, 0 ), "", ("net_public",net_public) );
if( outputs.size() > 1 )
{
for( auto out : outputs )
{
auto info = fc::ecc::range_get_info( out.range_proof );
FC_ASSERT( info.max_value <= GRAPHENE_MAX_SHARE_SUPPLY );
}
}
}
share_type transfer_to_blind_operation::calculate_fee( const fee_parameters_type& k )const
{
return k.fee + outputs.size() * k.price_per_output;
}
void transfer_from_blind_operation::validate()const
{
FC_ASSERT( amount.amount > 0 );
FC_ASSERT( fee.amount >= 0 );
FC_ASSERT( inputs.size() > 0 );
FC_ASSERT( amount.asset_id == fee.asset_id );
vector<commitment_type> in(inputs.size());
vector<commitment_type> out;
int64_t net_public = fee.amount.value + amount.amount.value;
out.push_back( fc::ecc::blind( blinding_factor, net_public ) );
for( uint32_t i = 0; i < in.size(); ++i )
{
in[i] = inputs[i].commitment;
/// by requiring all inputs to be sorted we also prevent duplicate commitments on the input
if( i > 0 ) FC_ASSERT( in[i-1] < in[i], "all inputs must be sorted by commitment id" );
}
FC_ASSERT( in.size(), "there must be at least one input" );
FC_ASSERT( fc::ecc::verify_sum( in, out, 0 ) );
}
/**
* If fee_payer = temp_account_id, then the fee is paid by the surplus balance of inputs-outputs and
* 100% of the fee goes to the network.
*/
account_id_type blind_transfer_operation::fee_payer()const
{
return GRAPHENE_TEMP_ACCOUNT;
}
/**
* This method can be computationally intensive because it verifies that input commitments - output commitments add up to 0
*/
void blind_transfer_operation::validate()const
{ try {
vector<commitment_type> in(inputs.size());
vector<commitment_type> out(outputs.size());
int64_t net_public = fee.amount.value;//from_amount.value - to_amount.value;
for( uint32_t i = 0; i < in.size(); ++i )
{
in[i] = inputs[i].commitment;
/// by requiring all inputs to be sorted we also prevent duplicate commitments on the input
if( i > 0 ) FC_ASSERT( in[i-1] < in[i] );
}
for( uint32_t i = 0; i < out.size(); ++i )
{
out[i] = outputs[i].commitment;
if( i > 0 ) FC_ASSERT( out[i-1] < out[i] );
FC_ASSERT( !outputs[i].owner.is_impossible() );
}
FC_ASSERT( in.size(), "there must be at least one input" );
FC_ASSERT( fc::ecc::verify_sum( in, out, net_public ), "", ("net_public", net_public) );
if( outputs.size() > 1 )
{
for( auto out : outputs )
{
auto info = fc::ecc::range_get_info( out.range_proof );
FC_ASSERT( info.max_value <= GRAPHENE_MAX_SHARE_SUPPLY );
}
}
} FC_CAPTURE_AND_RETHROW( (*this) ) }
share_type blind_transfer_operation::calculate_fee( const fee_parameters_type& k )const
{
return k.fee + outputs.size() * k.price_per_output;
}
/**
* Packs *this then encodes as base58 encoded string.
*/
stealth_confirmation::operator string()const
{
return fc::to_base58( fc::raw::pack( *this ) );
}
/**
* Unpacks from a base58 string
*/
stealth_confirmation::stealth_confirmation( const std::string& base58 )
{
*this = fc::raw::unpack<stealth_confirmation>( fc::from_base58( base58 ) );
}
} } // graphene::chain

Some files were not shown because too many files have changed in this diff Show more