From 78f0cfa5cc22c8a56a93aedfbbcb8f1d38d96716 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Mon, 13 Jul 2015 15:55:12 -0400 Subject: [PATCH 1/5] account_evaluator.cpp: Remove unused global_property_object --- libraries/chain/account_evaluator.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index 892e21f3..7a6ac1fd 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -82,7 +82,6 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio obj.options = o.options; }); - const auto& global_properties = db().get_global_properties(); const auto& dynamic_properties = db().get_dynamic_global_properties(); db().modify(dynamic_properties, [](dynamic_global_property_object& p) { ++p.accounts_registered_this_interval; From c019f18bd4f430f316390dce0b54701f2f02b410 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Mon, 13 Jul 2015 15:07:36 -0400 Subject: [PATCH 2/5] Implement recent_slots_filled and use it to measure witness participation --- libraries/chain/db_getter.cpp | 10 ---------- libraries/chain/db_init.cpp | 2 ++ libraries/chain/db_witness_schedule.cpp | 10 ++++++++++ libraries/chain/include/graphene/chain/database.hpp | 4 ++-- .../include/graphene/chain/global_property_object.hpp | 2 ++ .../include/graphene/chain/witness_schedule_object.hpp | 8 ++++++++ libraries/fc | 2 +- .../witness/include/graphene/witness/witness.hpp | 2 +- libraries/plugins/witness/witness.cpp | 8 ++++---- 9 files changed, 30 insertions(+), 18 deletions(-) diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp index 3e87beae..28e15602 100644 --- a/libraries/chain/db_getter.cpp +++ b/libraries/chain/db_getter.cpp @@ -73,14 +73,4 @@ node_property_object& database::node_properties() return _node_property_object; } -double database::witness_participation_rate()const -{ - if( head_block_num() < 10 ) return 1; // sample size is too small - uint32_t produced = std::min( head_block_num()-1, 100 ); - auto prior = fetch_block_by_number( head_block_num() - produced ); - auto delta_time = head_block_time() - prior->timestamp; - auto expected_slots = delta_time.to_seconds() / get_global_properties().parameters.block_interval; - return double(produced) / expected_slots; -} - } } diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index bf3e16bf..ffd928a3 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -525,6 +525,8 @@ void database::init_genesis(const genesis_state_type& genesis_state) _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() ); diff --git a/libraries/chain/db_witness_schedule.cpp b/libraries/chain/db_witness_schedule.cpp index 4585e605..d233c8bf 100644 --- a/libraries/chain/db_witness_schedule.cpp +++ b/libraries/chain/db_witness_schedule.cpp @@ -141,6 +141,16 @@ void database::update_witness_schedule(const signed_block& next_block) 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; +} + } } diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index bbd001d6..0500f834 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -138,9 +138,9 @@ namespace graphene { namespace chain { /** * Calculate the percent of block production slots that were missed in the - * past 100 blocks. + * past 128 blocks, not including the current block. */ - double witness_participation_rate()const; + uint32_t witness_participation_rate()const; void add_checkpoints( const flat_map& checkpts ); const flat_map get_checkpoints()const { return _checkpoints; } diff --git a/libraries/chain/include/graphene/chain/global_property_object.hpp b/libraries/chain/include/graphene/chain/global_property_object.hpp index 67d7e7b5..4b83b993 100644 --- a/libraries/chain/include/graphene/chain/global_property_object.hpp +++ b/libraries/chain/include/graphene/chain/global_property_object.hpp @@ -16,6 +16,8 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once +#include + #include #include #include diff --git a/libraries/chain/include/graphene/chain/witness_schedule_object.hpp b/libraries/chain/include/graphene/chain/witness_schedule_object.hpp index 5661651c..cd11ebca 100644 --- a/libraries/chain/include/graphene/chain/witness_schedule_object.hpp +++ b/libraries/chain/include/graphene/chain/witness_schedule_object.hpp @@ -19,6 +19,7 @@ // needed to serialize witness_scheduler #include +#include #include #include @@ -57,6 +58,12 @@ class witness_schedule_object : public abstract_object 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; }; } } @@ -77,4 +84,5 @@ FC_REFLECT_DERIVED( graphene::chain::witness_schedule_object, (graphene::chain:: (last_scheduling_block) (slots_since_genesis) (rng_seed) + (recent_slots_filled) ) diff --git a/libraries/fc b/libraries/fc index 03916654..5f43c06b 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit 0391665471b4dffb7af951853a777565a7ae9a74 +Subproject commit 5f43c06bae6d03e319dd2f230fe7cbd74399a773 diff --git a/libraries/plugins/witness/include/graphene/witness/witness.hpp b/libraries/plugins/witness/include/graphene/witness/witness.hpp index 0f5bbdb2..3d2dc0bc 100644 --- a/libraries/plugins/witness/include/graphene/witness/witness.hpp +++ b/libraries/plugins/witness/include/graphene/witness/witness.hpp @@ -57,7 +57,7 @@ private: boost::program_options::variables_map _options; bool _production_enabled = false; bool _consecutive_production_enabled = false; - int _required_witness_participation = 33; + uint32_t _required_witness_participation = 33 * GRAPHENE_1_PERCENT; std::map _private_keys; std::set _witnesses; diff --git a/libraries/plugins/witness/witness.cpp b/libraries/plugins/witness/witness.cpp index bd336c83..c48184da 100644 --- a/libraries/plugins/witness/witness.cpp +++ b/libraries/plugins/witness/witness.cpp @@ -39,7 +39,7 @@ void witness_plugin::plugin_set_program_options( string witness_id_example = fc::json::to_string(chain::witness_id_type()); command_line_options.add_options() ("enable-stale-production", bpo::bool_switch()->notifier([this](bool e){_production_enabled = e;}), "Enable block production, even if the chain is stale.") - ("required-participation", bpo::bool_switch()->notifier([this](int e){_required_witness_participation = e;}), "Percent of witnesses (0-99) that must be participating in order to produce blocks") + ("required-participation", bpo::bool_switch()->notifier([this](int e){_required_witness_participation = uint32_t(e*GRAPHENE_1_PERCENT);}), "Percent of witnesses (0-99) that must be participating in order to produce blocks") ("allow-consecutive", bpo::bool_switch()->notifier([this](bool e){_consecutive_production_enabled = e;}), "Allow block production, even if the last block was produced by the same witness.") ("witness-id,w", bpo::value>()->composing()->multitoken(), ("ID of witness controlled by this node (e.g. " + witness_id_example + ", quotes are required, may specify multiple times)").c_str()) @@ -191,11 +191,11 @@ void witness_plugin::block_production_loop() return false; } - double prate = db.witness_participation_rate(); - if( int(100*prate) < _required_witness_participation ) + uint32_t prate = db.witness_participation_rate(); + if( prate < _required_witness_participation ) { elog("Not producing block because node appers to be on a minority fork with only ${x}% witness participation", - ("x",uint32_t(100*prate) ) ); + ("x",uint32_t(100*uint64_t(prate) / GRAPHENE_1_PERCENT) ) ); return false; } From d48e97cab7b2bff9304f42bab06f8a701d959de7 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Mon, 13 Jul 2015 16:34:53 -0400 Subject: [PATCH 3/5] Test recent_slots_filled --- tests/tests/block_tests.cpp | 105 ++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index c7e3a575..0d31cd17 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -907,4 +907,109 @@ BOOST_FIXTURE_TEST_CASE( witness_scheduler_missed_blocks, database_fixture ) }); } FC_LOG_AND_RETHROW() } +BOOST_FIXTURE_TEST_CASE( rsf_missed_blocks, database_fixture ) +{ + try + { + generate_block(); + + auto rsf = [&]() -> string + { + fc::uint128 rsf = db.get( witness_schedule_id_type() ).recent_slots_filled; + string result = ""; + result.reserve(128); + for( int i=0; i<128; i++ ) + { + result += ((rsf.lo & 1) == 0) ? '0' : '1'; + rsf >>= 1; + } + return result; + }; + + BOOST_CHECK_EQUAL( rsf(), + "1111111111111111111111111111111111111111111111111111111111111111" + "1111111111111111111111111111111111111111111111111111111111111111" + ); + + generate_block( ~0, init_account_priv_key, 1 ); + BOOST_CHECK_EQUAL( rsf(), + "0111111111111111111111111111111111111111111111111111111111111111" + "1111111111111111111111111111111111111111111111111111111111111111" + ); + + generate_block( ~0, init_account_priv_key, 1 ); + BOOST_CHECK_EQUAL( rsf(), + "0101111111111111111111111111111111111111111111111111111111111111" + "1111111111111111111111111111111111111111111111111111111111111111" + ); + + generate_block( ~0, init_account_priv_key, 2 ); + BOOST_CHECK_EQUAL( rsf(), + "0010101111111111111111111111111111111111111111111111111111111111" + "1111111111111111111111111111111111111111111111111111111111111111" + ); + + generate_block( ~0, init_account_priv_key, 3 ); + BOOST_CHECK_EQUAL( rsf(), + "0001001010111111111111111111111111111111111111111111111111111111" + "1111111111111111111111111111111111111111111111111111111111111111" + ); + + generate_block( ~0, init_account_priv_key, 5 ); + BOOST_CHECK_EQUAL( rsf(), + "0000010001001010111111111111111111111111111111111111111111111111" + "1111111111111111111111111111111111111111111111111111111111111111" + ); + + generate_block( ~0, init_account_priv_key, 8 ); + BOOST_CHECK_EQUAL( rsf(), + "0000000010000010001001010111111111111111111111111111111111111111" + "1111111111111111111111111111111111111111111111111111111111111111" + ); + + generate_block( ~0, init_account_priv_key, 13 ); + BOOST_CHECK_EQUAL( rsf(), + "0000000000000100000000100000100010010101111111111111111111111111" + "1111111111111111111111111111111111111111111111111111111111111111" + ); + + generate_block(); + BOOST_CHECK_EQUAL( rsf(), + "1000000000000010000000010000010001001010111111111111111111111111" + "1111111111111111111111111111111111111111111111111111111111111111" + ); + + generate_block(); + BOOST_CHECK_EQUAL( rsf(), + "1100000000000001000000001000001000100101011111111111111111111111" + "1111111111111111111111111111111111111111111111111111111111111111" + ); + + generate_block(); + BOOST_CHECK_EQUAL( rsf(), + "1110000000000000100000000100000100010010101111111111111111111111" + "1111111111111111111111111111111111111111111111111111111111111111" + ); + + generate_block(); + BOOST_CHECK_EQUAL( rsf(), + "1111000000000000010000000010000010001001010111111111111111111111" + "1111111111111111111111111111111111111111111111111111111111111111" + ); + + generate_block( ~0, init_account_priv_key, 64 ); + BOOST_CHECK_EQUAL( rsf(), + "0000000000000000000000000000000000000000000000000000000000000000" + "1111100000000000001000000001000001000100101011111111111111111111" + ); + + generate_block( ~0, init_account_priv_key, 32 ); + BOOST_CHECK_EQUAL( rsf(), + "0000000000000000000000000000000010000000000000000000000000000000" + "0000000000000000000000000000000001111100000000000001000000001000" + ); + } + FC_LOG_AND_RETHROW() +} + BOOST_AUTO_TEST_SUITE_END() From 6fd566e6f73b69944db23c0366303c61ad2f4185 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Mon, 13 Jul 2015 16:43:49 -0400 Subject: [PATCH 4/5] block_tests.cpp: Test witness participation rate --- tests/tests/block_tests.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 0d31cd17..0dca6448 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -926,88 +926,107 @@ BOOST_FIXTURE_TEST_CASE( rsf_missed_blocks, database_fixture ) return result; }; + auto pct = []( uint32_t x ) -> uint32_t + { + return uint64_t( GRAPHENE_100_PERCENT ) * x / 128; + }; + BOOST_CHECK_EQUAL( rsf(), "1111111111111111111111111111111111111111111111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); + BOOST_CHECK_EQUAL( db.witness_participation_rate(), GRAPHENE_100_PERCENT ); generate_block( ~0, init_account_priv_key, 1 ); BOOST_CHECK_EQUAL( rsf(), "0111111111111111111111111111111111111111111111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); + BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(127) ); generate_block( ~0, init_account_priv_key, 1 ); BOOST_CHECK_EQUAL( rsf(), "0101111111111111111111111111111111111111111111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); + BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(126) ); generate_block( ~0, init_account_priv_key, 2 ); BOOST_CHECK_EQUAL( rsf(), "0010101111111111111111111111111111111111111111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); + BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(124) ); generate_block( ~0, init_account_priv_key, 3 ); BOOST_CHECK_EQUAL( rsf(), "0001001010111111111111111111111111111111111111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); + BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(121) ); generate_block( ~0, init_account_priv_key, 5 ); BOOST_CHECK_EQUAL( rsf(), "0000010001001010111111111111111111111111111111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); + BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(116) ); generate_block( ~0, init_account_priv_key, 8 ); BOOST_CHECK_EQUAL( rsf(), "0000000010000010001001010111111111111111111111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); + BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(108) ); generate_block( ~0, init_account_priv_key, 13 ); BOOST_CHECK_EQUAL( rsf(), "0000000000000100000000100000100010010101111111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); + BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(95) ); generate_block(); BOOST_CHECK_EQUAL( rsf(), "1000000000000010000000010000010001001010111111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); + BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(95) ); generate_block(); BOOST_CHECK_EQUAL( rsf(), "1100000000000001000000001000001000100101011111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); + BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(95) ); generate_block(); BOOST_CHECK_EQUAL( rsf(), "1110000000000000100000000100000100010010101111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); + BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(95) ); generate_block(); BOOST_CHECK_EQUAL( rsf(), "1111000000000000010000000010000010001001010111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); + BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(95) ); generate_block( ~0, init_account_priv_key, 64 ); BOOST_CHECK_EQUAL( rsf(), "0000000000000000000000000000000000000000000000000000000000000000" "1111100000000000001000000001000001000100101011111111111111111111" ); + BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(31) ); generate_block( ~0, init_account_priv_key, 32 ); BOOST_CHECK_EQUAL( rsf(), "0000000000000000000000000000000010000000000000000000000000000000" "0000000000000000000000000000000001111100000000000001000000001000" ); + BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(8) ); } FC_LOG_AND_RETHROW() } From 3ad1c1715d663f2696002a369f9e0a89f19f1b41 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Mon, 13 Jul 2015 17:22:06 -0400 Subject: [PATCH 5/5] [GUI] Add name field and identicon to transfer form --- programs/light_client/ClientDataModel.hpp | 11 ++++ programs/light_client/main.cpp | 4 +- programs/light_client/qml/.gitignore | 1 + programs/light_client/qml/FormBox.qml | 48 ++++++++++------- programs/light_client/qml/Scaling.qml | 12 +++++ programs/light_client/qml/TransferForm.qml | 52 +++++++++++++++++++ .../qml/jdenticon/jdenticon-1.0.1.min.js | 18 +++++++ .../light_client/qml/jdenticon/license.txt | 21 ++++++++ programs/light_client/qml/main.qml | 9 ++-- programs/light_client/qml/qml.qrc | 2 + programs/light_client/qml/qmldir | 1 + 11 files changed, 157 insertions(+), 22 deletions(-) create mode 100644 programs/light_client/qml/.gitignore create mode 100644 programs/light_client/qml/Scaling.qml create mode 100755 programs/light_client/qml/jdenticon/jdenticon-1.0.1.min.js create mode 100755 programs/light_client/qml/jdenticon/license.txt create mode 100644 programs/light_client/qml/qmldir diff --git a/programs/light_client/ClientDataModel.hpp b/programs/light_client/ClientDataModel.hpp index 9aab8112..fd1a209e 100644 --- a/programs/light_client/ClientDataModel.hpp +++ b/programs/light_client/ClientDataModel.hpp @@ -11,14 +11,25 @@ #include #include +#include #include #include +#include using boost::multi_index_container; using namespace boost::multi_index; Q_DECLARE_METATYPE(std::function) +class Crypto { + Q_GADGET + +public: + Q_INVOKABLE QString sha256(QByteArray data) { + return QCryptographicHash::hash(data, QCryptographicHash::Sha256).toHex(); + } +}; +QML_DECLARE_TYPE(Crypto) class Asset : public QObject { Q_OBJECT diff --git a/programs/light_client/main.cpp b/programs/light_client/main.cpp index ccd7a600..2c733daa 100644 --- a/programs/light_client/main.cpp +++ b/programs/light_client/main.cpp @@ -4,7 +4,6 @@ #include "ClientDataModel.hpp" - int main(int argc, char *argv[]) { fc::thread::current().set_name( "main" ); @@ -22,6 +21,9 @@ int main(int argc, char *argv[]) qmlRegisterType("Graphene.Client", 0, 1, "GrapheneApplication"); QQmlApplicationEngine engine; + QVariant crypto; + crypto.setValue(Crypto()); + engine.rootContext()->setContextProperty("Crypto", crypto); #ifdef NDEBUG engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); #else diff --git a/programs/light_client/qml/.gitignore b/programs/light_client/qml/.gitignore new file mode 100644 index 00000000..c6593022 --- /dev/null +++ b/programs/light_client/qml/.gitignore @@ -0,0 +1 @@ +*.depends diff --git a/programs/light_client/qml/FormBox.qml b/programs/light_client/qml/FormBox.qml index 7d50959c..1df969ef 100644 --- a/programs/light_client/qml/FormBox.qml +++ b/programs/light_client/qml/FormBox.qml @@ -5,6 +5,7 @@ import QtQuick.Dialogs 1.2 Rectangle { id: greySheet state: "HIDDEN" + color: Qt.rgba(0, 0, 0, showOpacity) property real showOpacity: .5 property int animationTime: 300 @@ -18,8 +19,11 @@ Rectangle { /// Emitted when closed, following fade-out animation signal closed - function showForm(formType, closedCallback) { - formLoader.sourceComponent = formType + function showForm(formType, params, closedCallback) { + if (formType.status === Component.Error) + console.log(formType.errorString()) + + formContainer.data = [formType.createObject(formContainer, params)] if (closedCallback instanceof Function) internal.callback = closedCallback state = "SHOWN" @@ -35,12 +39,12 @@ Rectangle { acceptedButtons: Qt.AllButtons } MouseArea { - anchors.fill: formLoader + anchors.fill: formContainer acceptedButtons: Qt.AllButtons onClicked: mouse.accepted = true } - Loader { - id: formLoader + Item { + id: formContainer anchors.centerIn: parent width: parent.width / 2 height: parent.height / 2 @@ -51,7 +55,7 @@ Rectangle { name: "HIDDEN" PropertyChanges { target: greySheet - color: Qt.rgba(0, 0, 0, 0) + opacity: 0 enabled: false } StateChangeScript { @@ -63,10 +67,12 @@ Rectangle { StateChangeScript { name: "postHidden" script: { + console.log("Post") greySheet.closed() - formLoader.sourceComponent = undefined + formContainer.data = [] if (internal.callback instanceof Function) internal.callback() + internal.callback = undefined } } }, @@ -74,7 +80,7 @@ Rectangle { name: "SHOWN" PropertyChanges { target: greySheet - color: Qt.rgba(0, 0, 0, showOpacity) + opacity: 1 enabled: true } StateChangeScript { @@ -95,22 +101,28 @@ Rectangle { Transition { from: "HIDDEN" to: "SHOWN" - ScriptAction { scriptName: "preShown" } - ColorAnimation { - target: greySheet - duration: animationTime + SequentialAnimation { + ScriptAction { scriptName: "preShown" } + PropertyAnimation { + target: greySheet + property: "opacity" + duration: animationTime + } + ScriptAction { scriptName: "postShown" } } - ScriptAction { scriptName: "postShown" } }, Transition { from: "SHOWN" to: "HIDDEN" - ScriptAction { scriptName: "preHidden" } - ColorAnimation { - target: greySheet - duration: animationTime + SequentialAnimation { + ScriptAction { scriptName: "preHidden" } + PropertyAnimation { + target: greySheet + property: "opacity" + duration: animationTime + } + ScriptAction { scriptName: "postHidden" } } - ScriptAction { scriptName: "postHidden" } } ] diff --git a/programs/light_client/qml/Scaling.qml b/programs/light_client/qml/Scaling.qml new file mode 100644 index 00000000..52ab128e --- /dev/null +++ b/programs/light_client/qml/Scaling.qml @@ -0,0 +1,12 @@ +pragma Singleton +import QtQuick 2.5 +import QtQuick.Window 2.2 + +Item { + function mm(millimeters) { + return Screen.pixelDensity * millimeters + } + function cm(centimeters) { + return mm(centimeters * 10) + } +} diff --git a/programs/light_client/qml/TransferForm.qml b/programs/light_client/qml/TransferForm.qml index fdbc1515..deab6255 100644 --- a/programs/light_client/qml/TransferForm.qml +++ b/programs/light_client/qml/TransferForm.qml @@ -1,8 +1,60 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Dialogs 1.2 +import QtQuick.Layouts 1.2 + +import "." +import "jdenticon/jdenticon-1.0.1.min.js" as Jdenticon Rectangle { + anchors.fill: parent + Component.onCompleted: console.log("Made a transfer form") Component.onDestruction: console.log("Destroyed a transfer form") + + Column { + anchors.centerIn: parent + + RowLayout { + Canvas { + id: identicon + width: Scaling.cm(2) + height: Scaling.cm(2) + contextType: "2d" + + onPaint: { + if (nameField.text) + Jdenticon.draw(identicon, nameField.text) + else { + var context = identicon.context + context.reset() + var draw_circle = function(context, x, y, radius) { + context.beginPath() + context.arc(x, y, radius, 0, 2 * Math.PI, false) + context.fillStyle = "rgba(0, 0, 0, 0.1)" + context.fill() + } + var size = Math.min(identicon.height, identicon.width) + var centerX = size / 2 + var centerY = size / 2 + var radius = size/15 + draw_circle(context, centerX, centerY, radius) + draw_circle(context, 2*radius, 2*radius, radius) + draw_circle(context, centerX, 2*radius, radius) + draw_circle(context, size - 2*radius, 2*radius, radius) + draw_circle(context, size - 2*radius, centerY, radius) + draw_circle(context, size - 2*radius, size - 2*radius, radius) + draw_circle(context, centerX, size - 2*radius, radius) + draw_circle(context, 2*radius, size - 2*radius, radius) + draw_circle(context, 2*radius, centerY, radius) + } + } + } + TextField { + id: nameField + Layout.fillWidth: true + onTextChanged: identicon.requestPaint() + } + } + } } diff --git a/programs/light_client/qml/jdenticon/jdenticon-1.0.1.min.js b/programs/light_client/qml/jdenticon/jdenticon-1.0.1.min.js new file mode 100755 index 00000000..dd0b1065 --- /dev/null +++ b/programs/light_client/qml/jdenticon/jdenticon-1.0.1.min.js @@ -0,0 +1,18 @@ +// Jdenticon 1.0.1 | jdenticon.com | zlib licensed | (c) 2014-2015 Daniel Mester Pirttijärvi +/*Alteration to official Jdenticon code: make jdenticon() a variable in this context instead of a property of window*/ +var jdenticon=function(){function h(){}function n(b,a,c,d){this.h=b;this.i=a;this.k=c;this.f=d}function u(b,a){this.e=b;this.g=a||n.q;b.beginPath()}function r(b,a,c){var d=(b="string"===typeof b?document.querySelector(b):b).getContext("2d");c=Math.min(b.width)*(1-2*(c===t?.08:c));d.save();d.clearRect(0,0,b.width,b.height);d.translate(0|(b.width-c)/2,0|(b.height-c)/2);v(d,a||b.getAttribute("data-jdenticon-hash"),c);d.restore()}function v(b,a,c){function d(b,c,d,e,g){e=e?parseInt(a.charAt(e),16): +0;c=c[parseInt(a.charAt(d),16)%c.length];var f;for(d=0;dc)throw Error("Jdenticon cannot render identicons smaller than 30 pixels.");if(!/^[0-9a-f]{10,}$/i.test(a))throw Error("Invalid hash passed to Jdenticon.");c|=0;for(var p=2*(0|c/8),f=parseInt(a.substr(-7),16)/268435455,m= +[h.o(76,76,76),h.m(f,.6),h.o(230,230,230),h.m(f,.8),h.n(f,.4)],l=[],k,f=0;3>f;f++){k=parseInt(a.charAt(8+f),16)%m.length;if(e([0,4])||e([2,3]))k=1;l.push(k)}b.clearRect(0,0,c,c);g(0);d(b,w,2,3,[[1,0],[2,0],[2,3],[1,3],[0,1],[3,1],[3,2],[0,2]]);g(1);d(b,w,4,5,[[0,0],[3,0],[3,3],[0,3]]);g(2);d(b,y,1,null,[[1,1],[2,1],[2,2],[1,2]])}function q(){for(var b,a=document.getElementsByTagName("canvas"),c=0;cmain.qml TransferForm.qml FormBox.qml + jdenticon/jdenticon-1.0.1.min.js + Scaling.qml diff --git a/programs/light_client/qml/qmldir b/programs/light_client/qml/qmldir new file mode 100644 index 00000000..79c7d88a --- /dev/null +++ b/programs/light_client/qml/qmldir @@ -0,0 +1 @@ +singleton Scaling Scaling.qml