Merge tag '0.1.8'

This commit is contained in:
Peter Conrad 2018-09-03 17:16:34 +02:00
commit 0bf8a9d73a
29 changed files with 217083 additions and 9526 deletions

370
README.md
View file

@ -1,240 +1,210 @@
Intro for new developers
Intro for new developers and witnesses
------------------------
This is a quick introduction to get new developers up to speed on Graphene.
This is a quick introduction to get new developers and witnesses up to speed on Peerplays blockchain. It is intended for witnesses plannig to join a live, already deployed blockchain.
Starting Graphene
Starting A Peerplays Node
-----------------
For Ubuntu 14.04 LTS users, see this link first:
For Ubuntu 14.04 LTS and up users, see this link first:
https://github.com/cryptonomex/graphene/wiki/build-ubuntu
and then proceed with:
git clone https://github.com/cryptonomex/graphene.git
cd graphene
git clone https://github.com/pbsa/peerplays.git
cd peerplays
git submodule update --init --recursive
cmake -DCMAKE_BUILD_TYPE=Debug .
cmake -DBOOST_ROOT="$BOOST_ROOT" -DCMAKE_BUILD_TYPE=Release .
make
./programs/witness_node/witness_node
Launching the witness creates required directories. Next, **stop the witness** and continue.
This will launch the witness node. If you would like to launch the command-line wallet, you must first specify a port
for communication with the witness node. To do this, add text to `witness_node_data_dir/config.ini` as follows, then
restart the node:
vi witness_node_data_dir/config.ini
p2p-endpoint = 0.0.0.0:9777
rpc-endpoint = 127.0.0.1:8090
seed-node = 213.184.225.234:59500
Start the witness back up
./programs/witness_node/witness_node
Then, in a separate terminal window, start the command-line wallet `cli_wallet`:
./programs/cli_wallet/cli_wallet
To set your iniital password to 'password' use:
To set your initial password to 'password' use:
>>> set_password password
>>> unlock password
To import your initial balance:
>>> import_balance nathan [5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3] true
If you send private keys over this connection, `rpc-endpoint` should be bound to localhost for security.
A list of CLI wallet commands is available
[here](https://github.com/cryptonomex/graphene/blob/master/libraries/wallet/include/graphene/wallet/wallet.hpp).
[here](https://github.com/PBSA/peerplays/blob/master/libraries/wallet/include/graphene/wallet/wallet.hpp).
Code coverage testing
---------------------
Check how much code is covered by unit tests, using gcov/lcov (see http://ltp.sourceforge.net/coverage/lcov.php ).
Testnet
----------------------
- chain-id - 5b37954aa0d33a8e0d57b084995c262a7c13dbc0693d3e96654e63ff45a9ceec
cmake -D ENABLE_COVERAGE_TESTING=true -D CMAKE_BUILD_TYPE=Debug .
make
lcov --capture --initial --directory . --output-file base.info --no-external
libraries/fc/bloom_test
libraries/fc/task_cancel_test
libraries/fc/api
libraries/fc/blind
libraries/fc/ecc_test test
libraries/fc/real128_test
libraries/fc/lzma_test README.md
libraries/fc/ntp_test
tests/intense_test
tests/app_test
tests/chain_bench
tests/chain_test
tests/performance_test
lcov --capture --directory . --output-file test.info --no-external
lcov --add-tracefile base.info --add-tracefile test.info --output-file total.info
lcov -o interesting.info -r total.info libraries/fc/vendor/\* libraries/fc/tests/\* tests/\*
mkdir -p lcov
genhtml interesting.info --output-directory lcov --prefix `pwd`
Register your username at the faucet address
---------------------------
https://595-dev.pixelplex.by/
Now open `lcov/index.html` in a browser.
Unit testing
------------
We use the Boost unit test framework for unit testing. Most unit
tests reside in the `chain_test` build target.
Witness node
------------
The role of the witness node is to broadcast transactions, download blocks, and optionally sign them.
Use the get_private_key_from_password command
---------------------------------
You will to generate owner and active keys
```
./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"'
get_private_key_from_password your_witness_username active the_key_you_received_from_the_faucet
```
This will reveal an array for your active key `["PPYxxx", "xxxx"]`
import_keys into your cli_wallet
-------------------------------
- use the second value in the array returned from the previous step for the private key
- be sure to wrap your username in quotes
- import the key with this command
```
import_key "your_witness_username" xxxx
```
Upgrade your account to lifetime membership
--------------------------------
```
upgrade_account your_witness_username true
```
Create your witness (substitute the url for your witness information)
-------------------------------
- place quotes around url
```
create_witness your_witness_username "url" true
```
**Be sure to take note of the block_signing_key**
IMPORTANT (issue below command using block_signing_key just obtained)
```
get_private_key block_signing_key
```
Compare this result to
```
dump_private_keys
```
You should see 3 pairs of keys. One of the pairs should match your block_signing_key and this is the one you will use in the next step!
Get your witness id
-----------------
```
get_witness username (note the "id" for your config)
```
Modify your witness_node config.ini to include **your** witness id and private key pair.
-------------------------
Comment out the existing private-key before adding yours
```
vim witness_node_data_dir/config.ini
witness-id = "1.6.x"
private-key = ["block_signing_key","private_key_for_your_block_signing_key"]
```
start your witness back up
------------------
```
./programs/witness_node/witness_node
```
If it fails to start, try with these flags (not for permanent use)
```
./programs/witness_node/witness_node --resync --replay
```
Vote for yourself
--------------
```
vote_for_witness your_witness_account your_witness_account true true
```
Ask to be voted in!
--------------
Join @Peerplays Telegram group to find information about the witness group.
http://t.me/@peerplayswitness
You will get logs that look like this:
```
2070264ms th_a application.cpp:506 handle_block ] Got block: #87913 time: 2017-05-27T16:34:30 latency: 264 ms from: bhuz-witness irreversible: 87903 (-10)
2071000ms th_a witness.cpp:204 block_production_loo ] Not producing block because slot has not yet arrived
2072000ms th_a witness.cpp:204 block_production_loo ] Not producing block because slot has not yet arrived
2073000ms th_a witness.cpp:201 block_production_loo ] Not producing block because it isn't my turn
```
Assuming you've received votes, you will start producing as a witness at the next maintenance interval (once per hour). You can check your votes with.
```
get_witness your_witness_account
```
systemd
----------------
It's important for your witness to start when your system boots up. The filepaths here assume that you installed your witness into `/home/ubuntu/peerplays`
Create a logfile to hold your stdout/err logging
```bash
sudo touch /var/log/peerplays.log
```
Save this file in your peerplays directory. `vi /home/ubuntu/peerplays/start.sh`
```bash
#!/bin/bash
cd /home/ubuntu/peerplays
./programs/witness_node/witness_node &> /var/log/peerplays.log
```
Make it executable
```bash
chmod 744 /home/ubuntu/peerplays/start.sh
```
Create this file: `sudo vi /etc/systemd/system/peerplays.service`
Note the path for start.sh. Change it to match where your start.sh file is if necessary.
```
[Unit]
Description=Peerplays Witness
After=network.target
[Service]
ExecStart=/home/ubuntu/peerplays/start.sh
[Install]
WantedBy = multi-user.target
```
Enable the service
```bash
sudo systemctl enable peerplays.service
```
Make sure you don't get any errors
```bash
sudo systemctl status peerplays.service
```
Stop your witness if it is currently running from previous steps, then start it with the service.
```bash
sudo systemctl start peerplays.service
```
Check your logfile for entries
```bash
tail -f /var/log/peerplays.log
```
Running specific tests
----------------------
- `tests/chain_tests -t block_tests/name_of_test`
Using the API
-------------
We provide several different API's. Each API has its own ID.
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.
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":[],"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":[],"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:
$ curl --data '{"jsonrpc": "2.0", "method": "get_accounts", "params": [["1.2.0"]], "id": 1}' http://127.0.0.1:8090/rpc
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, while allowing any other user to access the three public API's
necessary to use the wallet:
{
"permission_map" :
[
[
"bytemaster",
{
"password_hash_b64" : "9e9GF7ooXVb9k4BoSfNIPTelXeGOZ5DrgOYMj94elaY=",
"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"]
}
]
]
}
Passwords are stored in `base64` as as salted `sha256` hashes. A simple Python script, `saltpass.py` is avaliable to obtain hash and salt values from a password.
A single asterisk `"*"` may be specified as username or password hash to accept any value.
With the above configuration, here is an example of how to call `add_node` from the `network_node` API:
{"id":1, "method":"call", "params":[1,"login",["bytemaster", "supersecret"]]}
{"id":2, "method":"call", "params":[1,"network_node",[]]}
{"id":3, "method":"call", "params":[2,"add_node",["127.0.0.1:9090"]]}
Note, the call to `network_node` is necessary to obtain the correct API identifier for the network API. It is not guaranteed that the network API identifier will always be `2`.
Since the `network_node` API requires login, it is only accessible over the websocket RPC. Our `doxygen` documentation contains the most up-to-date information
about API's for the [witness node](https://bitshares.github.io/doxygen/namespacegraphene_1_1app.html) and the
[wallet](https://bitshares.github.io/doxygen/classgraphene_1_1wallet_1_1wallet__api.html).
If you want information which is not available from an API, it might be available
from the [database](https://bitshares.github.io/doxygen/classgraphene_1_1chain_1_1database.html);
it is fairly simple to write API methods to expose database methods.
Running private testnet
-----------------------
See the [documentation](https://github.com/cryptonomex/graphene/wiki/private-testnet) if you want to run a private testnet.
Questions
---------
- Is there a way to generate help with parameter names and method descriptions?
Yes. Documentation of the code base, including APIs, can be generated using Doxygen. Simply run `doxygen` in this directory.
If both Doxygen and perl are available in your build environment, the CLI wallet's `help` and `gethelp`
commands will display help generated from the doxygen documentation.
If your CLI wallet's `help` command displays descriptions without parameter names like
`signed_transaction transfer(string, string, string, string, string, bool)`
it means CMake was unable to find Doxygen or perl during configuration. If found, the
output should look like this:
`signed_transaction transfer(string from, string to, string amount, string asset_symbol, string memo, bool broadcast)`
- Is there a way to allow external program to drive `cli_wallet` via websocket, JSONRPC, or HTTP?
Yes. External programs may connect to the CLI wallet and make its calls over a websockets API. To do this, run the wallet in
server mode, i.e. `cli_wallet -s "127.0.0.1:9999"` and then have the external program connect to it over the specified port
(in this example, port 9999).
- Is there a way to access methods which require login over HTTP?
No. Login is inherently a stateful process (logging in changes what the server will do for certain requests, that's kind
of the point of having it). If you need to track state across HTTP RPC calls, you must maintain a session across multiple
connections. This is a famous source of security vulnerabilities for HTTP applications. Additionally, HTTP is not really
designed for "server push" notifications, and we would have to figure out a way to queue notifications for a polling client.
Websockets solves all these problems. If you need to access Graphene's stateful methods, you need to use Websockets.
- What is the meaning of `a.b.c` numbers?
The first number specifies the *space*. Space 1 is for protocol objects, 2 is for implementation objects.
Protocol space objects can appear on the wire, for example in the binary form of transactions.
Implementation space objects cannot appear on the wire and solely exist for implementation
purposes, such as optimization or internal bookkeeping.
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/protocol/types.hpp).
The third number specifies the *instance*. The instance of the object is different for each individual
object.
- The answer to the previous question was really confusing. Can you make it clearer?
All account ID's are of the form `1.2.x`. If you were the 9735th account to be registered,
your account's ID will be `1.2.9735`. Account `0` is special (it's the "committee account,"
which is controlled by the committee members and has a few abilities and restrictions other accounts
do not).
All asset ID's are of the form `1.3.x`. If you were the 29th asset to be registered,
your asset's ID will be `1.3.29`. Asset `0` is special (it's BTS, which is considered the "core asset").
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.

216118
genesis.json

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -125,12 +125,17 @@ namespace detail {
auto seeds = _options->at("seed-node").as<vector<string>>();
for( const string& endpoint_string : seeds )
{
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);
try {
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);
}
} catch( const fc::exception& e ) {
wlog( "caught exception ${e} while adding seed node ${endpoint}",
("e", e.to_detail_string())("endpoint", endpoint_string) );
}
}
}
@ -141,26 +146,53 @@ namespace detail {
auto seeds = fc::json::from_string(seeds_str).as<vector<string>>();
for( const string& endpoint_string : seeds )
{
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);
try {
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);
}
} catch( const fc::exception& e ) {
wlog( "caught exception ${e} while adding seed node ${endpoint}",
("e", e.to_detail_string())("endpoint", endpoint_string) );
}
}
}
else
{
// t.me/peerplays #seednodes
vector<string> seeds = {
"peerplays.blocktrades.info:2776"
"seed.ppy.blckchnd.com:6112", // blckchnd
"ppy.esteem.ws:7777", // good-karma
"peerplays.bitcoiner.me:9777", // bitcoiner
"peerplays.roelandp.nl:9777", // roelandp
"78.46.95.153:7777", // theprophet0
"ppyseed.bacchist.me:42420", // bacchist-witness
"5.9.18.213:18828", // pfunk
"31.171.244.121:7777", // taconator
"seed.peerplaysdb.com:9777", // jesta
"ppy-seed.xeldal.com:19777", // xeldal
"peerplays-seed.altcap.io:61388", // winner.winner.chicken.dinner
"seed.peerplaysnodes.com:9777", // wackou
"peerplays-seed.privex.io:7777", // someguy123/privex
"51.15.78.16:9777", // agoric.systems
"212.71.253.163:9777", // xtar
"51.15.35.96:9777", // lafona
"anyx.ca:9777" // anyx
};
for( const string& endpoint_string : seeds )
{
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);
try {
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);
}
} catch( const fc::exception& e ) {
wlog( "caught exception ${e} while adding seed node ${endpoint}",
("e", e.to_detail_string())("endpoint", endpoint_string) );
}
}
}
@ -801,6 +833,9 @@ namespace detail {
if (high_block_num == 0)
return synopsis; // we have no blocks
}
if( low_block_num == 0)
low_block_num = 1;
// at this point:
// low_block_num is the block before the first block we can undo,

View file

@ -1803,7 +1803,7 @@ vector<tournament_object> database_api_impl::get_tournaments(tournament_id_type
{
vector<tournament_object> result;
const auto& tournament_idx = _db.get_index_type<tournament_index>().indices().get<by_id>();
for ( auto elem : boost::make_iterator_range(tournament_idx.rbegin(), tournament_idx.rend())) {
for (auto elem: tournament_idx) {
if( result.size() >= limit ) break;
if( ( (elem.get_id().instance.value <= start.instance.value) || start == tournament_id_type()) &&
( (elem.get_id().instance.value >= stop.instance.value) || stop == tournament_id_type()))
@ -1829,7 +1829,7 @@ vector<tournament_object> database_api_impl::get_tournaments_by_state(tournament
{
vector<tournament_object> result;
const auto& tournament_idx = _db.get_index_type<tournament_index>().indices().get<by_id>();
for ( auto elem : boost::make_iterator_range(tournament_idx.rbegin(), tournament_idx.rend())) {
for (auto elem: tournament_idx) {
if( result.size() >= limit ) break;
if( ( (elem.get_id().instance.value <= start.instance.value) || start == tournament_id_type()) &&
( (elem.get_id().instance.value >= stop.instance.value) || stop == tournament_id_type()) &&

View file

@ -524,7 +524,7 @@ void database::_apply_block( const signed_block& next_block )
* for transactions when validating broadcast transactions or
* when building a block.
*/
apply_transaction( trx, skip | skip_transaction_signatures );
apply_transaction( trx, skip );
++_current_trx_in_block;
}
@ -687,8 +687,9 @@ const witness_object& database::validate_block_header( uint32_t skip, const sign
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)("null_secret_hash", secret_hash_type::hash( secret_hash_type())));
//DLN: TODO: Temporarily commented out to test shuffle vs RNG scheduling algorithm for witnesses, this was causing shuffle agorithm to fail during create_witness test. This should be re-enabled for RNG, and maybe for shuffle too, don't really know for sure.
// 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)("null_secret_hash", secret_hash_type::hash( secret_hash_type())));
if( !(skip&skip_witness_signature) )
FC_ASSERT( next_block.validate_signee( witness.signing_key ) );

View file

@ -641,12 +641,44 @@ void database::init_genesis(const genesis_state_type& genesis_state)
}
// Create balances for all bts accounts
for( const auto& account : genesis_state.initial_bts_accounts )
if (account.core_balance != share_type())
for( const auto& account : genesis_state.initial_bts_accounts ) {
if (account.core_balance != share_type()) {
total_supplies[asset_id_type()] += account.core_balance;
create<account_balance_object>([&](account_balance_object& b) {
b.owner = get_account_id(account.name);
b.balance = account.core_balance;
});
}
// create any vesting balances for this account
if (account.vesting_balances)
for (const auto& vesting_balance : *account.vesting_balances) {
create<vesting_balance_object>([&](vesting_balance_object& vbo) {
vbo.owner = get_account_id(account.name);
vbo.balance = asset(vesting_balance.amount, get_asset_id(vesting_balance.asset_symbol));
if (vesting_balance.policy_type == "linear") {
auto initial_linear_vesting_policy = vesting_balance.policy.as<genesis_state_type::initial_bts_account_type::initial_linear_vesting_policy>();
linear_vesting_policy new_vesting_policy;
new_vesting_policy.begin_timestamp = initial_linear_vesting_policy.begin_timestamp;
new_vesting_policy.vesting_cliff_seconds = initial_linear_vesting_policy.vesting_cliff_seconds;
new_vesting_policy.vesting_duration_seconds = initial_linear_vesting_policy.vesting_duration_seconds;
new_vesting_policy.begin_balance = initial_linear_vesting_policy.begin_balance;
vbo.policy = new_vesting_policy;
} else if (vesting_balance.policy_type == "cdd") {
auto initial_cdd_vesting_policy = vesting_balance.policy.as<genesis_state_type::initial_bts_account_type::initial_cdd_vesting_policy>();
cdd_vesting_policy new_vesting_policy;
new_vesting_policy.vesting_seconds = initial_cdd_vesting_policy.vesting_seconds;
new_vesting_policy.coin_seconds_earned = initial_cdd_vesting_policy.coin_seconds_earned;
new_vesting_policy.start_claim = initial_cdd_vesting_policy.start_claim;
new_vesting_policy.coin_seconds_earned_last_update = initial_cdd_vesting_policy.coin_seconds_earned_last_update;
vbo.policy = new_vesting_policy;
}
total_supplies[get_asset_id(vesting_balance.asset_symbol)] += vesting_balance.amount;
});
}
}
// Create initial balances
share_type total_allocation;
@ -671,7 +703,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
linear_vesting_policy policy;
policy.begin_timestamp = vest.begin_timestamp;
policy.vesting_cliff_seconds = 0;
policy.vesting_cliff_seconds = vest.vesting_cliff_seconds ? *vest.vesting_cliff_seconds : 0;
policy.vesting_duration_seconds = vest.vesting_duration_seconds;
policy.begin_balance = vest.begin_balance;
@ -722,7 +754,10 @@ void database::init_genesis(const genesis_state_type& genesis_state)
}
++it;
}
// @romek
#if 0
FC_ASSERT( !has_imbalanced_assets );
#endif
// Save tallied supplies
for( const auto& item : total_supplies )

View file

@ -377,7 +377,6 @@ void database::initialize_budget_record( fc::time_point_sec now, budget_record&
*/
void database::process_budget()
{
return;
try
{
const global_property_object& gpo = get_global_properties();
@ -414,9 +413,9 @@ void database::process_budget()
rec.witness_budget = witness_budget;
available_funds -= witness_budget;
fc::uint128_t worker_budget_u128 = uint64_t(1); //DLN Q&D HACK gpo.parameters.worker_budget_per_day.value;
//DLN worker_budget_u128 *= uint64_t(time_to_maint);
//DLN worker_budget_u128 /= 60*60*24;
fc::uint128_t worker_budget_u128 = gpo.parameters.worker_budget_per_day.value;
worker_budget_u128 *= uint64_t(time_to_maint);
worker_budget_u128 /= 60*60*24;
share_type worker_budget;
if( worker_budget_u128 >= available_funds.value )
@ -768,9 +767,9 @@ void schedule_pending_dividend_balances(database& db,
for (const vesting_balance_object& vesting_balance_obj : boost::make_iterator_range(vesting_balances_begin, vesting_balances_end))
{
vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount;
dlog("Vesting balance for account: ${owner}, amount: ${amount}",
("owner", vesting_balance_obj.owner(db).name)
("amount", vesting_balance_obj.balance.amount));
//dlog("Vesting balance for account: ${owner}, amount: ${amount}",
// ("owner", vesting_balance_obj.owner(db).name)
// ("amount", vesting_balance_obj.balance.amount));
}
auto current_distribution_account_balance_iter = current_distribution_account_balance_range.first;
@ -1233,9 +1232,9 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
for (const vesting_balance_object& vesting_balance_obj : boost::make_iterator_range(vesting_balances_begin, vesting_balances_end))
{
vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount;
dlog("Vesting balance for account: ${owner}, amount: ${amount}",
("owner", vesting_balance_obj.owner(d).name)
("amount", vesting_balance_obj.balance.amount));
//dlog("Vesting balance for account: ${owner}, amount: ${amount}",
// ("owner", vesting_balance_obj.owner(d).name)
// ("amount", vesting_balance_obj.balance.amount));
}
}

View file

@ -55,8 +55,9 @@ void database::update_global_dynamic_data( const signed_block& b )
#else
assert( missed_blocks != 0 );
#endif
if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM)
{
// bad if-condition, this code needs to execute for both shuffled and rng algorithms
// if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM)
// {
missed_blocks--;
for( uint32_t i = 0; i < missed_blocks; ++i ) {
const auto& witness_missed = get_scheduled_witness( i+1 )(*this);
@ -72,7 +73,7 @@ void database::update_global_dynamic_data( const signed_block& b )
});
}
}
}
// }
#ifdef DIRTY_TRICK
}
#endif

View file

@ -23,7 +23,7 @@
*/
#pragma once
#define GRAPHENE_SYMBOL "PPY2T"
#define GRAPHENE_SYMBOL "PPY"
#define GRAPHENE_ADDRESS_PREFIX "PPY"
#define GRAPHENE_MIN_ACCOUNT_NAME_LENGTH 1

View file

@ -59,6 +59,24 @@ struct genesis_state_type {
flat_map<public_key_type, weight_type> key_auths;
flat_map<address, weight_type> address_auths;
};
struct initial_cdd_vesting_policy {
uint32_t vesting_seconds;
fc::uint128_t coin_seconds_earned;
fc::time_point_sec start_claim;
fc::time_point_sec coin_seconds_earned_last_update;
};
struct initial_linear_vesting_policy {
fc::time_point_sec begin_timestamp;
uint32_t vesting_cliff_seconds;
uint32_t vesting_duration_seconds;
share_type begin_balance;
};
struct initial_vesting_balance {
string asset_symbol;
share_type amount;
std::string policy_type; // either "linear" or "cdd"
fc::variant policy; // either an initial_cdd_vesting_policy or initial_linear_vesting_policy
};
initial_bts_account_type(const string& name = string(),
const initial_authority& owner_authority = initial_authority(),
const initial_authority& active_authority = initial_authority(),
@ -72,6 +90,7 @@ struct genesis_state_type {
initial_authority owner_authority;
initial_authority active_authority;
share_type core_balance;
fc::optional<std::vector<initial_vesting_balance> > vesting_balances;
};
struct initial_asset_type {
struct initial_collateral_position {
@ -102,6 +121,7 @@ struct genesis_state_type {
string asset_symbol;
share_type amount;
time_point_sec begin_timestamp;
fc::optional<uint32_t> vesting_cliff_seconds;
uint32_t vesting_duration_seconds = 0;
share_type begin_balance;
};
@ -161,7 +181,7 @@ FC_REFLECT(graphene::chain::genesis_state_type::initial_balance_type,
(owner)(asset_symbol)(amount))
FC_REFLECT(graphene::chain::genesis_state_type::initial_vesting_balance_type,
(owner)(asset_symbol)(amount)(begin_timestamp)(vesting_duration_seconds)(begin_balance))
(owner)(asset_symbol)(amount)(begin_timestamp)(vesting_cliff_seconds)(vesting_duration_seconds)(begin_balance))
FC_REFLECT(graphene::chain::genesis_state_type::initial_witness_type, (owner_name)(block_signing_key))
@ -174,12 +194,27 @@ FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initia
(account_auths)
(key_auths)
(address_auths))
FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_cdd_vesting_policy,
(vesting_seconds)
(coin_seconds_earned)
(start_claim)
(coin_seconds_earned_last_update))
FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_linear_vesting_policy,
(begin_timestamp)
(vesting_cliff_seconds)
(vesting_duration_seconds)
(begin_balance))
FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_vesting_balance,
(asset_symbol)
(amount)
(policy_type)
(policy))
FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type,
(name)
(owner_authority)
(active_authority)
(core_balance))
(core_balance)
(vesting_balances))
FC_REFLECT(graphene::chain::genesis_state_type,
(initial_timestamp)(max_core_supply)(initial_parameters)(initial_bts_accounts)(initial_accounts)(initial_assets)(initial_balances)

View file

@ -68,6 +68,8 @@ namespace graphene { namespace chain {
optional< string > new_url;
/// The new block signing key.
optional< public_key_type > new_signing_key;
/// The new secreat hash.
optional<secret_hash_type> new_initial_secret;
account_id_type fee_payer()const { return witness_account; }
void validate()const;
@ -81,4 +83,4 @@ FC_REFLECT( graphene::chain::witness_create_operation::fee_parameters_type, (fee
FC_REFLECT( graphene::chain::witness_create_operation, (fee)(witness_account)(url)(block_signing_key)(initial_secret) )
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) )
FC_REFLECT( graphene::chain::witness_update_operation, (fee)(witness)(witness_account)(new_url)(new_signing_key)(new_initial_secret) )

View file

@ -181,7 +181,7 @@ namespace graphene { namespace chain {
ordered_non_unique< tag<by_account>,
member<vesting_balance_object, account_id_type, &vesting_balance_object::owner>
>,
ordered_unique< tag<by_asset_balance>,
ordered_non_unique< tag<by_asset_balance>,
composite_key<
vesting_balance_object,
member_offset<vesting_balance_object, asset_id_type, (size_t) (offset_s(vesting_balance_object,balance) + offset_s(asset,asset_id))>,

View file

@ -70,6 +70,8 @@ void_result witness_update_evaluator::do_apply( const witness_update_operation&
wit.url = *op.new_url;
if( op.new_signing_key.valid() )
wit.signing_key = *op.new_signing_key;
if( op.new_initial_secret.valid() )
wit.next_secret_hash = *op.new_initial_secret;
});
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }

View file

@ -166,7 +166,15 @@ struct egenesis_info
else if( genesis_json.valid() )
{
// If genesis not exist, generate from genesis_json
genesis = fc::json::from_string( *genesis_json ).as< genesis_state_type >();
try
{
genesis = fc::json::from_string( *genesis_json ).as< genesis_state_type >();
}
catch (const fc::exception& e)
{
edump((e));
throw;
}
}
else
{

View file

@ -3,4 +3,5 @@ add_subdirectory( account_history )
add_subdirectory( market_history )
add_subdirectory( delayed_node )
add_subdirectory( generate_genesis )
add_subdirectory( generate_uia_sharedrop_genesis )
add_subdirectory( debug_witness )

View file

@ -33,6 +33,7 @@
#include <fc/thread/thread.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <iostream>
#include <fstream>
@ -75,9 +76,10 @@ void generate_genesis_plugin::plugin_initialize(const boost::program_options::va
void generate_genesis_plugin::plugin_startup()
{ try {
chain::database& d = database();
ilog("generate genesis plugin: plugin_startup() begin");
if (_block_to_snapshot)
{
chain::database& d = database();
if (d.head_block_num() == *_block_to_snapshot)
{
ilog("generate genesis plugin: already at snapshot block");
@ -89,6 +91,10 @@ void generate_genesis_plugin::plugin_startup()
elog("generate genesis plugin: waiting for block ${snapshot_block} to generate snapshot, current head is ${head}",
("snapshot_block", _block_to_snapshot)("head", d.head_block_num()));
}
else
ilog("generate genesis plugin: no snapshot block number provided, plugin is disabled");
ilog("generate genesis plugin: plugin_startup() end");
} FC_CAPTURE_AND_RETHROW() }
void generate_genesis_plugin::block_applied(const graphene::chain::signed_block& b)
@ -110,145 +116,245 @@ bool is_special_account(const graphene::chain::account_id_type& account_id)
return account_id.instance < 100;
}
bool is_exchange(const std::string& account_name)
{
return account_name == "poloniexcoldstorage" ||
account_name == "btc38-public-for-bts-cold" ||
account_name == "poloniexwallet" ||
account_name == "btercom" ||
account_name == "yunbi-cold-wallet" ||
account_name == "btc38-btsx-octo-72722" ||
account_name == "bittrex-deposit" ||
account_name == "btc38btsxwithdrawal";
}
bool is_scam(const std::string& account_name)
{
static std::set<std::string> scam_accounts{
"polonie-wallet",
"polonie-xwallet",
"poloniewallet",
"poloniex-deposit",
"poloniex-wallet",
"poloniexwall-et",
"poloniexwallett",
"poloniexwall-t",
"poloniexwalle",
"poloniex",
"poloneix",
"poloniex1",
"bittrex-deopsit",
"bittrex-deposi",
"bittrex-depositt",
"bittrex-dposit",
"bittrex",
"bittrex-deposits",
"coinbase",
"blocktrade",
"locktrades",
"yun.bts",
"transwiser-walle",
"transwiser-wallets",
"ranswiser-wallet",
"yun.btc",
"pay.coinbase.com",
"pay.bts.com",
"btc38.com",
"yunbi.com",
"coinbase.com",
"ripple.com"
};
return scam_accounts.find(account_name) != scam_accounts.end();
}
bool is_exchange(const std::string& account_name)
{
static std::set<std::string> exchange_accounts{
"poloniexcoldstorage",
"btc38-public-for-bts-cold",
"poloniexwallet",
"btercom",
"yunbi-cold-wallet",
"btc38-btsx-octo-72722",
"bittrex-deposit",
"btc38btsxwithdrawal"
};
return exchange_accounts.find(account_name) != exchange_accounts.end();
}
bool exclude_account_from_sharedrop(graphene::chain::database& d, const graphene::chain::account_id_type& account_id)
{
if (is_special_account(account_id))
return true;
const std::string& account_name = account_id(d).name;
return is_exchange(account_name) || is_scam(account_name);
}
void generate_genesis_plugin::generate_snapshot()
{
{ try {
ilog("generate genesis plugin: generating snapshot now");
graphene::chain::genesis_state_type new_genesis_state;
chain::database& d = database();
// we'll distribute 5% of 1,000,000 tokens, so:
graphene::chain::share_type total_amount_to_distribute = 50000 * GRAPHENE_BLOCKCHAIN_PRECISION;
// we'll distribute 5% of (some amount of tokens), so:
graphene::chain::share_type total_amount_to_distribute(27302662972);
my_account_balance_object_index_type db_balances;
graphene::chain::share_type total_bts_balance;
// we need collection of mutable objects
std::vector<my_account_balance_object> db_balances;
// copy const objects to our collection
auto& balance_index = d.get_index_type<graphene::chain::account_balance_index>().indices().get<graphene::chain::by_asset_balance>();
for (auto balance_iter = balance_index.begin(); balance_iter != balance_index.end() && balance_iter->asset_type == graphene::chain::asset_id_type(); ++balance_iter)
if (!is_special_account(balance_iter->owner) && !is_exchange(balance_iter->owner(d).name))
{
if (balance_iter->balance > 0 && !exclude_account_from_sharedrop(d, balance_iter->owner))
{
// it is possible due to constructor
db_balances.emplace_back(*balance_iter);
total_bts_balance += balance_iter->balance;
my_account_balance_object new_balance_object;
new_balance_object.account_id = balance_iter->owner;
new_balance_object.balance = balance_iter->balance;
db_balances.insert(new_balance_object);
}
}
// account for BTS tied up in market orders
auto limit_order_index = d.get_index_type<graphene::chain::limit_order_index>().indices();
for (const graphene::chain::limit_order_object& limit_order : limit_order_index)
if (limit_order.amount_for_sale().asset_id == graphene::chain::asset_id_type())
{
graphene::chain::share_type limit_order_amount = limit_order.amount_for_sale().amount;
if (limit_order_amount > 0 && !exclude_account_from_sharedrop(d, limit_order.seller))
{
total_bts_balance += limit_order_amount;
auto my_balance_iter = db_balances.find(limit_order.seller);
if (my_balance_iter == db_balances.end())
{
my_account_balance_object balance_object;
balance_object.account_id = limit_order.seller;
balance_object.orders = limit_order_amount;
db_balances.insert(balance_object);
}
else
{
db_balances.modify(my_balance_iter, [&](my_account_balance_object& balance_object) {
balance_object.orders += limit_order_amount;
});
}
}
}
// walk through the balances; this index has the largest BTS balances first
// first, calculate orders and collaterals
// second, update balance
graphene::chain::share_type orders;
graphene::chain::share_type collaterals;
graphene::chain::share_type total_bts_balance;
std::ofstream logfile;
bool sort = false;
for (auto balance_iter = db_balances.begin(); balance_iter != db_balances.end(); ++balance_iter)
{
orders = 0;
collaterals = 0;
// BTS tied up in market orders
auto order_range = d.get_index_type<graphene::chain::limit_order_index>().indices().get<graphene::chain::by_account>().equal_range(balance_iter->owner);
std::for_each(order_range.first, order_range.second,
[&orders] (const graphene::chain::limit_order_object& order) {
if (order.amount_for_sale().asset_id == graphene::chain::asset_id_type())
orders += order.amount_for_sale().amount;
});
// BTS tied up in collateral for SmartCoins
auto collateral_range = d.get_index_type<graphene::chain::call_order_index>().indices().get<graphene::chain::by_account>().equal_range(balance_iter->owner);
std::for_each(collateral_range.first, collateral_range.second,
[&collaterals] (const graphene::chain::call_order_object& order) {
collaterals += order.collateral;
});
balance_iter->initial_balance = balance_iter->balance;
balance_iter->orders = orders;
balance_iter->collaterals = collaterals;
balance_iter->balance += orders + collaterals;
sort = sort || orders.value > 0 || collaterals.value > 0;
total_bts_balance += balance_iter->balance;
}
if (sort)
{
ilog("generate genesis plugin: sorting");
std::sort(db_balances.begin(), db_balances.end(),
[](const my_account_balance_object & a, const my_account_balance_object & b) -> bool
// account for BTS tied up in collateral for SmartCoins
auto call_order_index = d.get_index_type<graphene::chain::call_order_index>().indices();
for (const graphene::chain::call_order_object& call_order : call_order_index)
if (call_order.get_collateral().asset_id == graphene::chain::asset_id_type())
{
return a.balance.value > b.balance.value;
});
}
graphene::chain::share_type call_order_amount = call_order.get_collateral().amount;
if (call_order_amount > 0 && !exclude_account_from_sharedrop(d, call_order.borrower))
{
total_bts_balance += call_order_amount;
auto my_balance_iter = db_balances.find(call_order.borrower);
if (my_balance_iter == db_balances.end())
{
my_account_balance_object balance_object;
balance_object.account_id = call_order.borrower;
balance_object.collateral = call_order_amount;
db_balances.insert(balance_object);
}
else
{
db_balances.modify(my_balance_iter, [&](my_account_balance_object& balance_object) {
balance_object.collateral += call_order_amount;
});
}
}
}
// account available-but-unclaimed BTS in vesting balances
auto vesting_balance_index = d.get_index_type<graphene::chain::vesting_balance_index>().indices();
for (const graphene::chain::vesting_balance_object& vesting_balance : vesting_balance_index)
if (vesting_balance.balance.asset_id == graphene::chain::asset_id_type())
{
graphene::chain::share_type vesting_balance_amount = vesting_balance.get_allowed_withdraw(d.head_block_time()).amount;
if (vesting_balance_amount > 0 && !exclude_account_from_sharedrop(d, vesting_balance.owner))
{
total_bts_balance += vesting_balance_amount;
auto my_balance_iter = db_balances.find(vesting_balance.owner);
if (my_balance_iter == db_balances.end())
{
my_account_balance_object balance_object;
balance_object.account_id = vesting_balance.owner;
balance_object.vesting = vesting_balance_amount;
db_balances.insert(balance_object);
}
else
{
db_balances.modify(my_balance_iter, [&](my_account_balance_object& balance_object) {
balance_object.vesting += vesting_balance_amount;
});
}
}
}
graphene::chain::share_type total_shares_dropped;
// Now, we assume we're distributing balances to all BTS holders proportionally, figure
// the smallest balance we can distribute and still assign the user a satoshi of the share drop
graphene::chain::share_type effective_total_bts_balance;
auto balance_iter = db_balances.begin();
for (balance_iter = db_balances.begin(); balance_iter != db_balances.end(); ++balance_iter)
auto& by_effective_balance_index = db_balances.get<by_effective_balance>();
auto balance_iter = by_effective_balance_index.begin();
for (; balance_iter != by_effective_balance_index.end(); ++balance_iter)
{
fc::uint128 share_drop_amount = total_amount_to_distribute.value;
share_drop_amount *= balance_iter->balance.value;
share_drop_amount *= balance_iter->get_effective_balance().value;
share_drop_amount /= total_bts_balance.value;
if (!share_drop_amount.to_uint64())
break; // balances are decreasing, so every balance after will also round to zero
total_shares_dropped += share_drop_amount.to_uint64();
effective_total_bts_balance += balance_iter->balance;
effective_total_bts_balance += balance_iter->get_effective_balance();
}
// our iterator is just after the smallest balance we will process,
// walk it backwards towards the larger balances, distributing the sharedrop as we go
graphene::chain::share_type remaining_amount_to_distribute = total_amount_to_distribute;
graphene::chain::share_type bts_balance_remaining = effective_total_bts_balance;
std::map<graphene::chain::account_id_type, graphene::chain::share_type> sharedrop_balances;
do {
--balance_iter;
fc::uint128 share_drop_amount = remaining_amount_to_distribute.value;
share_drop_amount *= balance_iter->balance.value;
share_drop_amount *= balance_iter->get_effective_balance().value;
share_drop_amount /= bts_balance_remaining.value;
graphene::chain::share_type amount_distributed = share_drop_amount.to_uint64();
sharedrop_balances[balance_iter->owner] = amount_distributed;
balance_iter->sharedrop = amount_distributed;
by_effective_balance_index.modify(balance_iter, [&](my_account_balance_object& balance_object) {
balance_object.sharedrop += amount_distributed;
});
remaining_amount_to_distribute -= amount_distributed;
bts_balance_remaining -= balance_iter->balance.value;
} while (balance_iter != db_balances.begin());
bts_balance_remaining -= balance_iter->get_effective_balance();
} while (balance_iter != by_effective_balance_index.begin());
assert(remaining_amount_to_distribute == 0);
std::ofstream logfile;
logfile.open(_csvlog_filename);
assert(logfile.is_open());
logfile << "name,balance+orders+collaterals,balance,orders,collaterals,sharedrop\n";
char del = ',';
char nl = '\n';
for(const auto& o : db_balances)
{
logfile << o.owner(d).name << del << o.balance.value << del << o.initial_balance.value << del << o.orders.value << del << o.collaterals.value << del << o.sharedrop.value << nl;
}
logfile << "name,balance+orders+collateral+vesting,balance,orders,collateral,vesting,sharedrop\n";
for (const my_account_balance_object& balance : by_effective_balance_index)
logfile << balance.account_id(d).name << "," <<
balance.get_effective_balance().value << "," <<
balance.balance.value << "," <<
balance.orders.value << "," <<
balance.collateral.value << "," <<
balance.vesting.value << "," <<
balance.sharedrop.value << "\n";
ilog("CSV log written to file ${filename}", ("filename", _csvlog_filename));
logfile.close();
//auto& account_index = d.get_index_type<graphene::chain::account_index>();
//auto& account_by_id_index = account_index.indices().get<graphene::chain::by_id>();
// remove all balance objects with zero sharedrops
by_effective_balance_index.erase(by_effective_balance_index.lower_bound(0),
by_effective_balance_index.end());
// inefficient way of crawling the graph, but we only do it once
std::set<graphene::chain::account_id_type> already_generated;
for (;;)
{
unsigned accounts_generated_this_round = 0;
for (const auto& sharedrop_value : sharedrop_balances)
for (const my_account_balance_object& balance : by_effective_balance_index)
{
const graphene::chain::account_id_type& account_id = sharedrop_value.first;
const graphene::chain::share_type& sharedrop_amount = sharedrop_value.second;
const graphene::chain::account_object& account_obj = account_id(d);
if (already_generated.find(account_id) == already_generated.end())
const graphene::chain::account_object& account_obj = balance.account_id(d);
if (already_generated.find(balance.account_id) == already_generated.end())
{
graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority owner;
owner.weight_threshold = account_obj.owner.weight_threshold;
@ -256,7 +362,7 @@ void generate_genesis_plugin::generate_snapshot()
for (const auto& value : account_obj.owner.account_auths)
{
owner.account_auths.insert(std::make_pair(modify_account_name(value.first(d).name), value.second));
sharedrop_balances[value.first] += 0; // make sure the account is generated, even if it has a zero balance
db_balances.insert(my_account_balance_object{value.first}); // make sure the account is generated, even if it has a zero balance
}
owner.key_auths = account_obj.owner.key_auths;
owner.address_auths = account_obj.owner.address_auths;
@ -267,7 +373,7 @@ void generate_genesis_plugin::generate_snapshot()
for (const auto& value : account_obj.active.account_auths)
{
active.account_auths.insert(std::make_pair(modify_account_name(value.first(d).name), value.second));
sharedrop_balances[value.first] += 0; // make sure the account is generated, even if it has a zero balance
db_balances.insert(my_account_balance_object{value.first}); // make sure the account is generated, even if it has a zero balance
}
active.key_auths = account_obj.active.key_auths;
active.address_auths = account_obj.active.address_auths;
@ -275,8 +381,8 @@ void generate_genesis_plugin::generate_snapshot()
new_genesis_state.initial_bts_accounts.emplace_back(
graphene::chain::genesis_state_type::initial_bts_account_type(modify_account_name(account_obj.name),
owner, active,
sharedrop_amount));
already_generated.insert(account_id);
balance.sharedrop));
already_generated.insert(balance.account_id);
++accounts_generated_this_round;
}
}
@ -285,7 +391,7 @@ void generate_genesis_plugin::generate_snapshot()
}
fc::json::save_to_file(new_genesis_state, _genesis_filename);
ilog("New genesis state written to file ${filename}", ("filename", _genesis_filename));
}
} FC_LOG_AND_RETHROW() }
void generate_genesis_plugin::plugin_shutdown()
{

View file

@ -57,16 +57,29 @@ private:
std::string _csvlog_filename;
};
class my_account_balance_object : public graphene::chain::account_balance_object
class my_account_balance_object
{
public:
// constructor copying from base class
my_account_balance_object(const graphene::chain::account_balance_object& abo) : graphene::chain::account_balance_object(abo) {}
//my_account_balance_object(const graphene::chain::account_balance_object& abo) : graphene::chain::account_balance_object(abo) {}
graphene::chain::account_id_type account_id;
graphene::chain::share_type initial_balance;
graphene::chain::share_type balance;
graphene::chain::share_type orders;
graphene::chain::share_type collaterals;
graphene::chain::share_type collateral;
graphene::chain::share_type vesting;
graphene::chain::share_type sharedrop;
graphene::chain::share_type get_effective_balance() const { return balance + orders + collateral + vesting; }
};
using namespace boost::multi_index;
struct by_account{};
struct by_effective_balance{};
typedef multi_index_container<my_account_balance_object,
indexed_by<ordered_unique<tag<by_account>,
member<my_account_balance_object, graphene::chain::account_id_type, &my_account_balance_object::account_id> >,
ordered_non_unique<tag<by_effective_balance>,
const_mem_fun<my_account_balance_object, graphene::chain::share_type, &my_account_balance_object::get_effective_balance>,
std::greater<graphene::chain::share_type> > > > my_account_balance_object_index_type;
} } //graphene::generate_genesis_plugin

View file

@ -0,0 +1,17 @@
file(GLOB HEADERS "include/graphene/generate_uia_sharedrop_genesis/*.hpp")
add_library( graphene_generate_uia_sharedrop_genesis
generate_uia_sharedrop_genesis.cpp
)
target_link_libraries( graphene_generate_uia_sharedrop_genesis graphene_chain graphene_app graphene_time )
target_include_directories( graphene_generate_uia_sharedrop_genesis
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
install( TARGETS
graphene_generate_uia_sharedrop_genesis
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)

View file

@ -0,0 +1,362 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/generate_uia_sharedrop_genesis/generate_uia_sharedrop_genesis.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/genesis_state.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/utilities/key_conversion.hpp>
#include <fc/smart_ref_impl.hpp>
#include <fc/thread/thread.hpp>
#include <graphene/chain/market_object.hpp>
#include <iostream>
#include <fstream>
using namespace graphene::generate_uia_sharedrop_genesis;
using std::string;
using std::vector;
namespace bpo = boost::program_options;
void generate_uia_sharedrop_genesis_plugin::plugin_set_program_options(boost::program_options::options_description& command_line_options,
boost::program_options::options_description& config_file_options)
{
command_line_options.add_options()
("input-uia-sharedrop-genesis-file", bpo::value<std::string>()->default_value("genesis.json"), "Genesis file to read")
("output-uia-sharedrop-genesis-file", bpo::value<std::string>()->default_value("genesis.json"), "Genesis file to create")
("output-uia-sharedrop-csvlog-file", bpo::value<std::string>()->default_value("log.csv"), "CSV log file to create")
("uia-sharedrop-snapshot-block-number", bpo::value<uint32_t>(), "Block number at which to snapshot balances")
;
config_file_options.add(command_line_options);
}
std::string generate_uia_sharedrop_genesis_plugin::plugin_name()const
{
return "generate_uia_sharedrop_genesis";
}
void generate_uia_sharedrop_genesis_plugin::plugin_initialize(const boost::program_options::variables_map& options)
{ try {
ilog("generate uia sharedrop genesis plugin: plugin_initialize() begin");
_options = &options;
_output_genesis_filename = options["output-uia-sharedrop-genesis-file"].as<std::string>();
_input_genesis_filename = options["input-uia-sharedrop-genesis-file"].as<std::string>();
_csvlog_filename = options["output-uia-sharedrop-csvlog-file"].as<std::string>();
if (options.count("uia-sharedrop-snapshot-block-number"))
_block_to_snapshot = options["uia-sharedrop-snapshot-block-number"].as<uint32_t>();
database().applied_block.connect([this](const graphene::chain::signed_block& b){ block_applied(b); });
ilog("generate uia sharedrop genesis plugin: plugin_initialize() end");
} FC_LOG_AND_RETHROW() }
void generate_uia_sharedrop_genesis_plugin::plugin_startup()
{ try {
ilog("generate uia sharedrop genesis plugin: plugin_startup() begin");
if (_block_to_snapshot)
{
chain::database& d = database();
if (d.head_block_num() == *_block_to_snapshot)
{
ilog("generate uia sharedrop genesis plugin: already at snapshot block");
generate_snapshot();
}
else if (d.head_block_num() > *_block_to_snapshot)
elog("generate uia sharedrop genesis plugin: already passed snapshot block, you must reindex to return to the snapshot state");
else
elog("generate uia sharedrop genesis plugin: waiting for block ${snapshot_block} to generate snapshot, current head is ${head}",
("snapshot_block", _block_to_snapshot)("head", d.head_block_num()));
}
else
ilog("generate uia sharedrop genesis plugin: no snapshot block number provided, plugin is disabled");
ilog("generate uia sharedrop genesis plugin: plugin_startup() end");
} FC_CAPTURE_AND_RETHROW() }
void generate_uia_sharedrop_genesis_plugin::block_applied(const graphene::chain::signed_block& b)
{
if (_block_to_snapshot && b.block_num() == *_block_to_snapshot)
{
ilog("generate uia sharedrop genesis plugin: snapshot block has arrived");
generate_snapshot();
}
}
namespace
{
// anonymous namespace for file-scoped helper functions
std::string modify_account_name(const std::string& name)
{
return std::string("bts-") + name;
}
std::string unmodify_account_name(const std::string& name)
{
FC_ASSERT(name.substr(0, 4) == "bts-");
return name.substr(4);
}
bool is_special_account(const graphene::chain::account_id_type& account_id)
{
return account_id.instance < 100;
}
bool is_scam(const std::string& account_name)
{
static std::set<std::string> scam_accounts{
"polonie-wallet",
"polonie-xwallet",
"poloniewallet",
"poloniex-deposit",
"poloniex-wallet",
"poloniexwall-et",
"poloniexwallett",
"poloniexwall-t",
"poloniexwalle",
"poloniex",
"poloneix",
"poloniex1",
"bittrex-deopsit",
"bittrex-deposi",
"bittrex-depositt",
"bittrex-dposit",
"bittrex",
"bittrex-deposits",
"coinbase",
"blocktrade",
"locktrades",
"yun.bts",
"transwiser-walle",
"transwiser-wallets",
"ranswiser-wallet",
"yun.btc",
"pay.coinbase.com",
"pay.bts.com",
"btc38.com",
"yunbi.com",
"coinbase.com",
"ripple.com"
};
return scam_accounts.find(account_name) != scam_accounts.end();
}
bool is_exchange(const std::string& account_name)
{
static std::set<std::string> exchange_accounts{
"poloniexcoldstorage",
"btc38-public-for-bts-cold",
"poloniexwallet",
"btercom",
"yunbi-cold-wallet",
"btc38-btsx-octo-72722",
"bittrex-deposit",
"btc38btsxwithdrawal"
};
return exchange_accounts.find(account_name) != exchange_accounts.end();
}
}
void generate_uia_sharedrop_genesis_plugin::generate_snapshot()
{
ilog("generate genesis plugin: generating snapshot now");
chain::database& d = database();
// Lookup the ID of the UIA we will be sharedropping on
std::string uia_symbol("PEERPLAYS");
const auto& assets_by_symbol = d.get_index_type<graphene::chain::asset_index>().indices().get<graphene::chain::by_symbol>();
auto itr = assets_by_symbol.find(uia_symbol);
FC_ASSERT(itr != assets_by_symbol.end(), "Unable to find asset named ${uia_symbol}", ("uia_symbol", uia_symbol));
graphene::chain::asset_id_type uia_id = itr->get_id();
ilog("Scanning for all balances of asset ${uia_symbol} (${uia_id})", ("uia_symbol", uia_symbol)("uia_id", uia_id));
uia_sharedrop_balance_object_index_type sharedrop_balances;
// load the balances from the input genesis file, if any
graphene::chain::genesis_state_type new_genesis_state;
if (!_input_genesis_filename.empty())
{
new_genesis_state = fc::json::from_file<graphene::chain::genesis_state_type>(_input_genesis_filename);
for (const graphene::chain::genesis_state_type::initial_bts_account_type& initial_bts_account : new_genesis_state.initial_bts_accounts)
{
std::string account_name = unmodify_account_name(initial_bts_account.name);
auto& account_by_name_index = d.get_index_type<graphene::chain::account_index>().indices().get<graphene::chain::by_name>();
auto account_iter = account_by_name_index.find(account_name);
FC_ASSERT(account_iter != account_by_name_index.end(), "No account ${name}", ("name", account_name));
uia_sharedrop_balance_object balance_object;
balance_object.account_id = account_iter->id;
balance_object.genesis = initial_bts_account.core_balance;
sharedrop_balances.insert(balance_object);
ilog("Loaded genesis balance for ${name}: ${balance}", ("name", account_name)("balance", initial_bts_account.core_balance));
}
}
new_genesis_state.initial_bts_accounts.clear();
auto& balance_index = d.get_index_type<graphene::chain::account_balance_index>().indices().get<graphene::chain::by_asset_balance>();
for (auto balance_iter = balance_index.begin(); balance_iter != balance_index.end(); ++balance_iter)
if (balance_iter->asset_type == uia_id && balance_iter->balance != graphene::chain::share_type())
{
if (is_special_account(balance_iter->owner) || is_exchange(balance_iter->owner(d).name) || is_scam(balance_iter->owner(d).name))
{
ilog("skipping balance in ${account_id} because special or exchange", ("account_id", balance_iter->owner));
}
else
{
auto sharedrop_balance_iter = sharedrop_balances.find(balance_iter->owner);
if (sharedrop_balance_iter == sharedrop_balances.end())
{
uia_sharedrop_balance_object balance_object;
balance_object.account_id = balance_iter->owner;
balance_object.balance = balance_iter->balance;
sharedrop_balances.insert(balance_object);
}
else
{
sharedrop_balances.modify(sharedrop_balance_iter, [&](uia_sharedrop_balance_object& balance_object) {
balance_object.balance = balance_iter->balance;
});
}
}
}
// scan for PEERPLAYS tied up in market orders
auto& limit_order_index = d.get_index_type<graphene::chain::limit_order_index>().indices().get<graphene::chain::by_account>();
for (auto limit_order_iter = limit_order_index.begin(); limit_order_iter != limit_order_index.end(); ++limit_order_iter)
{
if (limit_order_iter->sell_price.base.asset_id == uia_id)
{
if (is_special_account(limit_order_iter->seller) || is_exchange(limit_order_iter->seller(d).name) || is_scam(limit_order_iter->seller(d).name))
ilog("Skipping account ${name} because special/scam/exchange", ("name", limit_order_iter->seller(d).name));
else
{
auto sharedrop_balance_iter = sharedrop_balances.find(limit_order_iter->seller);
if (sharedrop_balance_iter == sharedrop_balances.end())
{
//ilog("found order for new account ${account_id}", ("account_id", limit_order_iter->seller));
uia_sharedrop_balance_object balance_object;
balance_object.account_id = limit_order_iter->seller;
balance_object.orders = limit_order_iter->for_sale;
sharedrop_balances.insert(balance_object);
}
else
{
//ilog("found order for existing account ${account_id}", ("account_id", limit_order_iter->seller));
sharedrop_balances.modify(sharedrop_balance_iter, [&](uia_sharedrop_balance_object& balance_object) {
balance_object.orders += limit_order_iter->for_sale;
});
}
}
}
}
// compute the sharedrop
for (auto sharedrop_balance_iter = sharedrop_balances.begin(); sharedrop_balance_iter != sharedrop_balances.end();)
{
auto this_iter = sharedrop_balance_iter;
++sharedrop_balance_iter;
sharedrop_balances.modify(this_iter, [&](uia_sharedrop_balance_object& balance_object) {
balance_object.sharedrop = balance_object.genesis + (balance_object.balance + balance_object.orders) * 10;
});
}
// Generate CSV file of all sharedrops and the balances we used to calculate them
std::ofstream csv_log_file;
csv_log_file.open(_csvlog_filename);
assert(csv_log_file.is_open());
csv_log_file << "name,genesis,balance,orders,sharedrop\n";
for (const uia_sharedrop_balance_object& balance_object : sharedrop_balances)
csv_log_file << balance_object.account_id(d).name << "," << balance_object.genesis.value << "," << balance_object.balance.value << "," << balance_object.orders.value << "," << balance_object.sharedrop.value << "\n";
ilog("CSV log written to file ${filename}", ("filename", _csvlog_filename));
csv_log_file.close();
//auto& account_index = d.get_index_type<graphene::chain::account_index>();
//auto& account_by_id_index = account_index.indices().get<graphene::chain::by_id>();
// inefficient way of crawling the graph, but we only do it once
std::set<graphene::chain::account_id_type> already_generated;
for (;;)
{
unsigned accounts_generated_this_round = 0;
for (const uia_sharedrop_balance_object& balance_object : sharedrop_balances)
{
const graphene::chain::account_id_type& account_id = balance_object.account_id;
const graphene::chain::share_type& sharedrop_amount = balance_object.sharedrop;
const graphene::chain::account_object& account_obj = account_id(d);
if (already_generated.find(account_id) == already_generated.end())
{
graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority owner;
owner.weight_threshold = account_obj.owner.weight_threshold;
owner.key_auths = account_obj.owner.key_auths;
for (const auto& value : account_obj.owner.account_auths)
{
owner.account_auths.insert(std::make_pair(modify_account_name(value.first(d).name), value.second));
auto owner_balance_iter = sharedrop_balances.find(value.first);
if (owner_balance_iter == sharedrop_balances.end())
{
uia_sharedrop_balance_object balance_object;
balance_object.account_id = value.first;
sharedrop_balances.insert(balance_object);
}
}
owner.key_auths = account_obj.owner.key_auths;
owner.address_auths = account_obj.owner.address_auths;
graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority active;
active.weight_threshold = account_obj.active.weight_threshold;
active.key_auths = account_obj.active.key_auths;
for (const auto& value : account_obj.active.account_auths)
{
active.account_auths.insert(std::make_pair(modify_account_name(value.first(d).name), value.second));
auto active_balance_iter = sharedrop_balances.find(value.first);
if (active_balance_iter == sharedrop_balances.end())
{
uia_sharedrop_balance_object balance_object;
balance_object.account_id = value.first;
sharedrop_balances.insert(balance_object);
}
}
active.key_auths = account_obj.active.key_auths;
active.address_auths = account_obj.active.address_auths;
new_genesis_state.initial_bts_accounts.emplace_back(
graphene::chain::genesis_state_type::initial_bts_account_type(modify_account_name(account_obj.name),
owner, active,
sharedrop_amount));
already_generated.insert(account_id);
++accounts_generated_this_round;
}
}
if (accounts_generated_this_round == 0)
break;
}
fc::json::save_to_file(new_genesis_state, _output_genesis_filename);
ilog("New genesis state written to file ${filename}", ("filename", _output_genesis_filename));
}
void generate_uia_sharedrop_genesis_plugin::plugin_shutdown()
{
}

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/app/plugin.hpp>
#include <graphene/chain/database.hpp>
#include <fc/thread/future.hpp>
namespace graphene { namespace generate_uia_sharedrop_genesis {
class generate_uia_sharedrop_genesis_plugin : public graphene::app::plugin {
public:
~generate_uia_sharedrop_genesis_plugin() {
}
std::string plugin_name()const override;
virtual void plugin_set_program_options(
boost::program_options::options_description &command_line_options,
boost::program_options::options_description &config_file_options
) override;
virtual void plugin_initialize( const boost::program_options::variables_map& options ) override;
virtual void plugin_startup() override;
virtual void plugin_shutdown() override;
private:
void block_applied(const graphene::chain::signed_block& b);
void generate_snapshot();
boost::program_options::variables_map _options;
fc::optional<uint32_t> _block_to_snapshot;
std::string _input_genesis_filename;
std::string _output_genesis_filename;
std::string _csvlog_filename;
};
class uia_sharedrop_balance_object
{
public:
graphene::chain::account_id_type account_id;
graphene::chain::share_type genesis;
graphene::chain::share_type balance;
graphene::chain::share_type orders;
graphene::chain::share_type sharedrop;
};
using namespace boost::multi_index;
struct by_account{};
typedef multi_index_container<uia_sharedrop_balance_object,
indexed_by<ordered_unique<tag<by_account>,
member<uia_sharedrop_balance_object, graphene::chain::account_id_type, &uia_sharedrop_balance_object::account_id> > > > uia_sharedrop_balance_object_index_type;
} } //graphene::generate_uia_sharedrop_genesis_plugin

View file

@ -201,7 +201,7 @@ block_production_condition::block_production_condition_enum witness_plugin::bloc
ilog("Not producing block because it isn't my turn");
break;
case block_production_condition::not_time_yet:
ilog("Not producing block because slot has not yet arrived");
dlog("Not producing block because slot has not yet arrived");
break;
case block_production_condition::no_private_key:
ilog("Not producing block because I don't have the private key for ${scheduled_key}", (capture) );
@ -228,9 +228,7 @@ block_production_condition::block_production_condition_enum witness_plugin::mayb
{
chain::database& db = database();
fc::time_point now_fine = graphene::time::now();
fc::time_point_sec now = now_fine;
if (db.get_global_properties().parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM)
now += fc::microseconds( 500000 );
fc::time_point_sec now = now_fine + fc::microseconds( 500000 );
// If the next block production opportunity is in the present or future, we're synced.
if( !_production_enabled )

View file

@ -580,6 +580,11 @@ class wallet_api
*/
brain_key_info suggest_brain_key()const;
/**
* @param role - active | owner | memo
*/
pair<public_key_type,string> get_private_key_from_password( string account, string role, string password )const;
/** Converts a signed_transaction in JSON form to its binary representation.
*
* TODO: I don't see a broadcast_transaction() function, do we need one?
@ -1344,6 +1349,38 @@ class wallet_api
bool approve,
bool broadcast = false);
/** Change your witness votes.
*
* An account can publish a list of all witnesses they approve of.
* Each account's vote is weighted according to the number of shares of the
* core asset owned by that account at the time the votes are tallied.
* This command allows you to add or remove one or more witnesses from this list
* in one call. When you are changing your vote on several witnesses, this
* may be easier than multiple `vote_for_witness` and
* `set_desired_witness_and_committee_member_count` calls.
*
* @note you cannot vote against a witness, you can only vote for the witness
* or not vote for the witness.
*
* @param voting_account the name or id of the account who is voting with their shares
* @param witnesses_to_approve the names or ids of the witnesses owner accounts you wish
* to approve (these will be added to the list of witnesses
* you currently approve). This list can be empty.
* @param witnesses_to_reject the names or ids of the witnesses owner accounts you wish
* to reject (these will be removed fromthe list of witnesses
* you currently approve). This list can be empty.
* @param desired_number_of_witnesses the number of witnesses you believe the network
* should have. You must vote for at least this many
* witnesses. You can set this to 0 to abstain from
* voting on the number of witnesses.
* @param broadcast true if you wish to broadcast the transaction
* @return the signed transaction changing your vote for the given witnesses
*/
signed_transaction update_witness_votes(string voting_account,
std::vector<std::string> witnesses_to_approve,
std::vector<std::string> witnesses_to_reject,
uint16_t desired_number_of_witnesses,
bool broadcast = false);
/** Set the voting proxy for an account.
*
* If a user does not wish to take an active part in voting, they can choose
@ -1656,6 +1693,7 @@ FC_API( graphene::wallet::wallet_api,
(import_account_keys)
(import_balance)
(suggest_brain_key)
(get_private_key_from_password)
(register_account)
(upgrade_account)
(create_account_with_brain_key)
@ -1694,6 +1732,7 @@ FC_API( graphene::wallet::wallet_api,
(withdraw_vesting)
(vote_for_committee_member)
(vote_for_witness)
(update_witness_votes)
(set_voting_proxy)
(set_desired_witness_and_committee_member_count)
(get_account)

View file

@ -451,6 +451,20 @@ private:
{
// idump((e));
}
try
{
object_id_type id = changed_object_variant["id"].as<account_id_type>();
if (_wallet.my_accounts.find(id) != _wallet.my_accounts.end())
{
account_object account = changed_object_variant.as<account_object>();
_wallet.update_account(account);
}
continue;
}
catch (const fc::exception& e)
{
// idump((e));
}
}
}
}
@ -1753,6 +1767,7 @@ public:
fc::raw::pack(enc, secret_hash_type());
witness_create_op.initial_secret = secret_hash_type::hash(enc.result());
if (_remote_db->get_witness_by_account(witness_create_op.witness_account))
FC_THROW("Account ${owner_account} is already a witness", ("owner_account", owner_account));
@ -1780,8 +1795,10 @@ public:
witness_update_op.witness_account = witness_account.id;
if( url != "" )
witness_update_op.new_url = url;
if( block_signing_key != "" )
if( block_signing_key != "" ) {
witness_update_op.new_signing_key = public_key_type( block_signing_key );
witness_update_op.new_initial_secret = secret_hash_type::hash(secret_hash_type());
}
signed_transaction tx;
tx.operations.push_back( witness_update_op );
@ -2001,9 +2018,9 @@ public:
} FC_CAPTURE_AND_RETHROW( (voting_account)(committee_member)(approve)(broadcast) ) }
signed_transaction vote_for_witness(string voting_account,
string witness,
bool approve,
bool broadcast /* = false */)
string witness,
bool approve,
bool broadcast /* = false */)
{ try {
account_object voting_account_object = get_account(voting_account);
account_id_type witness_owner_account_id = get_account_id(witness);
@ -2034,6 +2051,47 @@ public:
return sign_transaction( tx, broadcast );
} FC_CAPTURE_AND_RETHROW( (voting_account)(witness)(approve)(broadcast) ) }
signed_transaction update_witness_votes(string voting_account,
std::vector<std::string> witnesses_to_approve,
std::vector<std::string> witnesses_to_reject,
uint16_t desired_number_of_witnesses,
bool broadcast /* = false */)
{ try {
account_object voting_account_object = get_account(voting_account);
for (const std::string& witness : witnesses_to_approve)
{
account_id_type witness_owner_account_id = get_account_id(witness);
fc::optional<witness_object> witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id);
if (!witness_obj)
FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness));
auto insert_result = voting_account_object.options.votes.insert(witness_obj->vote_id);
if (!insert_result.second)
FC_THROW("Account ${account} was already voting for witness ${witness}", ("account", voting_account)("witness", witness));
}
for (const std::string& witness : witnesses_to_reject)
{
account_id_type witness_owner_account_id = get_account_id(witness);
fc::optional<witness_object> witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id);
if (!witness_obj)
FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness));
unsigned votes_removed = voting_account_object.options.votes.erase(witness_obj->vote_id);
if (!votes_removed)
FC_THROW("Account ${account} is already not voting for witness ${witness}", ("account", voting_account)("witness", witness));
}
voting_account_object.options.num_witness = desired_number_of_witnesses;
account_update_operation account_update_op;
account_update_op.account = voting_account_object.id;
account_update_op.new_options = voting_account_object.options;
signed_transaction tx;
tx.operations.push_back( account_update_op );
set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees);
tx.validate();
return sign_transaction( tx, broadcast );
} FC_CAPTURE_AND_RETHROW( (voting_account)(witnesses_to_approve)(witnesses_to_reject)(desired_number_of_witnesses)(broadcast) ) }
signed_transaction set_voting_proxy(string account_to_modify,
optional<string> voting_account,
bool broadcast /* = false */)
@ -3379,6 +3437,14 @@ brain_key_info wallet_api::suggest_brain_key()const
return result;
}
pair<public_key_type,string> wallet_api::get_private_key_from_password( string account, string role, string password )const {
auto seed = password + account + role;
FC_ASSERT( seed.size() );
auto secret = fc::sha256::hash( seed.c_str(), seed.size() );
auto priv = fc::ecc::private_key::regenerate( secret );
return std::make_pair( public_key_type( priv.get_public_key() ), key_to_wif( priv ) );
}
string wallet_api::serialize_transaction( signed_transaction tx )const
{
return fc::to_hex(fc::raw::pack(tx));
@ -3845,6 +3911,15 @@ signed_transaction wallet_api::vote_for_witness(string voting_account,
return my->vote_for_witness(voting_account, witness, approve, broadcast);
}
signed_transaction wallet_api::update_witness_votes(string voting_account,
std::vector<std::string> witnesses_to_approve,
std::vector<std::string> witnesses_to_reject,
uint16_t desired_number_of_witnesses,
bool broadcast /* = false */)
{
return my->update_witness_votes(voting_account, witnesses_to_approve, witnesses_to_reject, desired_number_of_witnesses, broadcast);
}
signed_transaction wallet_api::set_voting_proxy(string account_to_modify,
optional<string> voting_account,
bool broadcast /* = false */)

View file

@ -70,6 +70,8 @@ int main( int argc, char** argv )
("dev-account-count", bpo::value<uint32_t>()->default_value(0), "Prefix for dev accounts")
("dev-balance-count", bpo::value<uint32_t>()->default_value(0), "Prefix for dev balances")
("dev-balance-amount", bpo::value<uint64_t>()->default_value(uint64_t(1000)*uint64_t(1000)*uint64_t(100000)), "Amount in each dev balance")
("nop", "just write the genesis file out after reading it in, do not alter any keys or add accounts or balances. used to pretty-print a genesis file")
("replace-all-keys", bpo::value<boost::filesystem::path>(), "Replace all keys/addresses in the genesis files with dev keys based on dev-key-prefix and dump the new keys to this filename.")
;
bpo::variables_map options;
@ -116,54 +118,134 @@ int main( int argc, char** argv )
genesis = graphene::app::detail::create_example_genesis();
}
std::string dev_key_prefix = options["dev-key-prefix"].as<std::string>();
auto get_dev_key = [&]( std::string prefix, uint32_t i ) -> public_key_type
if (!options.count("nop"))
{
return fc::ecc::private_key::regenerate( fc::sha256::hash( dev_key_prefix + prefix + std::to_string(i) ) ).get_public_key();
};
uint32_t dev_account_count = options["dev-account-count"].as<uint32_t>();
std::string dev_account_prefix = options["dev-account-prefix"].as<std::string>();
for(uint32_t i=0;i<dev_account_count;i++)
{
genesis_state_type::initial_account_type acct(
dev_account_prefix+std::to_string(i),
get_dev_key( "owner-", i ),
get_dev_key( "active-", i ),
false );
genesis.initial_accounts.push_back( acct );
}
uint32_t dev_balance_count = options["dev-balance-count"].as<uint32_t>();
uint64_t dev_balance_amount = options["dev-balance-amount"].as<uint64_t>();
for(uint32_t i=0;i<dev_balance_count;i++)
{
genesis_state_type::initial_balance_type bal;
bal.owner = address( get_dev_key( "balance-", i ) );
bal.asset_symbol = "CORE";
bal.amount = dev_balance_amount;
genesis.initial_balances.push_back( bal );
}
std::map< std::string, size_t > name2index;
size_t num_accounts = genesis.initial_accounts.size();
for( size_t i=0; i<num_accounts; i++ )
name2index[ genesis.initial_accounts[i].name ] = i;
for( uint32_t i=0; i<genesis.initial_active_witnesses; i++ )
{
genesis_state_type::initial_witness_type& wit = genesis.initial_witness_candidates[ i ];
genesis_state_type::initial_account_type& wit_acct = genesis.initial_accounts[ name2index[ wit.owner_name ] ];
if( wit.owner_name.substr(0, 4) != "init" )
std::string dev_key_prefix = options["dev-key-prefix"].as<std::string>();
auto get_dev_key = [&]( std::string prefix, uint32_t i ) -> public_key_type
{
std::cerr << "need " << genesis.initial_active_witnesses << " init accounts as first entries in initial_active_witnesses\n";
return 1;
return fc::ecc::private_key::regenerate( fc::sha256::hash( dev_key_prefix + prefix + std::to_string(i) ) ).get_public_key();
};
if (options.count("replace-all-keys"))
{
unsigned dev_keys_used = 0;
std::map<std::string, fc::ecc::private_key> replacement_keys;
auto get_replacement_key = [&](const std::string& original_key) -> fc::ecc::private_key {
auto iter = replacement_keys.find(original_key);
if (iter != replacement_keys.end())
return iter->second;
fc::ecc::private_key new_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(dev_key_prefix + std::to_string(dev_keys_used++)));
replacement_keys[original_key] = new_private_key;
return new_private_key;
};
for (genesis_state_type::initial_balance_type& initial_balance : genesis.initial_balances)
{
std::string address_string = (std::string)initial_balance.owner;
initial_balance.owner = address(get_replacement_key(address_string).get_public_key());
}
for (genesis_state_type::initial_vesting_balance_type& initial_balance : genesis.initial_vesting_balances)
{
std::string address_string = (std::string)initial_balance.owner;
initial_balance.owner = address(get_replacement_key(address_string).get_public_key());
}
for (genesis_state_type::initial_witness_type& initial_witness : genesis.initial_witness_candidates)
{
std::string public_key_string = (std::string)initial_witness.block_signing_key;
initial_witness.block_signing_key = get_replacement_key(public_key_string).get_public_key();
}
for (genesis_state_type::initial_account_type& initial_account : genesis.initial_accounts)
{
std::string public_key_string = (std::string)initial_account.owner_key;
initial_account.owner_key = get_replacement_key(public_key_string).get_public_key();
public_key_string = (std::string)initial_account.active_key;
initial_account.active_key = get_replacement_key(public_key_string).get_public_key();
}
for (genesis_state_type::initial_bts_account_type& initial_account : genesis.initial_bts_accounts)
{
for (auto iter = initial_account.owner_authority.key_auths.begin();
iter != initial_account.owner_authority.key_auths.end(); ++iter)
{
std::string public_key_string = (std::string)iter->first;
iter->first = get_replacement_key(public_key_string).get_public_key();
}
for (auto iter = initial_account.active_authority.key_auths.begin();
iter != initial_account.active_authority.key_auths.end(); ++iter)
{
std::string public_key_string = (std::string)iter->first;
iter->first = get_replacement_key(public_key_string).get_public_key();
}
for (auto iter = initial_account.owner_authority.address_auths.begin();
iter != initial_account.owner_authority.address_auths.end(); ++iter)
{
std::string address_string = (std::string)iter->first;
iter->first = address(get_replacement_key(address_string).get_public_key());
}
for (auto iter = initial_account.active_authority.address_auths.begin();
iter != initial_account.active_authority.address_auths.end(); ++iter)
{
std::string address_string = (std::string)iter->first;
iter->first = address(get_replacement_key(address_string).get_public_key());
}
}
fc::path keys_csv_path = options["replace-all-keys"].as<boost::filesystem::path>();
std::ofstream keys_csv(keys_csv_path.string());
keys_csv << "wif_private_key,public_key,address\n";
for (const auto& value : replacement_keys)
keys_csv << graphene::utilities::key_to_wif(value.second) << "," << std::string(public_key_type(value.second.get_public_key()))
<< "," << std::string(address(value.second.get_public_key())) << "\n";
}
else
{
uint32_t dev_account_count = options["dev-account-count"].as<uint32_t>();
std::string dev_account_prefix = options["dev-account-prefix"].as<std::string>();
for(uint32_t i=0;i<dev_account_count;i++)
{
genesis_state_type::initial_account_type acct(
dev_account_prefix+std::to_string(i),
get_dev_key( "owner-", i ),
get_dev_key( "active-", i ),
false );
genesis.initial_accounts.push_back( acct );
}
uint32_t dev_balance_count = options["dev-balance-count"].as<uint32_t>();
uint64_t dev_balance_amount = options["dev-balance-amount"].as<uint64_t>();
for(uint32_t i=0;i<dev_balance_count;i++)
{
genesis_state_type::initial_balance_type bal;
bal.owner = address( get_dev_key( "balance-", i ) );
bal.asset_symbol = "CORE";
bal.amount = dev_balance_amount;
genesis.initial_balances.push_back( bal );
}
std::map< std::string, size_t > name2index;
size_t num_accounts = genesis.initial_accounts.size();
for( size_t i=0; i<num_accounts; i++ )
name2index[ genesis.initial_accounts[i].name ] = i;
for( uint32_t i=0; i<genesis.initial_active_witnesses; i++ )
{
genesis_state_type::initial_witness_type& wit = genesis.initial_witness_candidates[ i ];
genesis_state_type::initial_account_type& wit_acct = genesis.initial_accounts[ name2index[ wit.owner_name ] ];
if( wit.owner_name.substr(0, 4) != "init" )
{
std::cerr << "need " << genesis.initial_active_witnesses << " init accounts as first entries in initial_active_witnesses\n";
return 1;
}
wit.block_signing_key = get_dev_key( "wit-block-signing-", i );
wit_acct.owner_key = get_dev_key( "wit-owner-", i );
wit_acct.active_key = get_dev_key( "wit-active-", i );
}
}
wit.block_signing_key = get_dev_key( "wit-block-signing-", i );
wit_acct.owner_key = get_dev_key( "wit-owner-", i );
wit_acct.active_key = get_dev_key( "wit-active-", i );
}
fc::path output_filename = options["out"].as<boost::filesystem::path>();

View file

@ -11,7 +11,8 @@ endif()
# We have to link against graphene_debug_witness because deficiency in our API infrastructure doesn't allow plugins to be fully abstracted #246
target_link_libraries( witness_node
PRIVATE graphene_app graphene_account_history graphene_market_history graphene_generate_genesis graphene_witness graphene_chain graphene_debug_witness graphene_egenesis_full fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
PRIVATE graphene_app graphene_account_history graphene_market_history graphene_witness graphene_chain graphene_debug_witness graphene_egenesis_full fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
# also add dependencies to graphene_generate_genesis graphene_generate_uia_sharedrop_genesis if you want those plugins
install( TARGETS
witness_node

View file

@ -26,7 +26,8 @@
#include <graphene/witness/witness.hpp>
#include <graphene/account_history/account_history_plugin.hpp>
#include <graphene/market_history/market_history_plugin.hpp>
#include <graphene/generate_genesis/generate_genesis_plugin.hpp>
//#include <graphene/generate_genesis/generate_genesis_plugin.hpp>
//#include <graphene/generate_uia_sharedrop_genesis/generate_uia_sharedrop_genesis.hpp>
#include <fc/exception/exception.hpp>
#include <fc/thread/thread.hpp>
@ -76,6 +77,7 @@ int main(int argc, char** argv) {
auto history_plug = node->register_plugin<account_history::account_history_plugin>();
auto market_history_plug = node->register_plugin<market_history::market_history_plugin>();
//auto generate_genesis_plug = node->register_plugin<generate_genesis_plugin::generate_genesis_plugin>();
//auto generate_uia_sharedrop_genesis_plug = node->register_plugin<generate_uia_sharedrop_genesis::generate_uia_sharedrop_genesis_plugin>();
try
{

View file

@ -80,6 +80,8 @@ database_fixture::database_fixture()
boost::program_options::variables_map options;
genesis_state.initial_timestamp = time_point_sec( GRAPHENE_TESTING_GENESIS_TIMESTAMP );
genesis_state.initial_timestamp = time_point_sec( (fc::time_point::now().sec_since_epoch() / GRAPHENE_DEFAULT_BLOCK_INTERVAL) * GRAPHENE_DEFAULT_BLOCK_INTERVAL );
// genesis_state.initial_parameters.witness_schedule_algorithm = GRAPHENE_WITNESS_SHUFFLED_ALGORITHM;
genesis_state.initial_active_witnesses = 10;
for( unsigned i = 0; i < genesis_state.initial_active_witnesses; ++i )

View file

@ -487,6 +487,8 @@ BOOST_AUTO_TEST_CASE( witness_create )
generator_helper h = std::for_each(near_witnesses.begin(), near_witnesses.end(),
generator_helper{*this, nathan_witness_id, nathan_private_key, false});
BOOST_CHECK(h.nathan_generated_block);
BOOST_CHECK_EQUAL( db.witness_participation_rate(), GRAPHENE_100_PERCENT );
}
if (db.get_global_properties().parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM)