diff --git a/.gitignore b/.gitignore
index c31fbcdc2c5c74f2ed9dbffeb26b27e630361cb8..dad43d5c42eb92672d82f08c4f81dd3c08b1647c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,4 +4,6 @@
node_modules/
platforms/
plugins/
-.idea/
\ No newline at end of file
+.idea/
+cesium.iml
+pom.xml
\ No newline at end of file
diff --git a/www/index.html b/www/index.html
index 32d7cbdc6c7c675a71d109b3c149f97caec71c0b..eb0d15a85c8a49e7e65cfd2ec1d6fa6a6cc2a096 100644
--- a/www/index.html
+++ b/www/index.html
@@ -29,13 +29,27 @@
<!-- cordova script (this will be a 404 during development) -->
<script src="cordova.js"></script>
- <!-- your app's js -->
- <script src="js/app.js"></script>
+ <!-- services -->
+ <script src="js/services/crypto-services.js"></script>
+ <script src="js/services/utils-services.js"></script>
+ <script src="js/services/wallet-services.js"></script>
+ <script src="js/services/bma-services.js"></script>
+
+ <!-- entities -->
<script src="js/entity/peer.js"></script>
- <script src="js/controllers/peer_controller.js"></script>
- <script src="js/home-controller.js"></script>
- <script src="js/explore-controller.js"></script>
+
+ <!-- controllers -->
+ <script src="js/controllers/home-controllers.js"></script>
+ <script src="js/controllers/wot-controllers.js"></script>
+ <script src="js/controllers/peer-controllers.js"></script>
+ <script src="js/controllers/currency-controllers.js"></script>
+ <script src="js/controllers/wallet-controllers.js"></script>
+
+ <!-- App -->
+ <script src="js/app.js"></script>
<script src="js/services.js"></script>
+ <script src="js/controllers.js"></script>
+
</head>
<body ng-app="cesium">
<ion-nav-view></ion-nav-view>
diff --git a/www/js/app.js b/www/js/app.js
index 856f0649a19f118db2debccea43b4ba7d388ca74..bc9ab915a74ef33df27aa93732c1356a82325485 100644
--- a/www/js/app.js
+++ b/www/js/app.js
@@ -58,87 +58,5 @@ angular.module('cesium', ['ionic', 'cesium.controllers'])
});
})
-.config(function($stateProvider, $urlRouterProvider) {
- $stateProvider
- .state('app', {
- url: "/app",
- abstract: true,
- templateUrl: "templates/menu.html",
- controller: 'HomeCtrl'
- })
-
- .state('app.home', {
- url: "/home",
- views: {
- 'menuContent': {
- templateUrl: "templates/home.html",
- controller: 'HomeCtrl'
- }
- }
- })
-
- .state('app.explore_currency', {
- url: "/home/explore",
- views: {
- 'menuContent': {
- templateUrl: "templates/explore/explore_currency.html",
- controller: 'CurrenciesCtrl'
- }
- }
- })
-
- .state('app.explore_tabs', {
- url: "/currency",
- views: {
- 'menuContent': {
- templateUrl: "templates/explore/explore_tabs.html",
- controller: 'ExploreCtrl'
- }
- }
- })
-
- .state('app.view_peer', {
- url: "/peer/:server",
- views: {
- 'menuContent': {
- templateUrl: "templates/explore/view_peer.html",
- controller: 'PeerCtrl'
- }
- }
- })
-
- .state('app.view_identity', {
- url: "/wot/:pub",
- views: {
- 'menuContent': {
- templateUrl: "templates/wot/view_identity.html",
- controller: 'IdentityCtrl'
- }
- }
- })
-
- .state('app.view_wallet', {
- url: "/wallet",
- views: {
- 'menuContent': {
- templateUrl: "templates/account/view_wallet.html",
- controller: 'WalletCtrl'
- }
- }
- })
-
- .state('app.view_transfer', {
- url: "/transfer/:pubkey/:uid",
- views: {
- 'menuContent': {
- templateUrl: "templates/account/view_transfer.html",
- controller: 'TransferCtrl'
- }
- }
- });
-
- // if none of the above states are matched, use this as the fallback
- $urlRouterProvider.otherwise('/app/home');
-})
;
diff --git a/www/js/controllers.js b/www/js/controllers.js
new file mode 100644
index 0000000000000000000000000000000000000000..0d217aaafe891f05ccaee098e1a8cec11a7da1bb
--- /dev/null
+++ b/www/js/controllers.js
@@ -0,0 +1,16 @@
+
+angular.module('cesium.controllers', [
+ 'cesium.home.controllers',
+ 'cesium.wallet.controllers',
+ 'cesium.currency.controllers',
+ 'cesium.wot.controllers'
+ ])
+
+ .config(function($httpProvider) {
+ //Enable cross domain calls
+ $httpProvider.defaults.useXDomain = true;
+
+ //Remove the header used to identify ajax call that would prevent CORS from working
+ delete $httpProvider.defaults.headers.common['X-Requested-With'];
+ })
+;
diff --git a/www/js/controllers/currency-controllers.js b/www/js/controllers/currency-controllers.js
new file mode 100644
index 0000000000000000000000000000000000000000..d8989dbaaf86b494701f1e6c22bb65e4c7aa979f
--- /dev/null
+++ b/www/js/controllers/currency-controllers.js
@@ -0,0 +1,305 @@
+
+angular.module('cesium.currency.controllers', ['cesium.services'])
+
+.config(function($stateProvider, $urlRouterProvider) {
+ $stateProvider
+
+ .state('app.explore_currency', {
+ url: "/home/explore",
+ views: {
+ 'menuContent': {
+ templateUrl: "templates/explore/explore_currency.html",
+ controller: 'CurrenciesCtrl'
+ }
+ }
+ })
+
+ .state('app.explore_tabs', {
+ url: "/currency",
+ views: {
+ 'menuContent': {
+ templateUrl: "templates/explore/explore_tabs.html",
+ controller: 'ExploreCtrl'
+ }
+ }
+ })
+
+ .state('app.view_peer', {
+ url: "/peer/:server",
+ views: {
+ 'menuContent': {
+ templateUrl: "templates/explore/view_peer.html",
+ controller: 'PeerCtrl'
+ }
+ }
+ })
+})
+
+.controller('CurrenciesCtrl', CurrenciesController)
+
+.controller('ExploreCtrl', ExploreController)
+
+.controller('PeerCtrl', PeerController)
+
+;
+
+function CurrenciesController($scope, $state) {
+
+ $scope.selectedCurrency = '';
+ $scope.knownCurrencies = ['meta_brouzouf'];
+
+ // Called to navigate to the main app
+ $scope.selectCurrency = function(currency) {
+ $scope.selectedCurrency = currency;
+ $state.go('app.explore_tabs');
+ };
+}
+
+function ExploreController($scope, $rootScope, $state, BMA, $q, UIUtils, $interval, $timeout) {
+
+ var USE_RELATIVE_DEFAULT = true;
+
+ CurrenciesController.call(this, $scope, $state);
+ WotLookupController.call(this, $scope, BMA, $state);
+ PeersController.call(this, $scope, $rootScope, BMA, UIUtils, $q, $interval, $timeout);
+
+ $scope.accountTypeMember = null;
+ $scope.accounts = [];
+ $scope.search = { text: '', results: {} };
+ $scope.knownCurrencies = ['meta_brouzouf'];
+ $scope.formData = { useRelative: false };
+ $scope.knownBlocks = [];
+ $scope.entered = false;
+
+ $scope.$on('$ionicView.enter', function(e, $state) {
+ if (!$scope.entered) {
+ $scope.entered = true;
+ $scope.startListeningOnSocket();
+ }
+ $timeout(function() {
+ if ((!$scope.search.peers || $scope.search.peers.length == 0) && $scope.search.lookingForPeers){
+ $scope.updateExploreView();
+ }
+ }, 2000);
+ });
+
+ $scope.startListeningOnSocket = function() {
+
+ // Currency OK
+ BMA.websocket.block().on('block', function(block) {
+ var theFPR = fpr(block);
+ if ($scope.knownBlocks.indexOf(theFPR) === -1) {
+ $scope.knownBlocks.push(theFPR);
+ // We wait 2s when a new block is received, just to wait for network propagation
+ var wait = $scope.knownBlocks.length === 1 ? 0 : 2000;
+ $timeout(function() {
+ $scope.updateExploreView();
+ }, wait);
+ }
+ });
+ BMA.websocket.peer().on('peer', function(peer) {
+ console.log(peer);
+ });
+ };
+
+ $scope.$watch('formData.useRelative', function() {
+ if ($scope.formData.useRelative) {
+ $scope.M = $scope.M / $scope.currentUD;
+ $scope.MoverN = $scope.MoverN / $scope.currentUD;
+ $scope.UD = $scope.UD / $scope.currentUD;
+ $scope.unit = 'universal_dividend';
+ $scope.udUnit = $scope.baseUnit;
+ } else {
+ $scope.M = $scope.M * $scope.currentUD;
+ $scope.MoverN = $scope.MoverN * $scope.currentUD;
+ $scope.UD = $scope.UD * $scope.currentUD;
+ $scope.unit = $scope.baseUnit;
+ $scope.udUnit = '';
+ }
+ }, true);
+
+ $scope.doUpdate = function() {
+ $scope.updateExploreView();
+ };
+
+ $scope.updateExploreView = function() {
+
+ UIUtils.loading.show();
+ $scope.formData.useRelative = false;
+
+ $q.all([
+
+ // Get the currency parameters
+ BMA.currency.parameters()
+ .then(function(json){
+ $scope.c = json.c;
+ $scope.baseUnit = json.currency;
+ $scope.unit = json.currency;
+ }),
+
+ // Get the current block informations
+ BMA.blockchain.current()
+ .then(function(block){
+ $scope.M = block.monetaryMass;
+ $scope.N = block.membersCount;
+ $scope.time = moment(block.medianTime*1000).format('YYYY-MM-DD HH:mm');
+ $scope.difficulty = block.powMin;
+ }),
+
+ // Get the UD informations
+ BMA.blockchain.stats.ud()
+ .then(function(res){
+ if (res.result.blocks.length) {
+ var lastBlockWithUD = res.result.blocks[res.result.blocks.length - 1];
+ return BMA.blockchain.block({ block: lastBlockWithUD })
+ .then(function(block){
+ $scope.currentUD = block.dividend;
+ $scope.UD = block.dividend;
+ $scope.Nprev = block.membersCount;
+ });
+ }
+ })
+ ])
+
+ // Done
+ .then(function(){
+ $scope.M = $scope.M - $scope.UD*$scope.Nprev;
+ $scope.MoverN = $scope.M / $scope.Nprev;
+ $scope.cactual = 100 * $scope.UD / $scope.MoverN;
+ $scope.formData.useRelative = USE_RELATIVE_DEFAULT;
+ UIUtils.loading.hide();
+ })
+ .catch(function(err) {
+ console.error('>>>>>>>' , err);
+ UIUtils.alert.error('Could not fetch informations from remote uCoin node.');
+ UIUtils.loading.hide();
+ })
+ .then(function(){
+ // Network
+ $scope.searchPeers();
+ });
+ };
+}
+
+
+function PeersController($scope, $rootScope, BMA, UIUtils, $q, $interval, $timeout) {
+
+ var newPeers = [], interval, lookingForPeers;
+ $scope.search.lookingForPeers = false;
+ $scope.search.peers = [];
+
+ $scope.overviewPeers = function() {
+ var currents = {}, block;
+ for (var i = 0, len = $scope.search.peers.length; i < len; i++) {
+ block = $scope.search.peers[i].current;
+ if (block) {
+ var bid = fpr(block);
+ currents[bid] = currents[bid] || 0;
+ currents[bid]++;
+ }
+ }
+ var fprs = _.keys(currents).map(function(key) {
+ return { fpr: key, qty: currents[key] };
+ });
+ var best = _.max(fprs, function(obj) {
+ return obj.qty;
+ });
+ var p;
+ for (var j = 0, len2 = $scope.search.peers.length; j < len2; j++) {
+ p = $scope.search.peers[j];
+ p.hasMainConsensusBlock = fpr(p.current) == best.fpr;
+ p.hasConsensusBlock = !p.hasMainConsensusBlock && currents[fpr(p.current)] > 1;
+ }
+ $scope.search.peers = _.uniq($scope.search.peers, false, function(peer) {
+ return peer.pubkey;
+ });
+ $scope.search.peers = _.sortBy($scope.search.peers, function(p) {
+ var score = 1
+ + 10000 * (p.online ? 1 : 0)
+ + 1000 * (p.hasMainConsensusBlock ? 1 : 0) +
+ + 100 * (p.uid ? 1 : 0);
+ return -score;
+ });
+ };
+
+ $scope.searchPeers = function() {
+
+ if (interval) {
+ $interval.cancel(interval);
+ }
+
+ interval = $interval(function() {
+ if (newPeers.length) {
+ $scope.search.peers = $scope.search.peers.concat(newPeers.splice(0));
+ $scope.overviewPeers();
+ } else if (lookingForPeers && !$scope.search.lookingForPeers) {
+ // The peer lookup endend, we can make a clean final report
+ $timeout(function(){
+ lookingForPeers = false;
+ $scope.overviewPeers();
+ }, 1000);
+ }
+ }, 1000);
+
+ var known = {};
+ $rootScope.members = [];
+ $scope.search.peers = [];
+ $scope.search.lookingForPeers = true;
+ lookingForPeers = true;
+ return BMA.network.peering.peers({ leaves: true })
+ .then(function(res){
+ return BMA.wot.members()
+ .then(function(json){
+ $rootScope.members = json.results;
+ return res;
+ });
+ })
+ .then(function(res){
+ return $q.all(res.leaves.map(function(leaf) {
+ return BMA.network.peering.peers({ leaf: leaf })
+ .then(function(subres){
+ var peer = subres.leaf.value;
+ if (peer) {
+ peer = new Peer(peer);
+ // Test each peer only once
+ if (!known[peer.getURL()]) {
+ peer.dns = peer.getDns();
+ peer.blockNumber = peer.block.replace(/-.+$/, '');
+ var member = _.findWhere($rootScope.members, { pubkey: peer.pubkey });
+ peer.uid = member && member.uid;
+ newPeers.push(peer);
+ var node = BMA.instance(peer.getURL());
+ return node.blockchain.current()
+ .then(function(block){
+ peer.current = block;
+ peer.online = true;
+ peer.server = peer.getURL();
+ if ($scope.knownBlocks.indexOf(fpr(block)) === -1) {
+ $scope.knownBlocks.push(fpr(block));
+ }
+ })
+ .catch(function(err) {
+ })
+ }
+ }
+ })
+ }))
+ .then(function(){
+ $scope.search.lookingForPeers = false;
+ })
+ })
+ .catch(function(err) {
+ //console.log(err);
+ //UIUtils.alert.error('Could get peers from remote uCoin node.');
+ //$scope.search.lookingForPeers = false;
+ });
+ };
+
+ $scope.viewPeer = function() {
+
+ };
+}
+
+function fpr(block) {
+ return block && [block.number, block.hash].join('-');
+}
diff --git a/www/js/controllers/home-controllers.js b/www/js/controllers/home-controllers.js
new file mode 100644
index 0000000000000000000000000000000000000000..5a55420b159970654e113c708afe20daced71b58
--- /dev/null
+++ b/www/js/controllers/home-controllers.js
@@ -0,0 +1,263 @@
+
+angular.module('cesium.home.controllers', ['cesium.services'])
+
+ .config(function($httpProvider) {
+ //Enable cross domain calls
+ $httpProvider.defaults.useXDomain = true;
+
+ //Remove the header used to identify ajax call that would prevent CORS from working
+ delete $httpProvider.defaults.headers.common['X-Requested-With'];
+ })
+
+ .controller('HomeCtrl', HomeController)
+
+ .config(function($stateProvider, $urlRouterProvider) {
+ $stateProvider
+
+ .state('app', {
+ url: "/app",
+ abstract: true,
+ templateUrl: "templates/menu.html",
+ controller: 'HomeCtrl'
+ })
+
+ .state('app.home', {
+ url: "/home",
+ views: {
+ 'menuContent': {
+ templateUrl: "templates/home.html",
+ controller: 'HomeCtrl'
+ }
+ }
+ })
+
+ // if none of the above states are matched, use this as the fallback
+ $urlRouterProvider.otherwise('/app/home');
+ })
+;
+
+function LoginController($scope, $ionicModal, Wallet, CryptoUtils, UIUtils, $q, $state, $timeout, $ionicSideMenuDelegate) {
+ // Form data for the login modal
+ $scope.loginData = {
+ username: null,
+ password: null
+ };
+
+ // Login modal
+ $scope.loginModal = "undefined";
+
+ // Create the login modal that we will use later
+ $ionicModal.fromTemplateUrl('templates/login.html', {
+ scope: $scope,
+ focusFirstInput: true
+ }).then(function(modal) {
+ $scope.loginModal = modal;
+ $scope.loginModal.hide();
+ });
+
+ // Open login modal
+ $scope.login = function(callback) {
+ if ($scope.loginModal != "undefined" && $scope.loginModal != null) {
+ $scope.loginModal.show();
+ $scope.loginData.callback = callback;
+ }
+ else{
+ $timeout($scope.login, 2000);
+ }
+ };
+
+ // Login and load wallet
+ $scope.loadWallet = function() {
+ return $q(function(resolve, reject){
+ if (!Wallet.isLogin()) {
+ $scope.login(function() {
+ Wallet.loadData()
+ .then(function(walletData){
+ resolve(walletData);
+ })
+ .catch(function(err) {
+ console.error('>>>>>>>' , err);
+ UIUtils.alert.error('Your browser is not compatible with cryptographic features.');
+ UIUtils.loading.hide();
+ reject(err);
+ });
+ });
+ }
+ else if (!Wallet.data.loaded) {
+ Wallet.loadData()
+ .then(function(walletData){
+ resolve(walletData);
+ })
+ .catch(function(err) {
+ console.error('>>>>>>>' , err);
+ UIUtils.alert.error('Could not fetch wallet data from remote uCoin node.');
+ UIUtils.loading.hide();
+ reject(err);
+ });
+ }
+ else {
+ resolve(Wallet.data);
+ }
+ });
+ };
+
+ // Triggered in the login modal to close it
+ $scope.closeLogin = function() {
+ return $scope.loginModal.hide();
+ };
+
+ // Login form submit
+ $scope.doLogin = function() {
+ $scope.closeLogin();
+ UIUtils.loading.show();
+
+ // Call wallet login
+ Wallet.login($scope.loginData.username, $scope.loginData.password)
+ .catch(function(err) {
+ $scope.loginData = {}; // Reset login data
+ UIUtils.loading.hide();
+ console.error('>>>>>>>' , err);
+ UIUtils.alert.error('Your browser is not compatible with cryptographic libraries.');
+ })
+ .then(function(){
+ UIUtils.loading.hide();
+ var callback = $scope.loginData.callback;
+ $scope.loginData = {}; // Reset login data
+ if (callback != "undefined" && callback != null) {
+ callback();
+ }
+ // Default: redirect to wallet view
+ else {
+ $state.go('app.view_wallet');
+ }
+ });
+ };
+
+ $scope.loginDataChanged = function() {
+ $scope.loginData.computing=false;
+ $scope.loginData.pubkey=null;
+ };
+
+ $scope.showLoginPubkey = function() {
+ $scope.loginData.computing=true;
+ CryptoUtils.connect($scope.loginData.username, $scope.loginData.password).then(
+ function(keypair) {
+ $scope.loginData.pubkey = CryptoUtils.util.encode_base58(keypair.signPk);
+ $scope.loginData.computing=false;
+ }
+ )
+ .catch(function(err) {
+ $scope.loginData.computing=false;
+ UIUtils.loading.hide();
+ console.error('>>>>>>>' , err);
+ UIUtils.alert.error('Your browser is not compatible with cryptographic libraries.');
+ });
+ };
+
+ // Logout
+ $scope.logout = function() {
+ UIUtils.loading.show();
+ Wallet.logout().then(
+ function() {
+ UIUtils.loading.hide();
+ $ionicSideMenuDelegate.toggleLeft();
+ $state.go('app.home');
+ }
+ );
+ };
+
+ // Is connected
+ $scope.isLogged = function() {
+ return Wallet.isLogin();
+ };
+
+ // Is not connected
+ $scope.isNotLogged = function() {
+ return !Wallet.isLogin();
+ };
+}
+
+function HomeController($scope, $ionicSlideBoxDelegate, $ionicModal, $state, BMA, UIUtils, $q, $timeout, Wallet, CryptoUtils, $ionicSideMenuDelegate) {
+
+ // With the new view caching in Ionic, Controllers are only called
+ // when they are recreated or on app start, instead of every page change.
+ // To listen for when this page is active (for example, to refresh data),
+ // listen for the $ionicView.enter event:
+ //$scope.$on('$ionicView.enter', function(e) {
+ //});
+
+ //CurrenciesController.call(this, $scope, $state);
+ //LookupController.call(this, $scope, BMA, $state);
+ LoginController.call(this, $scope, $ionicModal, Wallet, CryptoUtils, UIUtils, $q, $state, $timeout, $ionicSideMenuDelegate);
+
+ $scope.accountTypeMember = null;
+ $scope.accounts = [];
+ $scope.search = { text: '', results: {} };
+ $scope.knownCurrencies = ['meta_brouzouf'];
+
+ // Called to navigate to the main app
+ $scope.cancel = function() {
+ $scope.modal.hide();
+ $timeout(function(){
+ $scope.selectedCurrency = '';
+ $scope.accountTypeMember = null;
+ $scope.search.text = '';
+ $scope.search.results = [];
+ }, 200);
+ };
+
+ $scope.$on('currencySelected', function() {
+ $ionicSlideBoxDelegate.slide(1);
+ });
+
+ $scope.selectAccountTypeMember = function(bool) {
+ $scope.accountTypeMember = bool;
+ $ionicSlideBoxDelegate.slide(2);
+ };
+
+ $scope.next = function() {
+ $ionicSlideBoxDelegate.next();
+ };
+ $scope.previous = function() {
+ $ionicSlideBoxDelegate.previous();
+ };
+
+ // Called each time the slide changes
+ $scope.slideChanged = function(index) {
+ $scope.slideIndex = index;
+ $scope.nextStep = $scope.slideIndex == 2 ? 'Start using MyApp' : 'Next';
+ };
+
+ $scope.addAccount = function() {
+ $scope.modal.show();
+ $scope.slideChanged(0);
+ $ionicSlideBoxDelegate.slide(0);
+ $ionicSlideBoxDelegate.enableSlide(false);
+ // TODO: remove default
+ //$timeout(function() {
+ // $scope.selectedCurrency = $scope.knownCurrencies[0];
+ // $scope.accountTypeMember = true;
+ // $scope.searchChanged();
+ // $scope.search.text = 'cgeek';
+ // $ionicSlideBoxDelegate.next();
+ // $ionicSlideBoxDelegate.next();
+ //}, 300);
+ };
+
+ // Create the account modal that we will use later
+ $ionicModal.fromTemplateUrl('templates/account/new_account.html', {
+ scope: $scope
+ }).then(function(modal) {
+ $scope.modal = modal;
+ $scope.modal.hide();
+ // TODO: remove auto add account when done
+ //$timeout(function() {
+ // $scope.addAccount();
+ //}, 400);
+ });
+
+ $scope.selectCurrency = function(currency) {
+ $scope.selectedCurrency = currency;
+ $scope.next();
+ }
+}
diff --git a/www/js/controllers/peer_controller.js b/www/js/controllers/peer-controllers.js
similarity index 100%
rename from www/js/controllers/peer_controller.js
rename to www/js/controllers/peer-controllers.js
diff --git a/www/js/controllers/wallet-controllers.js b/www/js/controllers/wallet-controllers.js
new file mode 100644
index 0000000000000000000000000000000000000000..b7e91e9b6871c4c46b9c2b221faec8f9c3133acb
--- /dev/null
+++ b/www/js/controllers/wallet-controllers.js
@@ -0,0 +1,225 @@
+
+angular.module('cesium.wallet.controllers', ['cesium.services', 'cesium.currency.controllers'])
+
+ .config(function($stateProvider, $urlRouterProvider) {
+ $stateProvider
+
+ .state('app.view_wallet', {
+ url: "/wallet",
+ views: {
+ 'menuContent': {
+ templateUrl: "templates/account/view_wallet.html",
+ controller: 'WalletCtrl'
+ }
+ }
+ })
+
+ .state('app.view_transfer_uid', {
+ url: "/transfer/:pubkey/:uid",
+ views: {
+ 'menuContent': {
+ templateUrl: "templates/account/view_transfer.html",
+ controller: 'TransferCtrl'
+ }
+ }
+ })
+
+ .state('app.view_transfer_pubkey', {
+ url: "/transfer/:pubkey",
+ views: {
+ 'menuContent': {
+ templateUrl: "templates/account/view_transfer.html",
+ controller: 'TransferCtrl'
+ }
+ }
+ })
+ ;
+ })
+
+ .controller('WalletCtrl', WalletController)
+
+ .controller('TransferCtrl', TransferController)
+;
+
+function WalletController($scope, $state, $q, $ionicPopup, UIUtils, Wallet) {
+
+ $scope.walletData = {};
+ $scope.convertedBalance = 0;
+ $scope.hasCredit = false;
+ $scope.isMember = false;
+
+ $scope.$on('$ionicView.enter', function(e, $state) {
+ $scope.loadWallet()
+ .then(function(wallet) {
+ $scope.updateWalletView(wallet);
+ });
+ });
+
+ $scope.refreshConvertedBalance = function() {
+ if ($scope.walletData.useRelative) {
+ $scope.convertedBalance = $scope.walletData.balance / $scope.walletData.currentUD;
+ $scope.unit = 'universal_dividend';
+ $scope.udUnit = $scope.walletData.currency;
+ } else {
+ $scope.convertedBalance = $scope.walletData.balance;
+ $scope.unit = $scope.walletData.currency;
+ $scope.udUnit = '';
+ }
+ };
+ $scope.$watch('walletData.useRelative', $scope.refreshConvertedBalance, true);
+ $scope.$watch('walletData.balance', $scope.refreshConvertedBalance, true);
+
+ // Update view
+ $scope.updateWalletView = function(wallet) {
+ $scope.walletData = wallet;
+ $scope.hasCredit = ($scope.walletData.balance != "undefined" && $scope.walletData.balance > 0);
+ $scope.isMember = ($scope.walletData.requirements != "undefined" && $scope.walletData.requirements != null
+ && $scope.walletData.requirements.uid != "undefined" && $scope.walletData.requirements.uid != null);
+ };
+
+ // Has credit
+ $scope.hasCredit= function() {
+ return $scope.balance > 0;
+ };
+
+ // Transfer click
+ $scope.transfer= function() {
+ $state.go('app.view_transfer');
+ };
+
+ // Self cert
+ $scope.self= function() {
+
+ // Choose UID popup
+ $ionicPopup.show({
+ template: '<input type="text" ng-model="walletData.uid">',
+ title: 'Enter a pseudo',
+ subTitle: 'A pseudo is need to let other member find you.',
+ scope: $scope,
+ buttons: [
+ { text: 'Cancel' },
+ {
+ text: '<b>Send</b>',
+ type: 'button-positive',
+ onTap: function(e) {
+ if (!$scope.walletData.uid) {
+ //don't allow the user to close unless he enters a uid
+ e.preventDefault();
+ } else {
+ // TODO : check if not already used
+ return $scope.walletData.uid;
+ }
+ }
+ }
+ ]
+ })
+ .then(function(uid) {
+ UIUtils.loading.show();
+ Wallet.self(uid)
+ .then(function() {
+ UIUtils.loading.hide();
+ })
+ .catch(UIUtils.onError('Could not send self certification'));
+ });
+
+ };
+}
+
+function TransferController($scope, $ionicModal, $state, $ionicHistory, BMA, Wallet, UIUtils) {
+
+ $scope.walletData = {};
+ $scope.formData = {
+ destPub: null,
+ amount: null,
+ comments: null
+ };
+ $scope.dest = null;
+ $scope.udAmount = null;
+
+ WotLookupController.call(this, $scope, BMA, $state);
+
+ $scope.$on('$ionicView.enter', function(e, $state) {
+ if ($state.stateParams != null
+ && $state.stateParams.pubkey != null
+ && $state.stateParams.pubkey != "undefined") {
+ $scope.destPub = $state.stateParams.pubkey;
+ if ($state.stateParams.uid != null
+ && $state.stateParams.uid != "undefined") {
+ $scope.dest = $state.stateParams.uid;
+ }
+ else {
+ $scope.dest = $scope.destPub;
+ }
+ }
+
+ // Login and load wallet
+ $scope.loadWallet()
+ .then(function(walletData) {
+ $scope.walletData = walletData;
+ $scope.onUseRelativeChanged();
+ });
+ });
+
+ // When chaing use relative UD
+ $scope.onUseRelativeChanged = function() {
+ if ($scope.walletData.useRelative) {
+ $scope.udAmount = $scope.amount * $scope.walletData.currentUD;
+ $scope.unit = 'universal_dividend';
+ $scope.udUnit = $scope.walletData.currency;
+ } else {
+ $scope.formData.amount = ($scope.formData.amount != "undefined" && $scope.formData.amount != null)
+ ? Math.floor(parseFloat($scope.formData.amount.replace(new RegExp('[,]'), '.')))
+ : null;
+ $scope.udAmount = $scope.amount / $scope.walletData.currentUD;
+ $scope.unit = $scope.walletData.currency;
+ $scope.udUnit = '';
+ }
+ };
+ $scope.$watch('walletData.useRelative', $scope.onUseRelativeChanged, true);
+
+ $ionicModal.fromTemplateUrl('templates/wot/modal_lookup.html', {
+ scope: $scope,
+ focusFirstInput: true
+ }).then(function(modal) {
+ $scope.lookupModal = modal;
+ $scope.lookupModal.hide();
+ });
+
+ $scope.openSearch = function() {
+ $scope.lookupModal.show();
+ }
+
+ $scope.doTransfer = function() {
+ UIUtils.loading.show();
+
+ var amount = $scope.formData.amount;
+ if ($scope.walletData.useRelative
+ && amount != "undefined"
+ && amount != null) {
+ amount = $scope.walletData.currentUD
+ * amount.replace(new RegExp('[.,]'), '.');
+ }
+
+ Wallet.transfer($scope.formData.destPub, amount, $scope.formData.comments)
+ .then(function() {
+ UIUtils.loading.hide();
+ $ionicHistory.goBack()
+ })
+ .catch(UIUtils.onError('Could not send transaction'));
+ };
+
+ $scope.closeLookup = function() {
+ $scope.lookupModal.hide();
+ }
+
+ $scope.doSelectIdentity = function(pub, uid) {
+ if (uid != "undefined" && uid != null) {
+ $scope.dest = uid;
+ }
+ else {
+ $scope.dest = uid;
+ }
+ $scope.formData.destPub = pub;
+ $scope.lookupModal.hide();
+ }
+}
diff --git a/www/js/controllers/wot-controllers.js b/www/js/controllers/wot-controllers.js
new file mode 100644
index 0000000000000000000000000000000000000000..6b6c7058d3f1d6ae22d9a02c6b0e9588b2820ef5
--- /dev/null
+++ b/www/js/controllers/wot-controllers.js
@@ -0,0 +1,109 @@
+angular.module('cesium.wot.controllers', ['cesium.services'])
+
+ .config(function($stateProvider, $urlRouterProvider) {
+ $stateProvider
+
+ .state('app.view_identity', {
+ url: "/wot/:pub",
+ views: {
+ 'menuContent': {
+ templateUrl: "templates/wot/view_identity.html",
+ controller: 'IdentityCtrl'
+ }
+ }
+ })
+ })
+
+ .controller('IdentityCtrl', IdentityController)
+
+ .controller('WotLookupCtrl', WotLookupController)
+;
+
+function WotLookupController($scope, BMA, $state) {
+
+ $scope.searchChanged = function() {
+ $scope.search.text = $scope.search.text.toLowerCase();
+ if ($scope.search.text.length > 1) {
+ $scope.search.looking = true;
+ return BMA.wot.lookup({ search: $scope.search.text })
+ .then(function(res){
+ $scope.search.looking = false;
+ $scope.search.results = res.results.reduce(function(idties, res) {
+ return idties.concat(res.uids.reduce(function(uids, idty) {
+ return uids.concat({
+ uid: idty.uid,
+ pub: res.pubkey,
+ sigDate: idty.meta.timestamp
+ })
+ }, []));
+ }, []);
+ })
+ .catch(function() {
+ $scope.search.looking = false;
+ $scope.search.results = [];
+ });
+ }
+ else {
+ $scope.search.results = [];
+ }
+ };
+
+ $scope.doSelectIdentity = function(pub, uid) {
+ $state.go('app.view_identity', {pub: pub});
+ };
+}
+
+function IdentityController($scope, $state, BMA, Wallet, UIUtils, $q) {
+
+ $scope.identity = {};
+ $scope.hasSelf = false;
+
+ $scope.$on('$ionicView.enter', function(e, $state) {
+ $scope.loadIdentity($state.stateParams.pub);
+ });
+
+ $scope.loadIdentity = function(pub) {
+ UIUtils.loading.show();
+ BMA.wot.lookup({ search: pub })
+ .then(function(res){
+ $scope.identity = res.results.reduce(function(idties, res) {
+ return idties.concat(res.uids.reduce(function(uids, idty) {
+ return uids.concat({
+ uid: idty.uid,
+ pub: res.pubkey,
+ sigDate: idty.meta.timestamp,
+ sig: idty.self
+ })
+ }, []));
+ }, [])[0];
+ $scope.hasSelf = ($scope.identity.uid && $scope.identity.sigDate && $scope.identity.sig);
+ UIUtils.loading.hide();
+ })
+ .catch(UIUtils.onError('Could not load identity'));
+ };
+
+ $scope.signIdentity = function(identity) {
+ $scope.loadWallet()
+ .then(function(walletData) {
+ UIUtils.loading.show();
+ Wallet.sign($scope.identity.uid,
+ $scope.identity.pub,
+ $scope.identity.sigDate,
+ $scope.identity.sig)
+ .then(function() {
+ UIUtils.loading.hide();
+ UIUtils.alertInfo('Identity successfully signed');
+ })
+ .catch(UIUtils.onError('Could not certify identity'));
+ })
+ .catch(UIUtils.onError('Error while login'));
+ };
+
+ // Transfer click
+ $scope.transfer = function() {
+ $state.go('app.view_transfer_uid', {
+ pubkey: $scope.identity.pubkey,
+ uid: $scope.identity.uid,
+ });
+ };
+}
\ No newline at end of file
diff --git a/www/js/explore-controller.js b/www/js/explore-controller.js
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/www/js/home-controller.js b/www/js/home-controller.js
deleted file mode 100644
index b9f1ebea9f2bb48a316729001bca1c1ba192f70e..0000000000000000000000000000000000000000
--- a/www/js/home-controller.js
+++ /dev/null
@@ -1,805 +0,0 @@
-
-angular.module('cesium.controllers', ['cesium.services'])
-
- .config(function($httpProvider) {
- //Enable cross domain calls
- $httpProvider.defaults.useXDomain = true;
-
- //Remove the header used to identify ajax call that would prevent CORS from working
- delete $httpProvider.defaults.headers.common['X-Requested-With'];
- })
-
- .controller('HomeCtrl', HomeController)
-
- .controller('CurrenciesCtrl', CurrenciesController)
-
- .controller('ExploreCtrl', ExploreController)
-
- .controller('IdentityCtrl', IdentityController)
-
- .controller('PeerCtrl', PeerController)
-
- .controller('WalletCtrl', WalletController)
-
- .controller('TransferCtrl', TransferController)
-
-;
-
-function LoginController($scope, $ionicModal, Wallet, CryptoUtils, UIUtils, $q, $state, $timeout, $ionicSideMenuDelegate) {
- // Form data for the login modal
- $scope.loginData = {
- username: null,
- password: null
- };
-
- // Login modal
- $scope.loginModal = "undefined";
-
- // Create the login modal that we will use later
- $ionicModal.fromTemplateUrl('templates/login.html', {
- scope: $scope,
- focusFirstInput: true
- }).then(function(modal) {
- $scope.loginModal = modal;
- $scope.loginModal.hide();
- });
-
- // Open login modal
- $scope.login = function(callback) {
- if ($scope.loginModal != "undefined" && $scope.loginModal != null) {
- $scope.loginModal.show();
- $scope.loginData.callback = callback;
- }
- else{
- $timeout($scope.login, 2000);
- }
- };
-
- // Login and load wallet
- $scope.loadWallet = function() {
- return $q(function(resolve, reject){
- if (!Wallet.isLogin()) {
- $scope.login(function() {
- Wallet.loadData()
- .then(function(walletData){
- resolve(walletData);
- })
- .catch(function(err) {
- console.error('>>>>>>>' , err);
- UIUtils.alert.error('Your braower is not compatible with cryptographic features.');
- UIUtils.loading.hide();
- reject(err);
- });
- });
- }
- else if (!Wallet.data.loaded) {
- Wallet.loadData()
- .then(function(walletData){
- resolve(walletData);
- })
- .catch(function(err) {
- console.error('>>>>>>>' , err);
- UIUtils.alert.error('Could not fetch wallet informations from remote uCoin node.');
- UIUtils.loading.hide();
- reject(err);
- });
- }
- else {
- resolve(Wallet.data);
- }
- });
- };
-
- // Triggered in the login modal to close it
- $scope.closeLogin = function() {
- return $scope.loginModal.hide();
- };
-
- // Login form submit
- $scope.doLogin = function() {
- $scope.closeLogin();
- UIUtils.loading.show();
-
- // Call wallet login
- $q.all([
- Wallet.login($scope.loginData.username, $scope.loginData.password)
- .catch(function(err) {
- $scope.loginData = {}; // Reset login data
- UIUtils.loading.hide();
- console.error('>>>>>>>' , err);
- UIUtils.alert.error('Your browser is not compatible with cryptographic libraries.');
- })
- .then(function(){
- UIUtils.loading.hide();
- var callback = $scope.loginData.callback;
- $scope.loginData = {}; // Reset login data
- if (callback != "undefined" && callback != null) {
- callback();
- }
- // Default: redirect to wallet view
- else {
- $state.go('app.view_wallet');
- }
- })
- ]);
- };
-
- $scope.loginDataChanged = function() {
- $scope.loginData.computing=false;
- $scope.loginData.pubkey=null;
- };
-
- $scope.showLoginPubkey = function() {
- $scope.loginData.computing=true;
- CryptoUtils.connect($scope.loginData.username, $scope.loginData.password).then(
- function(keypair) {
- $scope.loginData.pubkey = CryptoUtils.util.encode_base58(keypair.signPk);
- $scope.loginData.computing=false;
- }
- )
- .catch(function(err) {
- $scope.loginData.computing=false;
- UIUtils.loading.hide();
- console.error('>>>>>>>' , err);
- UIUtils.alert.error('Your browser is not compatible with cryptographic libraries.');
- });
- };
-
- // Logout
- $scope.logout = function() {
- UIUtils.loading.show();
- Wallet.logout().then(
- function() {
- UIUtils.loading.hide();
- $ionicSideMenuDelegate.toggleLeft();
- $state.go('app.home');
- }
- );
- };
-
- // Is connected
- $scope.isLogged = function() {
- return Wallet.isLogin();
- };
-
- // Is not connected
- $scope.isNotLogged = function() {
- return !Wallet.isLogin();
- };
-}
-
-function CurrenciesController($scope, $state) {
-
- $scope.selectedCurrency = '';
- $scope.knownCurrencies = ['meta_brouzouf'];
-
- // Called to navigate to the main app
- $scope.selectCurrency = function(currency) {
- $scope.selectedCurrency = currency;
- $state.go('app.explore_tabs');
- };
-}
-
-function ExploreController($scope, $rootScope, $state, BMA, $q, UIUtils, $interval, $timeout) {
-
- var USE_RELATIVE_DEFAULT = true;
-
- CurrenciesController.call(this, $scope, $state);
- LookupController.call(this, $scope, BMA, $state);
- PeersController.call(this, $scope, $rootScope, BMA, UIUtils, $q, $interval, $timeout);
-
- $scope.accountTypeMember = null;
- $scope.accounts = [];
- $scope.search = { text: '', results: {} };
- $scope.knownCurrencies = ['meta_brouzouf'];
- $scope.formData = { useRelative: false };
- $scope.knownBlocks = [];
- $scope.entered = false;
-
- $scope.$on('$ionicView.enter', function(e, $state) {
- if (!$scope.entered) {
- $scope.entered = true;
- $scope.startListeningOnSocket();
- }
- $timeout(function() {
- if ((!$scope.search.peers || $scope.search.peers.length == 0) && $scope.search.lookingForPeers){
- $scope.updateExploreView();
- }
- }, 2000);
- });
-
- $scope.startListeningOnSocket = function() {
-
- // Currency OK
- BMA.websocket.block().on('block', function(block) {
- var theFPR = fpr(block);
- if ($scope.knownBlocks.indexOf(theFPR) === -1) {
- $scope.knownBlocks.push(theFPR);
- // We wait 2s when a new block is received, just to wait for network propagation
- var wait = $scope.knownBlocks.length === 1 ? 0 : 2000;
- $timeout(function() {
- $scope.updateExploreView();
- }, wait);
- }
- });
- BMA.websocket.peer().on('peer', function(peer) {
- console.log(peer);
- });
- };
-
- $scope.$watch('formData.useRelative', function() {
- if ($scope.formData.useRelative) {
- $scope.M = $scope.M / $scope.currentUD;
- $scope.MoverN = $scope.MoverN / $scope.currentUD;
- $scope.UD = $scope.UD / $scope.currentUD;
- $scope.unit = 'universal_dividend';
- $scope.udUnit = $scope.baseUnit;
- } else {
- $scope.M = $scope.M * $scope.currentUD;
- $scope.MoverN = $scope.MoverN * $scope.currentUD;
- $scope.UD = $scope.UD * $scope.currentUD;
- $scope.unit = $scope.baseUnit;
- $scope.udUnit = '';
- }
- }, true);
-
- $scope.doUpdate = function() {
- $scope.updateExploreView();
- };
-
- $scope.updateExploreView = function() {
-
- UIUtils.loading.show();
- $scope.formData.useRelative = false;
-
- $q.all([
-
- // Get the currency parameters
- BMA.currency.parameters()
- .then(function(json){
- $scope.c = json.c;
- $scope.baseUnit = json.currency;
- $scope.unit = json.currency;
- }),
-
- // Get the current block informations
- BMA.blockchain.current()
- .then(function(block){
- $scope.M = block.monetaryMass;
- $scope.N = block.membersCount;
- $scope.time = moment(block.medianTime*1000).format('YYYY-MM-DD HH:mm');
- $scope.difficulty = block.powMin;
- }),
-
- // Get the UD informations
- BMA.blockchain.stats.ud()
- .then(function(res){
- if (res.result.blocks.length) {
- var lastBlockWithUD = res.result.blocks[res.result.blocks.length - 1];
- return BMA.blockchain.block({ block: lastBlockWithUD })
- .then(function(block){
- $scope.currentUD = block.dividend;
- $scope.UD = block.dividend;
- $scope.Nprev = block.membersCount;
- });
- }
- })
- ])
-
- // Done
- .then(function(){
- $scope.M = $scope.M - $scope.UD*$scope.Nprev;
- $scope.MoverN = $scope.M / $scope.Nprev;
- $scope.cactual = 100 * $scope.UD / $scope.MoverN;
- $scope.formData.useRelative = USE_RELATIVE_DEFAULT;
- UIUtils.loading.hide();
- })
- .catch(function(err) {
- console.error('>>>>>>>' , err);
- UIUtils.alert.error('Could not fetch informations from remote uCoin node.');
- UIUtils.loading.hide();
- })
- .then(function(){
- // Network
- $scope.searchPeers();
- });
- };
-}
-
-function LookupController($scope, BMA, $state) {
-
- $scope.searchChanged = function() {
- $scope.search.text = $scope.search.text.toLowerCase();
- if ($scope.search.text.length > 1) {
- $scope.search.looking = true;
- return BMA.wot.lookup({ search: $scope.search.text })
- .then(function(res){
- $scope.search.looking = false;
- $scope.search.results = res.results.reduce(function(idties, res) {
- return idties.concat(res.uids.reduce(function(uids, idty) {
- return uids.concat({
- uid: idty.uid,
- pub: res.pubkey,
- sigDate: idty.meta.timestamp
- })
- }, []));
- }, []);
- })
- .catch(function() {
- $scope.search.looking = false;
- $scope.search.results = [];
- });
- }
- else {
- $scope.search.results = [];
- }
- };
-
- $scope.doSelectIdentity = function(pub, uid) {
- $state.go('app.view_identity', {pub: pub});
- };
-
-}
-
-function IdentityController($scope, $state, BMA, Wallet, UIUtils, $q) {
-
- $scope.$on('$ionicView.enter', function(e, $state) {
- $scope.showIdentity($state.stateParams.pub);
- });
-
- $scope.identity = {};
-
- $scope.showIdentity = function(pub) {
-
- BMA.wot.lookup({ search: pub })
- .then(function(res){
- $scope.identity = res.results.reduce(function(idties, res) {
- return idties.concat(res.uids.reduce(function(uids, idty) {
- return uids.concat({
- uid: idty.uid,
- pub: res.pubkey,
- sigDate: idty.meta.timestamp,
- sig: idty.self
- })
- }, []));
- }, [])[0];
- })
- };
-
- $scope.hasSelf = function() {
- return ($scope.identity.uid
- && $scope.identity.sigDate
- && $scope.identity.sig);
- }
-
- $scope.signIdentity = function(identity) {
- $scope.loadWallet()
- .then(function(walletData) {
- UIUtils.loading.show();
- Wallet.sign($scope.identity.uid,
- $scope.identity.pub,
- $scope.identity.sigDate,
- $scope.identity.sig)
- .then(function() {
- UIUtils.loading.hide();
- alert('Identity signed');
- })
- .catch(function(err) {
- console.error('>>>>>>>' , err);
- UIUtils.alert.error('Error while signing identity: ' + err);
- UIUtils.loading.hide();
- });
- })
- .catch(function(err) {
- console.error('>>>>>>>' , err);
- UIUtils.alert.error('Error while signing identity: ' + err);
- });
- };
-
- // Transfer click
- $scope.transfer = function() {
- $state.go('app.view_transfer', {
- pubkey: $scope.identity.pubkey,
- uid: $scope.identity.uid,
- });
- };
-
-
-}
-
-function PeersController($scope, $rootScope, BMA, UIUtils, $q, $interval, $timeout) {
-
- var newPeers = [], interval, lookingForPeers;
- $scope.search.lookingForPeers = false;
- $scope.search.peers = [];
-
- $scope.overviewPeers = function() {
- var currents = {}, block;
- for (var i = 0, len = $scope.search.peers.length; i < len; i++) {
- block = $scope.search.peers[i].current;
- if (block) {
- var bid = fpr(block);
- currents[bid] = currents[bid] || 0;
- currents[bid]++;
- }
- }
- var fprs = _.keys(currents).map(function(key) {
- return { fpr: key, qty: currents[key] };
- });
- var best = _.max(fprs, function(obj) {
- return obj.qty;
- });
- var p;
- for (var j = 0, len2 = $scope.search.peers.length; j < len2; j++) {
- p = $scope.search.peers[j];
- p.hasMainConsensusBlock = fpr(p.current) == best.fpr;
- p.hasConsensusBlock = !p.hasMainConsensusBlock && currents[fpr(p.current)] > 1;
- }
- $scope.search.peers = _.uniq($scope.search.peers, false, function(peer) {
- return peer.pubkey;
- });
- $scope.search.peers = _.sortBy($scope.search.peers, function(p) {
- var score = 1
- + 10000 * (p.online ? 1 : 0)
- + 1000 * (p.hasMainConsensusBlock ? 1 : 0) +
- + 100 * (p.uid ? 1 : 0);
- return -score;
- });
- };
-
- $scope.searchPeers = function() {
-
- if (interval) {
- $interval.cancel(interval);
- }
-
- interval = $interval(function() {
- if (newPeers.length) {
- $scope.search.peers = $scope.search.peers.concat(newPeers.splice(0));
- $scope.overviewPeers();
- } else if (lookingForPeers && !$scope.search.lookingForPeers) {
- // The peer lookup endend, we can make a clean final report
- $timeout(function(){
- lookingForPeers = false;
- $scope.overviewPeers();
- }, 1000);
- }
- }, 1000);
-
- var known = {};
- $rootScope.members = [];
- $scope.search.peers = [];
- $scope.search.lookingForPeers = true;
- lookingForPeers = true;
- return BMA.network.peering.peers({ leaves: true })
- .then(function(res){
- return BMA.wot.members()
- .then(function(json){
- $rootScope.members = json.results;
- return res;
- });
- })
- .then(function(res){
- return $q.all(res.leaves.map(function(leaf) {
- return BMA.network.peering.peers({ leaf: leaf })
- .then(function(subres){
- var peer = subres.leaf.value;
- if (peer) {
- peer = new Peer(peer);
- // Test each peer only once
- if (!known[peer.getURL()]) {
- peer.dns = peer.getDns();
- peer.blockNumber = peer.block.replace(/-.+$/, '');
- var member = _.findWhere($rootScope.members, { pubkey: peer.pubkey });
- peer.uid = member && member.uid;
- newPeers.push(peer);
- var node = BMA.instance(peer.getURL());
- return node.blockchain.current()
- .then(function(block){
- peer.current = block;
- peer.online = true;
- peer.server = peer.getURL();
- if ($scope.knownBlocks.indexOf(fpr(block)) === -1) {
- $scope.knownBlocks.push(fpr(block));
- }
- })
- .catch(function(err) {
- })
- }
- }
- })
- }))
- .then(function(){
- $scope.search.lookingForPeers = false;
- })
- })
- .catch(function(err) {
- //console.log(err);
- //UIUtils.alert.error('Could get peers from remote uCoin node.');
- //$scope.search.lookingForPeers = false;
- });
- };
-
- $scope.viewPeer = function() {
-
- };
-}
-
-function fpr(block) {
- return block && [block.number, block.hash].join('-');
-}
-
-function HomeController($scope, $ionicSlideBoxDelegate, $ionicModal, $state, BMA, UIUtils, $q, $timeout, Wallet, CryptoUtils, $ionicSideMenuDelegate) {
-
- // With the new view caching in Ionic, Controllers are only called
- // when they are recreated or on app start, instead of every page change.
- // To listen for when this page is active (for example, to refresh data),
- // listen for the $ionicView.enter event:
- //$scope.$on('$ionicView.enter', function(e) {
- //});
-
- CurrenciesController.call(this, $scope, $state);
- LookupController.call(this, $scope, BMA, $state);
- LoginController.call(this, $scope, $ionicModal, Wallet, CryptoUtils, UIUtils, $q, $state, $timeout, $ionicSideMenuDelegate);
-
- $scope.accountTypeMember = null;
- $scope.accounts = [];
- $scope.search = { text: '', results: {} };
- $scope.knownCurrencies = ['meta_brouzouf'];
-
- // Called to navigate to the main app
- $scope.cancel = function() {
- $scope.modal.hide();
- $timeout(function(){
- $scope.selectedCurrency = '';
- $scope.accountTypeMember = null;
- $scope.search.text = '';
- $scope.search.results = [];
- }, 200);
- };
-
- $scope.$on('currencySelected', function() {
- $ionicSlideBoxDelegate.slide(1);
- });
-
- $scope.selectAccountTypeMember = function(bool) {
- $scope.accountTypeMember = bool;
- $ionicSlideBoxDelegate.slide(2);
- };
-
- $scope.next = function() {
- $ionicSlideBoxDelegate.next();
- };
- $scope.previous = function() {
- $ionicSlideBoxDelegate.previous();
- };
-
- // Called each time the slide changes
- $scope.slideChanged = function(index) {
- $scope.slideIndex = index;
- $scope.nextStep = $scope.slideIndex == 2 ? 'Start using MyApp' : 'Next';
- };
-
- $scope.addAccount = function() {
- $scope.modal.show();
- $scope.slideChanged(0);
- $ionicSlideBoxDelegate.slide(0);
- $ionicSlideBoxDelegate.enableSlide(false);
- // TODO: remove default
- //$timeout(function() {
- // $scope.selectedCurrency = $scope.knownCurrencies[0];
- // $scope.accountTypeMember = true;
- // $scope.searchChanged();
- // $scope.search.text = 'cgeek';
- // $ionicSlideBoxDelegate.next();
- // $ionicSlideBoxDelegate.next();
- //}, 300);
- };
-
- // Create the account modal that we will use later
- $ionicModal.fromTemplateUrl('templates/account/new_account.html', {
- scope: $scope
- }).then(function(modal) {
- $scope.modal = modal;
- $scope.modal.hide();
- // TODO: remove auto add account when done
- //$timeout(function() {
- // $scope.addAccount();
- //}, 400);
- });
-
- $scope.selectCurrency = function(currency) {
- $scope.selectedCurrency = currency;
- $scope.next();
- }
-}
-
-
-function WalletController($scope, $state, $q, UIUtils, Wallet, $ionicPopup) {
-
- $scope.walletData = {};
- $scope.convertedBalance = 0;
- $scope.hasCredit = false;
- $scope.isMember = false;
-
- $scope.$on('$ionicView.enter', function(e, $state) {
- $scope.loadWallet()
- .then(function(wallet) {
- $scope.updateWalletView(wallet);
- });
- });
-
- $scope.refreshConvertedBalance = function() {
- if ($scope.walletData.useRelative) {
- $scope.convertedBalance = $scope.walletData.balance / $scope.walletData.currentUD;
- $scope.unit = 'universal_dividend';
- $scope.udUnit = $scope.walletData.currency;
- } else {
- $scope.convertedBalance = $scope.walletData.balance;
- $scope.unit = $scope.walletData.currency;
- $scope.udUnit = '';
- }
- };
- $scope.$watch('walletData.useRelative', $scope.refreshConvertedBalance, true);
- $scope.$watch('walletData.balance', $scope.refreshConvertedBalance, true);
-
- // Update view
- $scope.updateWalletView = function(wallet) {
- $scope.walletData = wallet;
- $scope.hasCredit = $scope.walletData.balance != "undefined" && ($scope.walletData.balance > 0);
- $scope.isMember = (wallet.requirements != null);
- };
-
- // Has credit
- $scope.hasCredit= function() {
- return $scope.balance > 0;
- };
-
- // Transfer click
- $scope.transfer= function() {
- $state.go('app.view_transfer');
- };
-
- // Self cert
- $scope.self= function() {
-
- // ASk uid
- $ionicPopup.show({
- template: '<input type="text" ng-model="walletData.uid">',
- title: 'Enter Pseudo',
- subTitle: 'Pseudo is used by other member member to find you',
- scope: $scope,
- buttons: [
- { text: 'Cancel' },
- {
- text: '<b>Send</b>',
- type: 'button-positive',
- onTap: function(e) {
- if (!$scope.walletData.uid) {
- //don't allow the user to close unless he enters wifi password
- e.preventDefault();
- } else {
- return $scope.walletData.uid;
- }
- }
- }
- ]
- })
- .then(function(uid) {
- UIUtils.loading.show();
- Wallet.self(uid)
- .then(function() {
- UIUtils.loading.hide();
- })
- .catch(function(err) {
- console.error('>>>>>>>' , err);
- UIUtils.alert.error('Error while sending self certification: ' + err);
- UIUtils.loading.hide();
- });
- });
-
-
- };
-}
-
-
-function TransferController($scope, $ionicModal, Wallet, UIUtils, $state, $ionicHistory) {
-
- $scope.walletData = {};
- $scope.formData = {
- destPub: null,
- amount: null,
- comments: null
- };
- $scope.dest = null;
- $scope.udAmount = null;
-
- $scope.$on('$ionicView.enter', function(e, $state) {
- if ($state.stateParams != null
- && $state.stateParams.pubkey != null
- && $state.stateParams.pubkey != "undefined") {
- $scope.destPub = $state.stateParams.pubkey;
- if ($state.stateParams.uid != null
- && $state.stateParams.uid != "undefined") {
- $scope.dest = $state.stateParams.uid;
- }
- else {
- $scope.dest = $scope.destPub;
- }
- }
-
- // Login and load wallet
- $scope.loadWallet()
- .then(function(walletData) {
- $scope.walletData = walletData;
- $scope.onUseRelativeChanged();
- });
- });
-
- // When chaing use relative UD
- $scope.onUseRelativeChanged = function() {
- if ($scope.walletData.useRelative) {
- $scope.udAmount = $scope.amount * $scope.walletData.currentUD;
- $scope.unit = 'universal_dividend';
- $scope.udUnit = $scope.walletData.currency;
- } else {
- $scope.formData.amount = ($scope.formData.amount != "undefined" && $scope.formData.amount != null)
- ? Math.floor(parseFloat($scope.formData.amount.replace(new RegExp('[,]'), '.')))
- : null;
- $scope.udAmount = $scope.amount / $scope.walletData.currentUD;
- $scope.unit = $scope.walletData.currency;
- $scope.udUnit = '';
- }
- };
- $scope.$watch('walletData.useRelative', $scope.onUseRelativeChanged, true);
-
- $ionicModal.fromTemplateUrl('templates/wot/modal_lookup.html', {
- scope: $scope,
- focusFirstInput: true
- }).then(function(modal) {
- $scope.lookupModal = modal;
- $scope.lookupModal.hide();
- });
-
- $scope.openSearch = function() {
- $scope.lookupModal.show();
- }
-
- $scope.doTransfer = function() {
- UIUtils.loading.show();
-
- var amount = $scope.formData.amount;
- if ($scope.walletData.useRelative
- && amount != "undefined"
- && amount != null) {
- amount = $scope.walletData.currentUD
- * amount.replace(new RegExp('[.,]'), '.');
- }
-
- Wallet.transfer($scope.formData.destPub, amount, $scope.formData.comments)
- .then(function() {
- UIUtils.loading.hide();
- $ionicHistory.goBack()
- })
- .catch(function(err) {
- console.error('>>>>>>>' , err);
- UIUtils.alert.error('Could not send transaction: ' + err);
- UIUtils.loading.hide();
- });
- };
-
- $scope.closeLookup = function() {
- $scope.lookupModal.hide();
- }
-
- $scope.doSelectIdentity = function(pub, uid) {
- if (uid != "undefined" && uid != null) {
- $scope.dest = uid;
- }
- else {
- $scope.dest = uid;
- }
- $scope.formData.destPub = pub;
- $scope.lookupModal.hide();
- }
-}
diff --git a/www/js/services.js b/www/js/services.js
index 4ba0959585279ce5c6bbea4f654582404c4800ef..44821043a3a0394403c3405523ae03c7e11181ed 100644
--- a/www/js/services.js
+++ b/www/js/services.js
@@ -1,849 +1,6 @@
-//var Base58, Base64, scrypt_module_factory = null, nacl_factory = null;
-
-angular.module('cesium.services', ['ngResource'])
-
-.factory('BMA', function($http, $q) {
-
- function BMA(server, wsServer) {
- if (wsServer == "undefined" || wsServer == null) {
- wsServer = server;
- }
-
- function processError(reject, data) {
- if (data != null && data.message != "undefined" && data.message != null) {
- reject(data.ucode + ": " + data.message);
- }
- else {
- reject('Unknown error from ucoin node');
- }
- }
-
- function prepare(uri, params, config, callback) {
- var pkeys = [], queryParams = {}, newUri = uri;
- if (typeof params == 'object') {
- pkeys = _.keys(params);
- }
-
- pkeys.forEach(function(pkey){
- var prevURI = newUri;
- newUri = newUri.replace(new RegExp(':' + pkey), params[pkey]);
- if (prevURI == newUri) {
- queryParams[pkey] = params[pkey];
- }
- });
- config.params = queryParams;
- callback(newUri, config);
- }
-
- function getResource(uri) {
- return function(params) {
- return $q(function(resolve, reject) {
- var config = {
- timeout: 4000
- };
-
- prepare(uri, params, config, function(uri, config) {
- $http.get(uri, config)
- .success(function(data, status, headers, config) {
- resolve(data);
- })
- .error(function(data, status, headers, config) {
- processError(reject, data);
- });
- });
- });
- }
- }
-
- function postResource(uri) {
- return function(data, params) {
- return $q(function(resolve, reject) {
- var config = {
- timeout: 4000,
- headers : {'Content-Type' : 'application/json'}
- };
-
- prepare(uri, params, config, function(uri, config) {
- $http.post(uri, data, config)
- .success(function(data, status, headers, config) {
- resolve(data);
- })
- .error(function(data, status, headers, config) {
- processError(reject, data);
- });
- });
- });
- }
- }
-
- function ws(uri) {
- var sock = new WebSocket(uri);
- return {
- on: function(type, callback) {
- sock.onmessage = function(e) {
- callback(JSON.parse(e.data));
- };
- }
- };
- }
-
- return {
- wot: {
- lookup: getResource('http://' + server + '/wot/lookup/:search'),
- members: getResource('http://' + server + '/wot/members'),
- requirements: getResource('http://' + server + '/wot/requirements/:pubkey'),
- add: postResource('http://' + server + '/wot/add')
- },
- network: {
- peering: {
- peers: getResource('http://' + server + '/network/peering/peers')
- },
- peers: getResource('http://' + server + '/network/peers')
- },
- currency: {
- parameters: getResource('http://' + server + '/blockchain/parameters')
- },
- blockchain: {
- current: getResource('http://' + server + '/blockchain/current'),
- block: getResource('http://' + server + '/blockchain/block/:block'),
- stats: {
- ud: getResource('http://' + server + '/blockchain/with/ud'),
- tx: getResource('http://' + server + '/blockchain/with/tx')
- },
- membership: postResource('http://' + server + '/blockchain/membership'),
- },
-
- tx: {
- sources: getResource('http://' + server + '/tx/sources/:pubkey'),
- process: postResource('http://' + server + '/tx/process'),
- history: {
- all: getResource('http://' + server + '/tx/history/:pubkey'),
- times: getResource('http://' + server + '/tx/history/:pubkey/times/:from/:to'),
- blocks: getResource('http://' + server + '/tx/history/:pubkey/blocks/:from/:to')
- }
- },
- websocket: {
- block: function() {
- return ws('ws://' + wsServer + '/ws/block');
- },
- peer: function() {
- return ws('ws://' + wsServer + '/ws/peer');
- }
- }
- }
- }
- //var service = BMA('metab.ucoin.fr', 'metab.ucoin.fr:9201');
- //var service = BMA('192.168.0.28:9201');
- var service = BMA('metab.ucoin.io');
- service.instance = BMA;
- return service;
-})
-
-.factory('UIUtils', function($ionicLoading, $ionicPopup) {
- function alertError(err, subtitle) {
- var message = err.message || err;
- return $ionicPopup.show({
- template: '<p>' + (message || 'Unknown error') + '</p>',
- title: 'Application error',
- subTitle: subtitle,
- buttons: [
- {
- text: '<b>OK</b>',
- type: 'button-assertive'
- }
- ]
- });
- }
-
- function hideLoading(){
- $ionicLoading.hide();
- }
-
- function showLoading() {
- $ionicLoading.show({
- template: 'Loading...'
- });
- }
-
- function onError(msg) {
- return function(err) {
- console.error('>>>>>>>' , err);
- alertError(msg + ': ' + err);
- hideLoading();
- }
- }
-
- return {
- alert: {
- error: alertError
- },
- loading: {
- show: showLoading,
- hide: hideLoading
- },
- onError: onError
- };
-})
-
-.factory('CryptoUtils', function($q, $timeout) {
-
- var async_load_scrypt = function() {
- if (typeof module !== 'undefined' && module.exports) {
- // add node.js implementations
- require('scrypt-em');
- return scrypt_module_factory();
- }
- else if (scrypt_module_factory !== null){
- return scrypt_module_factory();
- }
- else {
- return $timeout(async_load_scrypt, 100);
- }
- },
-
- async_load_nacl = function() {
- if (typeof module !== 'undefined' && module.exports) {
- // add node.js implementations
- require('nacl_factory');
- return nacl_factory.instantiate();
- }
- else if (nacl_factory !== null){
- return nacl_factory.instantiate();
- }
- else {
- return $timeout(async_load_nacl, 100);
- }
- },
-
- async_load_base58 = function() {
- if (typeof module !== 'undefined' && module.exports) {
- // add node.js implementations
- require('base58');
- return Base58;
- }
- else if (Base58 !== null){
- return Base58;
- }
- else {
- return $timeout(async_load_base58, 100);
- }
- },
-
- async_load_base64 = function() {
- if (typeof module !== 'undefined' && module.exports) {
- // add node.js implementations
- require('base58');
- return Base64;
- }
- else if (Base64 !== null){
- return Base64;
- }
- else {
- return setTimetout(async_load_base64, 100);
- }
- };
-
- function CryptoUtils() {
- var
- // Const
- crypto_sign_BYTES= 64,
- SEED_LENGTH= 32, // Length of the key
- SCRYPT_PARAMS= {
- "N":4096,
- "r":16,
- "p":1
- }
-
- // load libraries
- scrypt = async_load_scrypt(),
- nacl = async_load_nacl(),
- base58 = async_load_base58(),
- base64 = async_load_base64(),
- decode_utf8 = function(s) {
- var i, d = unescape(encodeURIComponent(s)), b = new Uint8Array(d.length);
- for (i = 0; i < d.length; i++) b[i] = d.charCodeAt(i);
- return b;
- },
- encode_base58 = function(a) {
- return base58.encode(a);
- },
-
- /**
- * Create a key pair, from salt+password, and return a wallet object
- */
- connect = function(salt, password) {
- return $q(function(resolve, reject) {
- var seed = scrypt.crypto_scrypt(
- nacl.encode_utf8(password),
- nacl.encode_utf8(salt),
- 4096, 16, 1, 32 // TODO: put in var SCRYPT_PARAMS
- );
- var keypair = nacl.crypto_sign_keypair_from_seed(seed);
- resolve(keypair);
- })
- },
-
- /**
- * Verify a signature of a message, for a pubkey
- */
- verify = function (message, signature, pubkey) {
- return $q(function(resolve, reject) {
- var msg = decode_utf8(message);
- var sig = base64.decode(signature);
- var pub = base58.decode(pubkey);
- var m = new Uint8Array(crypto_sign_BYTES + msg.length);
- var sm = new Uint8Array(crypto_sign_BYTES + msg.length);
- var i;
- for (i = 0; i < crypto_sign_BYTES; i++) sm[i] = sig[i];
- for (i = 0; i < msg.length; i++) sm[i+crypto_sign_BYTES] = msg[i];
-
- // Call to verification lib...
- var verified = nacl.crypto_sign_open(sm, pub) !== null;
- resolve(verified);
- });
- },
-
- /**
- * Sign a message, from a wallet
- */
- sign = function(message, keypair) {
- return $q(function(resolve, reject) {
- var m = decode_utf8(message);
- var sk = keypair.signSk;
- var signedMsg = nacl.crypto_sign(m, sk);
- var sig = new Uint8Array(crypto_sign_BYTES);
- for (var i = 0; i < sig.length; i++) sig[i] = signedMsg[i];
- var signature = base64.encode(sig);
- resolve(signature);
- })
- }
-
- ;
-
- // Service's exposed methods
- return {
- /*
- TODO: uncomment if need to expose
- nacl: nacl,
- scrypt: scrypt,
- base58: base58,
- base64: base64,*/
- util: {
- encode_utf8: nacl.encode_utf8,
- decode_utf8: decode_utf8,
- encode_base58: encode_base58
- },
-
-
- connect: connect,
- sign: sign,
- verify: verify
- //,isCompatible: isCompatible
- }
- }
- var service = CryptoUtils();
- service.instance = CryptoUtils;
- return service;
-})
-
-.factory('$localstorage', ['$window', 'CryptoUtils', '$q', function($window, CryptoUtils, $q) {
- return {
- set: function(key, value) {
- $window.localStorage[key] = value;
- },
- get: function(key, defaultValue) {
- return $window.localStorage[key] || defaultValue;
- },
- setObject: function(key, value) {
- $window.localStorage[key] = JSON.stringify(value);
- },
- getObject: function(key) {
- return JSON.parse($window.localStorage[key] || '{}');
- }
- }
-}])
-
-.factory('Wallet', ['CryptoUtils', 'BMA', '$q', function(CryptoUtils, BMA, $q) {
- Wallet = function(id) {
-
- var
-
- USE_RELATIVE_DEFAULT = true,
-
- createData = function() {
- return {
- uid: null,
- pubkey: null,
- keypair: {
- signSk: null,
- signPk: null
- },
- balance: 0,
- sources: null,
- useRelative: USE_RELATIVE_DEFAULT,
- currency: null,
- currentUD: null,
- history: {},
- loaded: false,
- requirements: null
- };
- },
-
- data = createData(),
-
- reduceTx = function(txArray) {
- var list = [];
- txArray.forEach(function(tx) {
- var issuerIndex = -1;
- var issuer = tx.issuers.reduce(function(issuer, res, index) {
- issuerIndex = (res == data.pubkey) ? index : issuerIndex;
- return issuer + ((res != data.pubkey) ? ', ' + res : '');
- }, ', ').substring(2);
- var amount =
- tx.inputs.reduce(function(sum, input) {
- var inputArray = input.split(':',5);
- return sum - ((inputArray[0] == issuerIndex) ? parseInt(inputArray[4]) : 0);
- }, 0);
- amount += tx.outputs.reduce(function(sum, output) {
- var outputArray = output.split(':',2);
- return sum + ((outputArray[0] == data.pubkey) ? parseInt(outputArray[1]) : 0);
- }, 0);
-
- list.push({
- time: ((tx.time != null && tx.time != "undefined") ? tx.time : 9999999),
- amount: amount,
- issuer: issuer,
- comments: 'comments',
- isUD: false,
- hash: tx.hash,
- block_number: tx.block_number
- });
- });
-
- return list;
- },
-
- login = function(salt, password) {
- return $q(function(resolve, reject) {
- CryptoUtils.connect(salt, password).then(
- function(keypair) {
- // Copy result to properties
- data.pubkey = CryptoUtils.util.encode_base58(keypair.signPk);
- data.keypair = keypair;
- resolve();
- }
- );
- });
- },
-
- logout = function(username, password) {
- return $q(function(resolve, reject) {
- data = createData();
- resolve();
- });
- },
-
- isLogin = function() {
- return data.pubkey != "undefined"
- && data.pubkey != null;
- },
-
- isSourceEquals = function(arg1, arg2) {
- return arg1.type == arg2.type
- && arg1.fingerprint == arg2.fingerprint
- && arg1.number == arg2.number
- && arg1.amount == arg2.amount;
- },
-
- loadData = function() {
- if (data.loaded) {
- return refreshData();
- }
-
- return $q(function(resolve, reject){
- data.loaded = false;
-
- //console.log('calling loadData');
- $q.all([
-
- // Get currency parameters
- BMA.currency.parameters()
- .then(function(json){
- data.currency = json.currency;
- }),
-
- // Get the UD informations
- BMA.blockchain.stats.ud()
- .then(function(res){
- if (res.result.blocks.length) {
- var lastBlockWithUD = res.result.blocks[res.result.blocks.length - 1];
- return BMA.blockchain.block({ block: lastBlockWithUD })
- .then(function(block){
- data.currentUD = block.dividend;
- });
- }
- }),
-
- // Get sources
- BMA.tx.sources({pubkey: data.pubkey})
- .then(function(res){
- data.sources = res.sources;
-
- var balance = 0;
- if (res.sources.length) {
- for (var i=0; i<res.sources.length; i++) {
- balance += res.sources[i].amount;
- res.sources[i].consumed = false;
- }
- }
- data.balance = balance;
- }),
-
- // Get requirements
- BMA.wot.requirements({pubkey: data.pubkey})
- .then(function(res){
- if (res.identities != "undefined"
- && res.identities != null
- && res.identities.length == 1) {
- data.requirements = res.identities[0];
- data.uid = res.identities[0].uid;
- }
- })
- .catch(function(err) {
- data.requirements = null;
- }),
-
- // Get transactions
- BMA.tx.history.all({pubkey: data.pubkey})
- .then(function(res){
- var list = reduceTx(res.history.sent);
- list.push(reduceTx(res.history.received));
- list.push(reduceTx(res.history.sending));
- list.push(reduceTx(res.history.receiving));
- list.push(reduceTx(res.history.pending));
-
- var history = [];
- list.forEach(function(tx){
- history['T:'+ tx.block_number + tx.hash] = tx;
- });
- var result = [];
- _.keys(history).forEach(function(key) {
- result.push(history[key]);
- })
- data.history = result.sort(function(tx1, tx2) {
- return tx2.time - tx1.time;
- });
- })
- ])
- .then(function() {
- data.loaded = true;
- resolve(data);
- })
- .catch(function(err) {
- data.loaded = false;
- reject(err);
- });
- });
- },
-
- refreshData = function() {
- return $q(function(resolve, reject){
- console.log('calling refreshData');
-
- $q.all([
-
- // Get the UD informations
- BMA.blockchain.stats.ud()
- .then(function(res){
- if (res.result.blocks.length) {
- var lastBlockWithUD = res.result.blocks[res.result.blocks.length - 1];
- return BMA.blockchain.block({ block: lastBlockWithUD })
- .then(function(block){
- data.currentUD = block.dividend;
- });
- }
- }),
-
- // Get requirements
- BMA.wot.requirements({pubkey: data.pubkey})
- .then(function(res){
- if (res.identities != "undefined"
- && res.identities != null
- && res.identities.length == 1) {
- data.requirements = res.identities[0];
- data.uid = res.identities[0].uid;
- }
- else {
- data.requirements = null;
- }
- })
- .catch(function(err) {
- data.requirements = null;
- }),
-
- // Get sources
- BMA.tx.sources({pubkey: data.pubkey})
- .then(function(res){
-
- var balance = 0;
- if (res.sources.length) {
- for (var i=0; i<res.sources.length; i++) {
- res.sources[i].consumed = false;
- if (data.sources.length) {
- for (var j=0; j<data.sources.length; j++) {
- if (isSourceEquals(res.sources[i], data.sources[j])
- && data.sources[j].consumed){
- res.sources[i].consumed = true;
- break;
- }
- }
- }
- if (!res.sources[i].consumed){
- balance += res.sources[i].amount;
- }
- }
- data.sources = res.sources;
- }
- data.balance = balance;
- })
- ])
- .then(function() {
- resolve(data);
- })
- .catch(function(err) {
- reject(err);
- });
- });
- },
-
- /**
- * Send a new transaction
- */
- transfer = function(destPub, amount, comments) {
- return $q(function(resolve, reject) {
-
- if (!isLogin()){
- reject('Wallet required to be login first.'); return;
- }
- if (amount == null) {
- reject('amount must not be null'); return;
- }
- amount = Math.round(amount);
- if (amount <= 0) {
- reject('amount must be greater than zero'); return;
- }
- if (amount > data.balance) {
- reject('Not enought credit'); return;
- }
-
- var tx = "Version: 1\n"
- + "Type: Transaction\n"
- + "Currency: " + data.currency + "\n"
- + "Issuers:\n"
- + data.pubkey + "\n"
- + "Inputs:\n";
- var sourceAmount = 0;
- var inputs = [];
- for (var i = 0; i<data.sources.length; i++) {
- var input = data.sources[i];
- if (input.consumed == "undefined" || !input.consumed){
- // INDEX:SOURCE:NUMBER:FINGERPRINT:AMOUNT
- tx += "0:"+input.type+":"+ input.number+":"
- + input.fingerprint+":"
- + input.amount+"\n";
- sourceAmount += input.amount;
- inputs.push(input);
- if (sourceAmount >= amount) {
- break;
- }
- }
- }
-
- if (sourceAmount < amount) {
- reject('Not enought sources (max amount: '
- +(data.useRelative ? (sourceAmount / data.currentUD)+' UD' : sourceAmount)
- +'). Please wait next block computation.');
- return;
- }
-
- tx += "Outputs:\n"
- // ISSUERS:AMOUNT
- + destPub +":" + amount + "\n";
- if (sourceAmount > amount) {
- tx += data.pubkey+":"+(sourceAmount-amount)+"\n";
- }
-
- tx += "Comment: "+ (comments!=null?comments:"") + "\n";
-
-
-
- CryptoUtils.sign(tx, data.keypair)
- .then(function(signature) {
- var signedTx = tx + signature + "\n";
- BMA.tx.process({transaction: signedTx})
- .then(function(result) {
- data.balance -= amount;
- for(var i=0;i<inputs.length;i++)inputs[i].consumed=true;
- resolve(result);
- })
- .catch(function(err){
- reject(err);
- });
- })
- .catch(function(err){
- reject(err);
- });
- });
- }
-
- /**
- * Send self certification
- */
- self = function(uid) {
- return $q(function(resolve, reject) {
-
- BMA.blockchain.current()
- .then(function(block) {
- // Create the self part to sign
- var self = 'UID:' + uid + '\n'
- + 'META:TS:' + (block.time+1) + '\n';
-
- CryptoUtils.sign(self, data.keypair)
- .then(function(signature) {
- var signedSelf = self + signature + '\n';
- // Send self
- BMA.wot.add({pubkey: data.pubkey, self: signedSelf, other: ''})
- .then(function(result) {
- // Check requirements
- BMA.wot.requirements({pubkey: data.pubkey})
- .then(function(res){
- if (res.identities != "undefined"
- && res.identities != null
- && res.identities.length == 1) {
- data.requirements = res.identities[0];
- data.uid = uid;
- resolve();
- }
- else{
- reject();
- }
- })
- .catch(function(err) {
- reject();
- })
- })
- .catch(function(err){
- reject(err);
- });
- })
- .catch(function(err){
- reject(err);
- });
- })
- .catch(function(err) {
- reject(err);
- });
- });
- },
-
- /**
- * Send identity certification
- */
- sign = function(uid, pubkey, timestamp, signature) {
- return $q(function(resolve, reject) {
-
- BMA.blockchain.current()
- .then(function(block) {
- // Create the self part to sign
- var self = 'UID:' + uid + '\n'
- + 'META:TS:' + timestamp + '\n'
- + signature /*+"\n"*/;
-
- var cert = self + '\n'
- + 'META:TS:' + block.number + '-' + block.hash + '\n';
-
- CryptoUtils.sign(cert, data.keypair)
- .then(function(signature) {
- var inlineCert = data.pubkey
- + ':' + pubkey
- + ':' + block.number
- + ':' + signature + '\n';
- BMA.wot.add({pubkey: pubkey, self: self, other: inlineCert})
- .then(function(result) {
- resolve(result);
- })
- .catch(function(err){
- reject(err);
- });
- })
- .catch(function(err){
- reject(err);
- });
- })
- .catch(function(err) {
- reject(err);
- });
- });
- },
-
- /**
- * Serialize to JSON string
- */
- toJson = function() {
- return $q(function(resolve, reject) {
- var json = JSON.stringify(data);
- resolve(json);
- })
- },
-
- /**
- * De-serialize from JSON string
- */
- fromJson = function(json) {
- return $q(function(resolve, reject) {
- var obj = JSON.parse(json || '{}');
- if (obj.keypair != "undefined"
- && obj.keypair != null) {
- var keypair = obj.keypair;
-
- // Convert to Uint8Array type
- var signPk = new Uint8Array(32);
- for (var i = 0; i < 32; i++) signPk[i] = keypair.signPk[i];
- keypair.signPk = signPk;
-
- var signSk = new Uint8Array(64);
- for (var i = 0; i < 64; i++) signSk[i] = keypair.signSk[i];
- keypair.signSk = signSk;
-
- data.pubkey = obj.pubkey;
- data.keypair = keypair;
-
- resolve();
- }
- else {
- reject('Not a valid Wallet.data object');
- }
- })
- };
-
- return {
- id: id,
- data: data,
-
- login: login,
- logout: logout,
- isLogin: isLogin,
- toJson: toJson,
- fromJson: fromJson,
- loadData: loadData,
- refreshData: refreshData,
- transfer: transfer,
- self: self,
- sign: sign
- }
- }
- var service = Wallet('default');
- service.instance = service;
- return service;
-}])
+angular.module('cesium.services', [
+ 'cesium.bma.services',
+ 'cesium.crypto.services',
+ 'cesium.utils.services',
+ 'cesium.wallet.services'])
;
diff --git a/www/js/services/bma-services.js b/www/js/services/bma-services.js
new file mode 100644
index 0000000000000000000000000000000000000000..75d5fdb2f07e39169833626af1844b51d182da62
--- /dev/null
+++ b/www/js/services/bma-services.js
@@ -0,0 +1,141 @@
+//var Base58, Base64, scrypt_module_factory = null, nacl_factory = null;
+
+angular.module('cesium.bma.services', ['ngResource'])
+
+.factory('BMA', function($http, $q) {
+
+ function BMA(server, wsServer) {
+ if (wsServer == "undefined" || wsServer == null) {
+ wsServer = server;
+ }
+
+ function processError(reject, data) {
+ if (data != null && data.message != "undefined" && data.message != null) {
+ reject(data.ucode + ": " + data.message);
+ }
+ else {
+ reject('Unknown error from ucoin node');
+ }
+ }
+
+ function prepare(uri, params, config, callback) {
+ var pkeys = [], queryParams = {}, newUri = uri;
+ if (typeof params == 'object') {
+ pkeys = _.keys(params);
+ }
+
+ pkeys.forEach(function(pkey){
+ var prevURI = newUri;
+ newUri = newUri.replace(new RegExp(':' + pkey), params[pkey]);
+ if (prevURI == newUri) {
+ queryParams[pkey] = params[pkey];
+ }
+ });
+ config.params = queryParams;
+ callback(newUri, config);
+ }
+
+ function getResource(uri) {
+ return function(params) {
+ return $q(function(resolve, reject) {
+ var config = {
+ timeout: 4000
+ };
+
+ prepare(uri, params, config, function(uri, config) {
+ $http.get(uri, config)
+ .success(function(data, status, headers, config) {
+ resolve(data);
+ })
+ .error(function(data, status, headers, config) {
+ processError(reject, data);
+ });
+ });
+ });
+ }
+ }
+
+ function postResource(uri) {
+ return function(data, params) {
+ return $q(function(resolve, reject) {
+ var config = {
+ timeout: 4000,
+ headers : {'Content-Type' : 'application/json'}
+ };
+
+ prepare(uri, params, config, function(uri, config) {
+ $http.post(uri, data, config)
+ .success(function(data, status, headers, config) {
+ resolve(data);
+ })
+ .error(function(data, status, headers, config) {
+ processError(reject, data);
+ });
+ });
+ });
+ }
+ }
+
+ function ws(uri) {
+ var sock = new WebSocket(uri);
+ return {
+ on: function(type, callback) {
+ sock.onmessage = function(e) {
+ callback(JSON.parse(e.data));
+ };
+ }
+ };
+ }
+
+ return {
+ wot: {
+ lookup: getResource('http://' + server + '/wot/lookup/:search'),
+ members: getResource('http://' + server + '/wot/members'),
+ requirements: getResource('http://' + server + '/wot/requirements/:pubkey'),
+ add: postResource('http://' + server + '/wot/add')
+ },
+ network: {
+ peering: {
+ peers: getResource('http://' + server + '/network/peering/peers')
+ },
+ peers: getResource('http://' + server + '/network/peers')
+ },
+ currency: {
+ parameters: getResource('http://' + server + '/blockchain/parameters')
+ },
+ blockchain: {
+ current: getResource('http://' + server + '/blockchain/current'),
+ block: getResource('http://' + server + '/blockchain/block/:block'),
+ membership: postResource('http://' + server + '/blockchain/membership'),
+ stats: {
+ ud: getResource('http://' + server + '/blockchain/with/ud'),
+ tx: getResource('http://' + server + '/blockchain/with/tx')
+ }
+ },
+
+ tx: {
+ sources: getResource('http://' + server + '/tx/sources/:pubkey'),
+ process: postResource('http://' + server + '/tx/process'),
+ history: {
+ all: getResource('http://' + server + '/tx/history/:pubkey'),
+ times: getResource('http://' + server + '/tx/history/:pubkey/times/:from/:to'),
+ blocks: getResource('http://' + server + '/tx/history/:pubkey/blocks/:from/:to')
+ }
+ },
+ websocket: {
+ block: function() {
+ return ws('ws://' + wsServer + '/ws/block');
+ },
+ peer: function() {
+ return ws('ws://' + wsServer + '/ws/peer');
+ }
+ }
+ }
+ }
+ //var service = BMA('metab.ucoin.fr', 'metab.ucoin.fr:9201');
+ //var service = BMA('192.168.0.28:9201');
+ var service = BMA('metab.ucoin.io');
+ service.instance = BMA;
+ return service;
+})
+;
diff --git a/www/js/services/crypto-services.js b/www/js/services/crypto-services.js
new file mode 100644
index 0000000000000000000000000000000000000000..77d5b07da3776626cd1c06e4feafdac3e4a3ab95
--- /dev/null
+++ b/www/js/services/crypto-services.js
@@ -0,0 +1,165 @@
+//var Base58, Base64, scrypt_module_factory = null, nacl_factory = null;
+
+angular.module('cesium.crypto.services', ['ngResource'])
+
+.factory('CryptoUtils', function($q, $timeout) {
+
+ var async_load_scrypt = function() {
+ if (typeof module !== 'undefined' && module.exports) {
+ // add node.js implementations
+ require('scrypt-em');
+ return scrypt_module_factory();
+ }
+ else if (scrypt_module_factory !== null){
+ return scrypt_module_factory();
+ }
+ else {
+ return $timeout(async_load_scrypt, 100);
+ }
+ },
+
+ async_load_nacl = function() {
+ if (typeof module !== 'undefined' && module.exports) {
+ // add node.js implementations
+ require('nacl_factory');
+ return nacl_factory.instantiate();
+ }
+ else if (nacl_factory !== null){
+ return nacl_factory.instantiate();
+ }
+ else {
+ return $timeout(async_load_nacl, 100);
+ }
+ },
+
+ async_load_base58 = function() {
+ if (typeof module !== 'undefined' && module.exports) {
+ // add node.js implementations
+ require('base58');
+ return Base58;
+ }
+ else if (Base58 !== null){
+ return Base58;
+ }
+ else {
+ return $timeout(async_load_base58, 100);
+ }
+ },
+
+ async_load_base64 = function() {
+ if (typeof module !== 'undefined' && module.exports) {
+ // add node.js implementations
+ require('base58');
+ return Base64;
+ }
+ else if (Base64 !== null){
+ return Base64;
+ }
+ else {
+ return setTimetout(async_load_base64, 100);
+ }
+ };
+
+ function CryptoUtils() {
+ var
+ // Const
+ crypto_sign_BYTES= 64,
+ SEED_LENGTH= 32, // Length of the key
+ SCRYPT_PARAMS= {
+ "N":4096,
+ "r":16,
+ "p":1
+ }
+
+ // load libraries
+ scrypt = async_load_scrypt(),
+ nacl = async_load_nacl(),
+ base58 = async_load_base58(),
+ base64 = async_load_base64(),
+ decode_utf8 = function(s) {
+ var i, d = unescape(encodeURIComponent(s)), b = new Uint8Array(d.length);
+ for (i = 0; i < d.length; i++) b[i] = d.charCodeAt(i);
+ return b;
+ },
+ encode_base58 = function(a) {
+ return base58.encode(a);
+ },
+
+ /**
+ * Create a key pair, from salt+password, and return a wallet object
+ */
+ connect = function(salt, password) {
+ return $q(function(resolve, reject) {
+ var seed = scrypt.crypto_scrypt(
+ nacl.encode_utf8(password),
+ nacl.encode_utf8(salt),
+ 4096, 16, 1, 32 // TODO: put in var SCRYPT_PARAMS
+ );
+ var keypair = nacl.crypto_sign_keypair_from_seed(seed);
+ resolve(keypair);
+ })
+ },
+
+ /**
+ * Verify a signature of a message, for a pubkey
+ */
+ verify = function (message, signature, pubkey) {
+ return $q(function(resolve, reject) {
+ var msg = decode_utf8(message);
+ var sig = base64.decode(signature);
+ var pub = base58.decode(pubkey);
+ var m = new Uint8Array(crypto_sign_BYTES + msg.length);
+ var sm = new Uint8Array(crypto_sign_BYTES + msg.length);
+ var i;
+ for (i = 0; i < crypto_sign_BYTES; i++) sm[i] = sig[i];
+ for (i = 0; i < msg.length; i++) sm[i+crypto_sign_BYTES] = msg[i];
+
+ // Call to verification lib...
+ var verified = nacl.crypto_sign_open(sm, pub) !== null;
+ resolve(verified);
+ });
+ },
+
+ /**
+ * Sign a message, from a wallet
+ */
+ sign = function(message, keypair) {
+ return $q(function(resolve, reject) {
+ var m = decode_utf8(message);
+ var sk = keypair.signSk;
+ var signedMsg = nacl.crypto_sign(m, sk);
+ var sig = new Uint8Array(crypto_sign_BYTES);
+ for (var i = 0; i < sig.length; i++) sig[i] = signedMsg[i];
+ var signature = base64.encode(sig);
+ resolve(signature);
+ })
+ }
+
+ ;
+
+ // Service's exposed methods
+ return {
+ /*
+ TODO: uncomment if need to expose
+ nacl: nacl,
+ scrypt: scrypt,
+ base58: base58,
+ base64: base64,*/
+ util: {
+ encode_utf8: nacl.encode_utf8,
+ decode_utf8: decode_utf8,
+ encode_base58: encode_base58
+ },
+
+
+ connect: connect,
+ sign: sign,
+ verify: verify
+ //,isCompatible: isCompatible
+ }
+ }
+ var service = CryptoUtils();
+ service.instance = CryptoUtils;
+ return service;
+})
+;
diff --git a/www/js/services/utils-services.js b/www/js/services/utils-services.js
new file mode 100644
index 0000000000000000000000000000000000000000..41ce70c4d37e8916198795cca61217d9ad44928a
--- /dev/null
+++ b/www/js/services/utils-services.js
@@ -0,0 +1,83 @@
+//var Base58, Base64, scrypt_module_factory = null, nacl_factory = null;
+
+angular.module('cesium.utils.services', ['ngResource'])
+
+.factory('UIUtils', function($ionicLoading, $ionicPopup) {
+ function alertError(err, subtitle) {
+ var message = err.message || err;
+ return $ionicPopup.show({
+ template: '<p>' + (message || 'Unknown error') + '</p>',
+ title: 'Application error',
+ subTitle: subtitle,
+ buttons: [
+ {
+ text: '<b>OK</b>',
+ type: 'button-assertive'
+ }
+ ]
+ });
+ }
+
+ function alertInfo(message, subtitle) {
+ var message = err.message || err;
+ return $ionicPopup.show({
+ template: '<p>' + message + '</p>',
+ title: 'Information',
+ subTitle: subtitle,
+ buttons: [
+ {
+ text: '<b>OK</b>',
+ type: 'button-positive'
+ }
+ ]
+ });
+ }
+
+ function hideLoading(){
+ $ionicLoading.hide();
+ }
+
+ function showLoading() {
+ $ionicLoading.show({
+ template: 'Loading...'
+ });
+ }
+
+ function onError(msg) {
+ return function(err) {
+ console.error('>>>>>>>' , err);
+ alertError(msg + ': ' + err);
+ hideLoading();
+ }
+ }
+
+ return {
+ alert: {
+ error: alertError
+ },
+ loading: {
+ show: showLoading,
+ hide: hideLoading
+ },
+ onError: onError
+ };
+})
+
+.factory('$localstorage', ['$window', 'CryptoUtils', '$q', function($window, CryptoUtils, $q) {
+ return {
+ set: function(key, value) {
+ $window.localStorage[key] = value;
+ },
+ get: function(key, defaultValue) {
+ return $window.localStorage[key] || defaultValue;
+ },
+ setObject: function(key, value) {
+ $window.localStorage[key] = JSON.stringify(value);
+ },
+ getObject: function(key) {
+ return JSON.parse($window.localStorage[key] || '{}');
+ }
+ }
+}])
+
+;
diff --git a/www/js/services/wallet-services.js b/www/js/services/wallet-services.js
new file mode 100644
index 0000000000000000000000000000000000000000..248bddefb87933a89d1e1b7c3026227e3916b7d5
--- /dev/null
+++ b/www/js/services/wallet-services.js
@@ -0,0 +1,487 @@
+//var Base58, Base64, scrypt_module_factory = null, nacl_factory = null;
+
+angular.module('cesium.wallet.services', ['ngResource', 'cesium.bma.services', 'cesium.crypto.services'])
+
+.factory('Wallet', ['CryptoUtils', 'BMA', '$q', function(CryptoUtils, BMA, $q) {
+
+ Wallet = function(id) {
+
+ var
+
+ USE_RELATIVE_DEFAULT = true,
+
+ createData = function() {
+ return {
+ pubkey: null,
+ keypair: {
+ signSk: null,
+ signPk: null
+ },
+ balance: 0,
+ sources: null,
+ useRelative: USE_RELATIVE_DEFAULT,
+ currency: null,
+ currentUD: null,
+ history: {},
+ requirements: null,
+ loaded: false
+ };
+ },
+
+ data = createData(),
+
+ reduceTx = function(txArray) {
+ var list = [];
+ txArray.forEach(function(tx) {
+ var issuerIndex = -1;
+ var issuer = tx.issuers.reduce(function(issuer, res, index) {
+ issuerIndex = (res == data.pubkey) ? index : issuerIndex;
+ return issuer + ((res != data.pubkey) ? ', ' + res : '');
+ }, ', ').substring(2);
+ var amount =
+ tx.inputs.reduce(function(sum, input) {
+ var inputArray = input.split(':',5);
+ return sum - ((inputArray[0] == issuerIndex) ? parseInt(inputArray[4]) : 0);
+ }, 0);
+ amount += tx.outputs.reduce(function(sum, output) {
+ var outputArray = output.split(':',2);
+ return sum + ((outputArray[0] == data.pubkey) ? parseInt(outputArray[1]) : 0);
+ }, 0);
+
+ list.push({
+ time: ((tx.time != null && tx.time != "undefined") ? tx.time : 9999999),
+ amount: amount,
+ issuer: issuer,
+ comments: 'comments',
+ isUD: false,
+ hash: tx.hash,
+ block_number: tx.block_number
+ });
+ });
+
+ return list;
+ },
+
+ login = function(salt, password) {
+ return $q(function(resolve, reject) {
+ CryptoUtils.connect(salt, password).then(
+ function(keypair) {
+ // Copy result to properties
+ data.pubkey = CryptoUtils.util.encode_base58(keypair.signPk);
+ data.keypair = keypair;
+ resolve();
+ }
+ );
+ });
+ },
+
+ logout = function(username, password) {
+ return $q(function(resolve, reject) {
+ data = createData();
+ resolve();
+ });
+ },
+
+ isLogin = function() {
+ return data.pubkey != "undefined"
+ && data.pubkey != null;
+ },
+
+ isSourceEquals = function(arg1, arg2) {
+ return arg1.type == arg2.type
+ && arg1.fingerprint == arg2.fingerprint
+ && arg1.number == arg2.number
+ && arg1.amount == arg2.amount;
+ },
+
+ loadData = function() {
+ if (data.loaded) {
+ return refreshData();
+ }
+
+ return $q(function(resolve, reject){
+ data.loaded = false;
+
+ $q.all([
+
+ // Get currency parameters
+ BMA.currency.parameters()
+ .then(function(json){
+ data.currency = json.currency;
+ }),
+
+ // Get the UD informations
+ BMA.blockchain.stats.ud()
+ .then(function(res){
+ if (res.result.blocks.length) {
+ var lastBlockWithUD = res.result.blocks[res.result.blocks.length - 1];
+ return BMA.blockchain.block({ block: lastBlockWithUD })
+ .then(function(block){
+ data.currentUD = block.dividend;
+ });
+ }
+ }),
+
+ // Get sources
+ BMA.tx.sources({pubkey: data.pubkey})
+ .then(function(res){
+ data.sources = res.sources;
+
+ var balance = 0;
+ if (res.sources.length) {
+ for (var i=0; i<res.sources.length; i++) {
+ balance += res.sources[i].amount;
+ res.sources[i].consumed = false;
+ }
+ }
+ data.balance = balance;
+ }),
+
+ // Get requirements
+ BMA.wot.requirements({pubkey: data.pubkey})
+ .then(function(res){
+ if (res.identities != "undefined"
+ && res.identities != null
+ && res.identities.length == 1) {
+ data.requirements = res.identities[0];
+ data.uid = res.identities[0].uid;
+ }
+ })
+ .catch(function(err) {
+ data.requirements = null;
+ }),
+
+ // Get transactions
+ BMA.tx.history.all({pubkey: data.pubkey})
+ .then(function(res){
+ var list = reduceTx(res.history.sent);
+ list.push(reduceTx(res.history.received));
+ list.push(reduceTx(res.history.sending));
+ list.push(reduceTx(res.history.receiving));
+ list.push(reduceTx(res.history.pending));
+
+ var history = [];
+ list.forEach(function(tx){
+ history['T:'+ tx.block_number + tx.hash] = tx;
+ });
+ var result = [];
+ _.keys(history).forEach(function(key) {
+ result.push(history[key]);
+ })
+ data.history = result.sort(function(tx1, tx2) {
+ return tx2.time - tx1.time;
+ });
+ })
+ ])
+ .then(function() {
+ data.loaded = true;
+ resolve(data);
+ })
+ .catch(function(err) {
+ data.loaded = false;
+ reject(err);
+ });
+ });
+ },
+
+ refreshData = function() {
+ return $q(function(resolve, reject){
+ $q.all([
+
+ // Get the UD informations
+ BMA.blockchain.stats.ud()
+ .then(function(res){
+ if (res.result.blocks.length) {
+ var lastBlockWithUD = res.result.blocks[res.result.blocks.length - 1];
+ return BMA.blockchain.block({ block: lastBlockWithUD })
+ .then(function(block){
+ data.currentUD = block.dividend;
+ });
+ }
+ }),
+
+ // Get requirements
+ BMA.wot.requirements({pubkey: data.pubkey})
+ .then(function(res){
+ if (res.identities != "undefined"
+ && res.identities != null
+ && res.identities.length == 1) {
+ data.requirements = res.identities[0];
+ data.uid = res.identities[0].uid;
+ }
+ else {
+ data.requirements = null;
+ }
+ })
+ .catch(function(err) {
+ data.requirements = null;
+ }),
+
+ // Get sources
+ BMA.tx.sources({pubkey: data.pubkey})
+ .then(function(res){
+
+ var balance = 0;
+ if (res.sources.length) {
+ for (var i=0; i<res.sources.length; i++) {
+ res.sources[i].consumed = false;
+ if (data.sources.length) {
+ for (var j=0; j<data.sources.length; j++) {
+ if (isSourceEquals(res.sources[i], data.sources[j])
+ && data.sources[j].consumed){
+ res.sources[i].consumed = true;
+ break;
+ }
+ }
+ }
+ if (!res.sources[i].consumed){
+ balance += res.sources[i].amount;
+ }
+ }
+ data.sources = res.sources;
+ }
+ data.balance = balance;
+ })
+ ])
+ .then(function() {
+ resolve(data);
+ })
+ .catch(function(err) {
+ reject(err);
+ });
+ });
+ },
+
+ /**
+ * Send a new transaction
+ */
+ transfer = function(destPub, amount, comments) {
+ return $q(function(resolve, reject) {
+
+ if (!isLogin()){
+ reject('Wallet required to be login first.'); return;
+ }
+ if (amount == null) {
+ reject('amount must not be null'); return;
+ }
+ amount = Math.round(amount);
+ if (amount <= 0) {
+ reject('amount must be greater than zero'); return;
+ }
+ if (amount > data.balance) {
+ reject('Not enought credit'); return;
+ }
+
+ var tx = "Version: 1\n"
+ + "Type: Transaction\n"
+ + "Currency: " + data.currency + "\n"
+ + "Issuers:\n"
+ + data.pubkey + "\n"
+ + "Inputs:\n";
+ var sourceAmount = 0;
+ var inputs = [];
+ for (var i = 0; i<data.sources.length; i++) {
+ var input = data.sources[i];
+ if (input.consumed == "undefined" || !input.consumed){
+ // INDEX:SOURCE:NUMBER:FINGERPRINT:AMOUNT
+ tx += "0:"+input.type+":"+ input.number+":"
+ + input.fingerprint+":"
+ + input.amount+"\n";
+ sourceAmount += input.amount;
+ inputs.push(input);
+ if (sourceAmount >= amount) {
+ break;
+ }
+ }
+ }
+
+ if (sourceAmount < amount) {
+ reject('Not enought sources (max amount: '
+ +(data.useRelative ? (sourceAmount / data.currentUD)+' UD' : sourceAmount)
+ +'). Please wait next block computation.');
+ return;
+ }
+
+ tx += "Outputs:\n"
+ // ISSUERS:AMOUNT
+ + destPub +":" + amount + "\n";
+ if (sourceAmount > amount) {
+ tx += data.pubkey+":"+(sourceAmount-amount)+"\n";
+ }
+
+ tx += "Comment: "+ (comments!=null?comments:"") + "\n";
+
+
+
+ CryptoUtils.sign(tx, data.keypair)
+ .then(function(signature) {
+ var signedTx = tx + signature + "\n";
+ BMA.tx.process({transaction: signedTx})
+ .then(function(result) {
+ data.balance -= amount;
+ for(var i=0;i<inputs.length;i++)inputs[i].consumed=true;
+ resolve(result);
+ })
+ .catch(function(err){
+ reject(err);
+ });
+ })
+ .catch(function(err){
+ reject(err);
+ });
+ });
+ },
+
+ /**
+ * Send self certification
+ */
+ self = function(uid) {
+ return $q(function(resolve, reject) {
+
+ BMA.blockchain.current()
+ .then(function(block) {
+ // Create the self part to sign
+ var self = 'UID:' + uid + '\n'
+ + 'META:TS:' + (block.time+1) + '\n';
+
+ CryptoUtils.sign(self, data.keypair)
+ .then(function(signature) {
+ var signedSelf = self + signature + '\n';
+ // Send self
+ BMA.wot.add({pubkey: data.pubkey, self: signedSelf, other: ''})
+ .then(function(result) {
+ // Check requirements
+ BMA.wot.requirements({pubkey: data.pubkey})
+ .then(function(res){
+ if (res.identities != "undefined"
+ && res.identities != null
+ && res.identities.length == 1) {
+ data.requirements = res.identities[0];
+ data.uid = uid;
+ resolve();
+ }
+ else{
+ reject();
+ }
+ })
+ .catch(function(err) {
+ reject();
+ })
+ })
+ .catch(function(err){
+ reject(err);
+ });
+ })
+ .catch(function(err){
+ reject(err);
+ });
+ })
+ .catch(function(err) {
+ reject(err);
+ });
+ });
+ },
+
+ /**
+ * Send identity certification
+ */
+ sign = function(uid, pubkey, timestamp, signature) {
+ return $q(function(resolve, reject) {
+
+ BMA.blockchain.current()
+ .then(function(block) {
+ // Create the self part to sign
+ var self = 'UID:' + uid + '\n'
+ + 'META:TS:' + timestamp + '\n'
+ + signature /*+"\n"*/;
+
+ var cert = self + '\n'
+ + 'META:TS:' + block.number + '-' + block.hash + '\n';
+
+ CryptoUtils.sign(cert, data.keypair)
+ .then(function(signature) {
+ var inlineCert = data.pubkey
+ + ':' + pubkey
+ + ':' + block.number
+ + ':' + signature + '\n';
+ BMA.wot.add({pubkey: pubkey, self: self, other: inlineCert})
+ .then(function(result) {
+ resolve(result);
+ })
+ .catch(function(err){
+ reject(err);
+ });
+ })
+ .catch(function(err){
+ reject(err);
+ });
+ })
+ .catch(function(err) {
+ reject(err);
+ });
+ });
+ },
+
+ /**
+ * Serialize to JSON string
+ */
+ toJson = function() {
+ return $q(function(resolve, reject) {
+ var json = JSON.stringify(data);
+ resolve(json);
+ })
+ },
+
+ /**
+ * De-serialize from JSON string
+ */
+ fromJson = function(json) {
+ return $q(function(resolve, reject) {
+ var obj = JSON.parse(json || '{}');
+ if (obj.keypair != "undefined"
+ && obj.keypair != null) {
+ var keypair = obj.keypair;
+
+ // Convert to Uint8Array type
+ var signPk = new Uint8Array(32);
+ for (var i = 0; i < 32; i++) signPk[i] = keypair.signPk[i];
+ keypair.signPk = signPk;
+
+ var signSk = new Uint8Array(64);
+ for (var i = 0; i < 64; i++) signSk[i] = keypair.signSk[i];
+ keypair.signSk = signSk;
+
+ data.pubkey = obj.pubkey;
+ data.keypair = keypair;
+
+ resolve();
+ }
+ else {
+ reject('Not a valid Wallet.data object');
+ }
+ })
+ };
+
+ return {
+ id: id,
+ data: data,
+ // auth
+ login: login,
+ logout: logout,
+ isLogin: isLogin,
+ loadData: loadData,
+ refreshData: refreshData,
+ // operations
+ transfer: transfer,
+ self: self,
+ sign: sign,
+ // serialization
+ toJson: toJson,
+ fromJson: fromJson
+ }
+ }
+ var service = Wallet('default');
+ service.instance = service;
+ return service;
+}])
+;
diff --git a/www/templates/account/view_wallet.html b/www/templates/account/view_wallet.html
index ee491dd0183f1cc5ddb5e72cf3f24b1102e8fb8e..3b82e5ebbd8095e958f4a35f826de6dbe6364427 100644
--- a/www/templates/account/view_wallet.html
+++ b/www/templates/account/view_wallet.html
@@ -5,13 +5,13 @@
<span class="item item-icon-left" ng-if="isMember">
<i class="icon ion-person"></i>
- User
- <span class="badge">{{walletData.uid}}</span>
+ <h2>{{walletData.uid}}</h2>
+ <p>Public key: {{walletData.pubkey | formatPubkey}}</p>
</span>
<span class="item item-icon-left" ng-if="!isMember">
<i class="icon ion-key"></i>
- Pubkey
+ Public key
<span class="badge">{{walletData.pubkey | formatPubkey}}</span>
</span>
diff --git a/www/templates/wot/view_identity.html b/www/templates/wot/view_identity.html
index bc61831df776c8cf68dd41c589196e216bb73e75..d8cf130e76429858b168d130ccf6ceb5f8fbdef7 100644
--- a/www/templates/wot/view_identity.html
+++ b/www/templates/wot/view_identity.html
@@ -1,17 +1,12 @@
-<ion-view view-title="Identity {{identity.uid}}" left-buttons="leftButtons">
+<ion-view view-title="{{identity.uid}}" left-buttons="leftButtons">
<ion-content>
<div class="scroll">
<div class="list">
<span class="item item-icon-left">
<i class="icon ion-person"></i>
- Uid
- <span class="badge badge-royal">{{identity.uid}}</span>
- </span>
-
- <span class="item item-icon-left">
- <i class="icon ion-key"></i>
- Public key
- <span class="badge">{{identity.pub | formatPubkey}}</span>
+ <h2>{{identity.uid}}</h2>
+ <p>public key: {{identity.pub | formatPubkey}}</p>
+ <span class="badge"></span>
</span>
<span class="item item-icon-left">