Merge branch 'github_master'
Conflicts: libraries/fc
This commit is contained in:
commit
9c4a54edce
271 changed files with 18481 additions and 5676 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -6,6 +6,8 @@ CMakeCache.txt
|
|||
CMakeFiles
|
||||
Makefile
|
||||
compile_commands.json
|
||||
moc_*
|
||||
*.moc
|
||||
|
||||
libraries/utilities/git_revision.cpp
|
||||
|
||||
|
|
|
|||
59
README.md
59
README.md
|
|
@ -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
112
Vagrantfile
vendored
Normal 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
|
||||
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
|||
1531
libraries/app/database_api.cpp
Normal file
1531
libraries/app/database_api.cpp
Normal file
File diff suppressed because it is too large
Load diff
208
libraries/app/impacted.cpp
Normal file
208
libraries/app/impacted.cpp
Normal 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 );
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
549
libraries/app/include/graphene/app/database_api.hpp
Normal file
549
libraries/app/include/graphene/app/database_api.hpp
Normal 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)
|
||||
)
|
||||
41
libraries/app/include/graphene/app/full_account.hpp
Normal file
41
libraries/app/include/graphene/app/full_account.hpp
Normal 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)
|
||||
)
|
||||
36
libraries/app/include/graphene/app/impacted.hpp
Normal file
36
libraries/app/include/graphene/app/impacted.hpp
Normal 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
|
||||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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) ||
|
||||
|
|
|
|||
|
|
@ -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 );
|
||||
|
|
|
|||
|
|
@ -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>();
|
||||
}
|
||||
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
137
libraries/chain/confidential_evaluator.cpp
Normal file
137
libraries/chain/confidential_evaluator.cpp
Normal 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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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 ));
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -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() }
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>();
|
||||
|
|
|
|||
|
|
@ -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] );
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
15
libraries/chain/genesis_state.cpp
Normal file
15
libraries/chain/genesis_state.cpp
Normal 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
|
||||
109
libraries/chain/get_config.cpp
Normal file
109
libraries/chain/get_config.cpp
Normal 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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) )
|
||||
|
|
|
|||
|
|
@ -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) )
|
||||
|
|
@ -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))
|
||||
///@}
|
||||
|
|
|
|||
|
|
@ -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) ) }
|
||||
|
||||
} }
|
||||
|
|
|
|||
144
libraries/chain/include/graphene/chain/db_with.hpp
Normal file
144
libraries/chain/include/graphene/chain/db_with.hpp
Normal 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
|
||||
|
|
@ -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) );
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
26
libraries/chain/include/graphene/chain/get_config.hpp
Normal file
26
libraries/chain/include/graphene/chain/get_config.hpp
Normal 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
|
||||
|
|
@ -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)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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) )
|
||||
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
||||
|
|
|
|||
|
|
@ -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) );
|
||||
|
||||
|
|
|
|||
|
|
@ -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) )
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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, )
|
||||
|
|
|
|||
|
|
@ -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) )
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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) );
|
||||
|
|
|
|||
264
libraries/chain/include/graphene/chain/protocol/confidential.hpp
Normal file
264
libraries/chain/include/graphene/chain/protocol/confidential.hpp
Normal 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) )
|
||||
|
|
@ -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) )
|
||||
|
|
|
|||
|
|
@ -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) )
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) )
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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) )
|
||||
|
||||
|
|
|
|||
|
|
@ -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) )
|
||||
|
|
|
|||
|
|
@ -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) )
|
||||
|
||||
|
|
|
|||
|
|
@ -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) )
|
||||
|
|
|
|||
|
|
@ -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) )
|
||||
|
||||
|
|
|
|||
147
libraries/chain/include/graphene/chain/protocol/vote.hpp
Normal file
147
libraries/chain/include/graphene/chain/protocol/vote.hpp
Normal 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) )
|
||||
|
|
@ -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) )
|
||||
|
||||
|
|
|
|||
|
|
@ -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) )
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) )
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
} }
|
||||
|
|
@ -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;
|
||||
} ;
|
||||
|
||||
} }
|
||||
|
|
@ -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)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) ) }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
32
libraries/chain/protocol/authority.cpp
Normal file
32
libraries/chain/protocol/authority.cpp
Normal 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
|
||||
|
|
@ -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] );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
140
libraries/chain/protocol/confidential.cpp
Normal file
140
libraries/chain/protocol/confidential.cpp
Normal 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
Loading…
Reference in a new issue