diff --git a/.gitignore b/.gitignore index d41ca053c57ee2e5a482c6728ae9fc590281080d..80e9514b212a1d7f659e51f53f4e00b9e05915a1 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,4 @@ npm-debug.log bin/jpgp*.jar .idea/ naclb/build -naclb/node_modules -dump*/ -/ui/ \ No newline at end of file +naclb/node_modules \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..dd15f02a551959395883ce811b6c0a64c209fe52 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "web-ui"] + path = web-ui + url = https://github.com/duniter/duniter-ui.git diff --git a/.travis.yml b/.travis.yml index 229034e4f2f418c0dca6345c2621a5b057ff66f8..ddc57c53aa577de047f003d3ab56afe2edfe8351 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,62 +26,25 @@ after_success: before_deploy: # Download & embed Nodejs binary - - NVER=`node -v` - - DUNITER_VER=`git describe --exact-match --tags $(git log -n1 --pretty='%h') | grep -Po "\d.*"` - - DUNITER_DEB_VER=" $DUNITER_VER" - - wget http://nodejs.org/dist/${NVER}/node-${NVER}-linux-x64.tar.gz - - tar xzf node-${NVER}-linux-x64.tar.gz - - mv node-${NVER}-linux-x64 node - - rm node-${NVER}-linux-x64.tar.gz - # Clean testing packages - - npm prune --production - - tar czf ../ucoin-x64.tar.gz ./ --exclude ".git" --exclude "coverage" --exclude "test" - - SRC=`pwd` - - cd .. - # GUI Version - - mkdir ucoin_release - - NW_RELEASE="v0.13.0-rc2" - - NW="nwjs-${NW_RELEASE}-linux-x64" - - NW_GZ="${NW}.tar.gz" - - wget http://dl.nwjs.io/${NW_RELEASE}/${NW_GZ} - - tar xvzf ${NW_GZ} - - mv ${NW} ucoin_release/nw - - cp ${SRC}/gui/* ucoin_release/nw/ - - cp -R ${SRC}/ ucoin_release/sources/ - - rm -Rf ucoin_release/sources/ui/package/node_modules - - rm -Rf ucoin_release/sources/ui/package/bower_components - - cd ucoin_release - - tar czf ../duniter-x64.tar.gz * --exclude ".git" --exclude "coverage" --exclude "test" - - cd .. - - git clone https://github.com/ucoin-io/debpkg.git duniter-x64 - - rm -Rf duniter-x64/.git - - mkdir -p duniter-x64/opt/duniter/ - - chmod 755 duniter-x64/DEBIAN/post* - - chmod 755 duniter-x64/DEBIAN/pre* - - sed -i "s/Version:.*/Version:$DUNITER_DEB_VER/g" duniter-x64/DEBIAN/control - - cd ucoin_release/sources - - rm -Rf .git - - zip -qr ../duniter-desktop.nw * - - cd ../nw - - zip -qr ../nw.nwb * - - cd ../.. - - mv ucoin_release/duniter-desktop.nw duniter-x64/opt/duniter/ - - mv ucoin_release/nw.nwb duniter-x64/opt/duniter/ - - fakeroot dpkg-deb --build duniter-x64 - - mv duniter-x64.deb duniter-${TRAVIS_TAG}-${TRAVIS_OS_NAME}-x64.deb - - mv duniter-x64.tar.gz duniter-${TRAVIS_TAG}-${TRAVIS_OS_NAME}-x64.tar.gz - -# Releases deployed on GitHub + - ci/travis/before_deploy.sh deploy: - provider: releases - prerelease: true - skip_cleanup: true - api_key: - secure: eZ+DHkTYXkBqoHp/Qea1duDQXTAgJ+oYWbWET7PNlNbq6IGHRae//VqGkOIGaYvPCu6ezZbI/z/nQvBhm794vihYYek1Y8AAXp0QmxmRJhCgsCxRcVmyLu99un2aAYiZzUKKOylQrhc30JsObgo3JrovHAdMJFDf3f123whAPCA= - file: - - ucoin-x64.tar.gz - - duniter-${TRAVIS_TAG}-${TRAVIS_OS_NAME}-x64.deb - - duniter-${TRAVIS_TAG}-${TRAVIS_OS_NAME}-x64.tar.gz - on: - repo: duniter/duniter - tags: true + - provider: npm + email: cem.moreau@gmail.com + skip_cleanup: true + api_key: + secure: gZV7yLxDwwxD4oQXl1hwugmtnWXqP8vojuVGtAGwtMlwJE0n270w6O5xZHDd7DSmOZLftk6/wue/RdhLDsD6J1z3Uxu+VoUWy7aG/sFcGRaBwct+bGqFGkyd+I1mCXFnAZMDwbtgdkQlOCS9PM1BfMEYq49XXqaLaDnwouR+2bI= + on: + tags: true + repo: duniter/duniter + - provider: releases + prerelease: true + skip_cleanup: true + api-key: + secure: h+GgYhh8/I/l5EWdI1+bDQYtN57jvDf0xhiwcxlfm/AvaJWOZa+q81HsXdHqtwgmsCoRklKtOqTM/nqgSOcWPXsAditRjEXOui2FjZiJCosDjDPMnN6HKzw6XVLtSOx7CeCX/xFmoNTIdsAuC+HUfDWCUFgrsMj9F0V9J3U1nDs= + file: + - /home/travis/build/ucoin-x64.tar.gz + - /home/travis/build/duniter-${TRAVIS_TAG}-${TRAVIS_OS_NAME}-x64.deb + - /home/travis/build/duniter-${TRAVIS_TAG}-${TRAVIS_OS_NAME}-x64.tar.gz + on: + repo: duniter/duniter + tags: true diff --git a/readme.md b/README.md similarity index 73% rename from readme.md rename to README.md index 233b3923417ed593d9a4549548f0879009e1ec0d..f6ca5713f048e1a66fa863bbfdd72bae1d9a1607 100644 --- a/readme.md +++ b/README.md @@ -4,36 +4,36 @@ Duniter (previously uCoin) is a libre software allowing to create a new kind of P2P crypto-currencies based on individuals and Universal Dividend. -Inspired by [Bitcoin](https://github.com/bitcoin/bitcoin) and [OpenUDC project](https://github.com/Open-UDC/open-udc). +Inspired by [Bitcoin](https://github.com/bitcoin/bitcoin) and [OpenUDC](https://github.com/Open-UDC/open-udc) projects. ## Development state Software is still under development, and **no production currency using Duniter exists for now**. -However, it already exists a testing currency named [MetaBrouzouf](http://duniter.org/try/). Want to test it? Two ways. +However, it already exists a testing currency named [Test_Net](https://forum.duniter.org/t/join-our-new-testnet-currency/813). Want to test it? Two ways. ### Add your node to the network ``` $ wget -qO- https://raw.githubusercontent.com/duniter/duniter/master/install.sh | bash $ duniter init -$ duniter sync metab.ucoin.io 9201 +$ duniter sync duniter.org 8999 $ duniter start ``` -and you are done! See [Install documentation](https://github.com/duniter/duniter/wiki/Install-uCoin-node) for further details. +and you are done! See [Install documentation](https://github.com/duniter/duniter/wiki/Install-Duniter-node) for further details. ### Try Sakia Wallet -In the world of crypto-currencies, [Sakia Wallet](http://sakia-wallet.org/) would be called a [SPV](https://en.bitcoin.it/wiki/Thin_Client_Security#Simplified_Payment_Verification_.28SPV.29_Clients) client. It is a graphical client connecting to the network. See this [tutorial](https://forum.duniter.org/t/subscribing-to-meta-brouzouf-testing-currency/199) to join in the testing currency with Sakia. +In the world of crypto-currencies, [Sakia Wallet](http://sakia-wallet.org/) would be called a [SPV](https://en.bitcoin.it/wiki/Thin_Client_Security#Simplified_Payment_Verification_.28SPV.29_Clients) client. It is a graphical client connecting to the network. See this [tutorial](https://forum.duniter.org/t/join-our-new-testnet-currency/813) to join in the testing currency with Sakia. - +<p align="center"><img src="http://sakia-wallet.org/img/Dividends.png" /></p> ## Going further ### Documentation -Visit [Duniter website](http://duniter.org): it gathers theoretical informations, FAQ and several useful links. If you want to learn, this is the first place to visit. +Visit [Duniter website](https://duniter.org): it gathers theoretical informations, FAQ and several useful links. If you want to learn, this is the first place to visit. ### Talk about/get involved in Duniter project @@ -47,8 +47,8 @@ If you wish to participate/debate on Duniter, you can: # References ## Theoretical -* [[en] Relative theory of money](http://vit.free.fr/TRM/en_US/) -* [[fr] Théorie relative de la monaie](http://trm.creationmonetaire.info/) +* [[en] Relative theory of money](http://en.trm.creationmonetaire.info) +* [[fr] Théorie relative de la monaie](http://trm.creationmonetaire.info) ## OpenUDC diff --git a/app/controllers/abstract.js b/app/controllers/abstract.js index 9c57444660af390e3936318a48e633eb2ee47b31..8c80d1e933058b10fbe53a7aee7f264cf2fc0f49 100644 --- a/app/controllers/abstract.js +++ b/app/controllers/abstract.js @@ -5,11 +5,10 @@ var dos2unix = require('../lib/dos2unix'); module.exports = function AbstractController (server) { - this.pushEntity = (req, rawer, parser) => co(function *() { + this.pushEntity = (req, rawer, type) => co(function *() { let rawDocument = rawer(req); rawDocument = dos2unix(rawDocument); - let obj = parser.syncWrite(rawDocument); - let written = yield server.singleWritePromise(obj); + let written = yield server.writeRaw(rawDocument, type); return written.json(); }); }; diff --git a/app/controllers/blockchain.js b/app/controllers/blockchain.js index aad2fd7636d51eee43698e6ccb7aa3127d59491f..55a209cf18b796ebf2f5157d6fc762f4b7e12a30 100644 --- a/app/controllers/blockchain.js +++ b/app/controllers/blockchain.js @@ -2,11 +2,9 @@ var co = require('co'); var _ = require('underscore'); -var moment = require('moment'); var rules = require('../lib/rules'); var constants = require('../lib/constants'); var http2raw = require('../lib/streams/parsers/http2raw'); -var parsers = require('../lib/streams/parsers/doc'); var Membership = require('../lib/entity/membership'); var AbstractController = require('./abstract'); @@ -29,9 +27,9 @@ function BlockchainBinding (server) { var Block = require('../lib/entity/block'); var Stat = require('../lib/entity/stat'); - this.parseMembership = (req) => this.pushEntity(req, http2raw.membership, parsers.parseMembership); + this.parseMembership = (req) => this.pushEntity(req, http2raw.membership, constants.ENTITY_MEMBERSHIP); - this.parseBlock = (req) => this.pushEntity(req, http2raw.block, parsers.parseBlock); + this.parseBlock = (req) => this.pushEntity(req, http2raw.block, constants.ENTITY_BLOCK); this.parameters = () => server.dal.getParameters(); diff --git a/app/controllers/network.js b/app/controllers/network.js index 0689f2222cec19ac4f8779945034354dfc276b06..a5e5a156b81bc97a9ddc557ac29cabe015789c71 100644 --- a/app/controllers/network.js +++ b/app/controllers/network.js @@ -2,9 +2,7 @@ var _ = require('underscore'); var co = require('co'); var Q = require('q'); -var async = require('async'); var http2raw = require('../lib/streams/parsers/http2raw'); -var parsers = require('../lib/streams/parsers/doc'); var constants = require('../lib/constants'); var Peer = require('../lib/entity/peer'); var AbstractController = require('./abstract'); @@ -53,7 +51,7 @@ function NetworkBinding (server) { }); }); - this.peersPost = (req) => this.pushEntity(req, http2raw.peer, parsers.parsePeer); + this.peersPost = (req) => this.pushEntity(req, http2raw.peer, constants.ENTITY_PEER); this.peers = () => co(function *() { let peers = yield server.dal.listAllPeers(); diff --git a/app/controllers/transactions.js b/app/controllers/transactions.js index cec9883530c466332aea7fcc8c52c9562db66214..a1930f87c499f61d22733fcd010047c2e6ad514b 100644 --- a/app/controllers/transactions.js +++ b/app/controllers/transactions.js @@ -4,8 +4,8 @@ var Q = require('q'); var async = require('async'); var _ = require('underscore'); var http2raw = require('../lib/streams/parsers/http2raw'); -var parsers = require('../lib/streams/parsers/doc'); var Transaction = require('../lib/entity/transaction'); +var constants = require('../lib/constants'); var AbstractController = require('./abstract'); module.exports = function (server) { @@ -26,7 +26,7 @@ function TransactionBinding(server) { let getHistoryP = (pubkey, filter) => Q.nbind(getHistory, this)(pubkey, filter); - this.parseTransaction = (req) => this.pushEntity(req, http2raw.transaction, parsers.parseTransaction); + this.parseTransaction = (req) => this.pushEntity(req, http2raw.transaction, constants.ENTITY_TRANSACTION); this.getSources = (req) => co(function *() { let pubkey = yield ParametersService.getPubkeyP(req); diff --git a/app/controllers/webmin.controller.js b/app/controllers/webmin.controller.js index 939776000abe0bd915511d2b1483c087efab3e7e..71ec11fe77138726a78a66e50aca72ef27154ebc 100644 --- a/app/controllers/webmin.controller.js +++ b/app/controllers/webmin.controller.js @@ -7,20 +7,16 @@ var _ = require('underscore'); var Q = require('q'); let co = require('co'); let ucoin = require('../../index'); -var upnp = require('../lib/upnp'); let ucp = require('../lib/ucp'); let constants = require('../lib/constants'); let base58 = require('../lib/base58'); let rawer = require('../lib/rawer'); let crypto = require('../lib/crypto'); let http2raw = require('../lib/streams/parsers/http2raw'); -let parsers = require('../lib/streams/parsers/doc'); let bma = require('../lib/streams/bma'); let Identity = require('../lib/entity/identity'); let network = require('../lib/network'); let AbstractController = require('../controllers/abstract'); -var Synchroniser = require('../lib/sync'); -var multicaster = require('../lib/streams/multicaster'); var logger = require('../lib/logger')('webmin'); module.exports = (dbConf, overConf) => { @@ -50,13 +46,7 @@ function WebAdmin (dbConf, overConf) { yield pluggedConfP; // Routing documents - server - // The router asks for multicasting of documents - .pipe(server.router()) - // The documents get sent to peers - .pipe(multicaster(server.conf)) - // The multicaster may answer 'unreachable peer' - .pipe(server.router()); + server.routing(); return plugForDAL(); }); @@ -96,7 +86,7 @@ function WebAdmin (dbConf, overConf) { this.openUPnP = () => co(function *() { yield pluggedDALP; - return upnp(server.conf.port, server.conf.remoteport); + return server.upnp(); }); this.regularUPnP = () => co(function *() { @@ -105,7 +95,7 @@ function WebAdmin (dbConf, overConf) { server.upnpAPI.stopRegular(); } try { - server.upnpAPI = yield upnp(server.conf.port, server.conf.remoteport); + yield server.upnp(); server.upnpAPI.startRegular(); } catch (e) { logger.error(e); @@ -134,7 +124,7 @@ function WebAdmin (dbConf, overConf) { yield server.dal.saveConf({ routing: true, createNext: true, - cpu: 0.5, + cpu: constants.DEFAULT_CPU, ipv4: conf.local_ipv4, ipv6: conf.local_ipv6, port: conf.lport, @@ -187,7 +177,7 @@ function WebAdmin (dbConf, overConf) { if (!found) { let selfCert = rawer.getOfficialIdentity(entity); selfCert += crypto.signSync(selfCert, secretKey) + '\n'; - found = yield that.pushEntity({ body: { identity: selfCert }}, http2raw.identity, parsers.parseIdentity); + found = yield that.pushEntity({ body: { identity: selfCert }}, http2raw.identity, constants.ENTITY_IDENTITY); } yield server.dal.fillInMembershipsOfIdentity(Q(found)); if (_.filter(found.memberships, { membership: 'IN'}).length == 0) { @@ -202,7 +192,7 @@ function WebAdmin (dbConf, overConf) { "certts": block }); join += crypto.signSync(join, secretKey) + '\n'; - yield that.pushEntity({ body: { membership: join }}, http2raw.membership, parsers.parseMembership); + yield that.pushEntity({ body: { membership: join }}, http2raw.membership, constants.ENTITY_MEMBERSHIP); yield server.recomputeSelfPeer(); } // @@ -366,46 +356,48 @@ function WebAdmin (dbConf, overConf) { }); this.autoConfNetwork = () => co(function *() { - let bestLocal4 = network.getBestLocalIPv4(); - let bestLocal6 = network.getBestLocalIPv6(); - let upnpConf = { - remoteipv4: bestLocal4, - remoteipv6: bestLocal6, - upnp: false - }; - try { - upnpConf = yield network.upnpConf(); - upnpConf.upnp = true; - } catch (e) { - logger.error(e.stack || e); + // Reconfigure the network if it has not been initialized yet + if (!server.conf.remoteipv4 && !server.conf.remoteipv6 && !server.conf.remotehost) { + let bestLocal4 = network.getBestLocalIPv4(); + let bestLocal6 = network.getBestLocalIPv6(); + let upnpConf = { + remoteipv4: bestLocal4, + remoteipv6: bestLocal6, + upnp: false + }; + try { + upnpConf = yield network.upnpConf(); + upnpConf.upnp = true; + } catch (e) { + logger.error(e.stack || e); + } + let randomPort = network.getRandomPort(server.conf); + _.extend(server.conf, { + ipv4: bestLocal4, + ipv6: bestLocal6, + port: randomPort, + remoteipv4: upnpConf.remoteipv4, + remoteipv6: upnpConf.remoteipv6, + remoteport: randomPort, + upnp: upnpConf.upnp + }); + yield server.dal.saveConf(server.conf); + pluggedConfP = co(function *() { + yield bmapi.closeConnections(); + yield server.loadConf(); + bmapi = yield bma(server, null, true); + }); } - let randomPort = network.getRandomPort(server.conf); - _.extend(server.conf, { - ipv4: bestLocal4, - ipv6: bestLocal6, - port: randomPort, - remoteipv4: upnpConf.remoteipv4, - remoteipv6: upnpConf.remoteipv6, - remoteport: randomPort, - upnp: upnpConf.upnp - }); - yield server.dal.saveConf(server.conf); - pluggedConfP = co(function *() { - yield bmapi.closeConnections(); - yield server.loadConf(); - bmapi = yield bma(server, null, true); - }); return {}; }); this.startSync = (req) => co(function *() { - // Synchronize - var remote = new Synchroniser(server, req.body.host, parseInt(req.body.port), server.conf, false); - remote.pipe(es.mapSync(function(data) { + let sync = server.synchronize(req.body.host, parseInt(req.body.port), parseInt(req.body.to), parseInt(req.body.chunkLen)); + sync.flow.pipe(es.mapSync(function(data) { // Broadcast block that.push(data); })); - yield remote.sync(parseInt(req.body.to), parseInt(req.body.chunkLen)); + yield sync.syncPromise; return {}; }); diff --git a/app/controllers/wot.js b/app/controllers/wot.js index 6a3857eaf9cf39135d69faa5bf0dc1f2c6965b4c..d24ef622774399f65604b1484e3cb94c2a3dd283 100644 --- a/app/controllers/wot.js +++ b/app/controllers/wot.js @@ -1,15 +1,7 @@ "use strict"; var co = require('co'); -var async = require('async'); var _ = require('underscore'); -var Q = require('q'); -var moment = require('moment'); -var dos2unix = require('../lib/dos2unix'); var http2raw = require('../lib/streams/parsers/http2raw'); -var jsoner = require('../lib/streams/jsoner'); -var parsers = require('../lib/streams/parsers/doc'); -var es = require('event-stream'); -var http400 = require('../lib/http/http400'); var constants = require('../lib/constants'); var AbstractController = require('./abstract'); var logger = require('../lib/logger')(); @@ -63,7 +55,11 @@ function WOTBinding (server) { let cert = _.clone(signed[j]); if (!(excluding && cert.block <= excluding.number)) { cert.idty = yield server.dal.getIdentityByHashOrNull(cert.target); - validSigned.push(cert); + if (cert.idty) { + validSigned.push(cert); + } else { + logger.debug('A certification to an unknown identity was found (%s => %s)', cert.from, cert.to); + } } } idty.signed = validSigned; @@ -224,9 +220,9 @@ function WOTBinding (server) { }; }); - this.add = (req) => this.pushEntity(req, http2raw.identity, parsers.parseIdentity); + this.add = (req) => this.pushEntity(req, http2raw.identity, constants.ENTITY_IDENTITY); - this.certify = (req) => this.pushEntity(req, http2raw.certification, parsers.parseCertification); + this.certify = (req) => this.pushEntity(req, http2raw.certification, constants.ENTITY_CERTIFICATION); - this.revoke = (req) => this.pushEntity(req, http2raw.revocation, parsers.parseRevocation); + this.revoke = (req) => this.pushEntity(req, http2raw.revocation, constants.ENTITY_REVOCATION); } diff --git a/app/lib/blockGenerator.js b/app/lib/blockGenerator.js index 18a030ca548917e564b93a6566d04f2c8c7e92d9..790f1d26d6658f53fd9d74383179f43e1878ddd7 100644 --- a/app/lib/blockGenerator.js +++ b/app/lib/blockGenerator.js @@ -5,6 +5,7 @@ var co = require('co'); var Q = require('q'); var moment = require('moment'); var inquirer = require('inquirer'); +var rawer = require('./rawer'); var hashf = require('./hashf'); var constants = require('./constants'); var base58 = require('./base58'); @@ -658,6 +659,9 @@ function BlockGenerator(mainContext, prover) { } } } + // InnerHash + block.time = block.medianTime; + block.inner_hash = hashf(rawer.getBlockInnerPart(block)).toUpperCase(); return block; }); } diff --git a/app/lib/constants.js b/app/lib/constants.js index ff878304f66a28b118fa8e743586580a0df9c3b3..a3a3a5b3a4c2bd7da67669d7ccc7e37c3f1e9539 100644 --- a/app/lib/constants.js +++ b/app/lib/constants.js @@ -16,7 +16,7 @@ var SIGNATURE = "[A-Za-z0-9+\\/=]{87,88}"; var FINGERPRINT = "[A-F0-9]{64}"; var COMMENT = "[ a-zA-Z0-9-_:/;*\\[\\]()?!^\\+=@&~#{}|\\\\<>%.]{0,255}"; var UNLOCK = "(SIG\\(" + INTEGER + "\\)|XHX\\(" + INTEGER + "\\))"; -var CONDITIONS = "(&&|\\|\\|| |[()]|(SIG\\([0-9a-zA-Z]{44,45}\\)|(XHX\\([A-F0-9]{64}\\))))*"; +var CONDITIONS = "(&&|\\|\\|| |[()]|(SIG\\([0-9a-zA-Z]{43,44}\\)|(XHX\\([A-F0-9]{64}\\))))*"; //var CONDITIONS = "(&&|\|\|| |[()]|(SIG\\(\\da-zA-Z\\))|(XHX\\(" + FINGERPRINT + "\\)))*"; var BLOCK_UID = INTEGER + "-" + FINGERPRINT; var META_TS = "META:TS:" + BLOCK_UID; @@ -25,7 +25,15 @@ var IPV4_REGEXP = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9 var IPV6_REGEXP = /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b).){3}(b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b))|(([0-9A-Fa-f]{1,4}:){0,5}:((b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b).){3}(b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b))|(::([0-9A-Fa-f]{1,4}:){0,5}((b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b).){3}(b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/; module.exports = { - + + ENTITY_TRANSACTION: 'transaction', + ENTITY_BLOCK: 'block', + ENTITY_MEMBERSHIP: 'membership', + ENTITY_PEER: 'peer', + ENTITY_IDENTITY: 'identity', + ENTITY_CERTIFICATION: 'certification', + ENTITY_REVOCATION: 'revocation', + ERROR: { PEER: { @@ -254,6 +262,8 @@ module.exports = { A_MONTH: (3600 * 24 * 365.25) / 12 }, + DEFAULT_CPU: 0.6, + CONTRACT: { DEFAULT: { C: 0.007376575, diff --git a/app/lib/dal/fileDAL.js b/app/lib/dal/fileDAL.js index 61a3b487a64834dd2d958839be72bf53f0a74f28..bd2c0eb5f07403df7f2a526dad408b5fd3453834 100644 --- a/app/lib/dal/fileDAL.js +++ b/app/lib/dal/fileDAL.js @@ -2,7 +2,6 @@ var Q = require('q'); var co = require('co'); var _ = require('underscore'); -var path = require('path'); var hashf = require('../hashf'); var wotb = require('../wot'); var logger = require('../logger')('filedal'); @@ -25,7 +24,7 @@ function FileDAL(params) { let rootPath = params.home; let myFS = params.fs; - let sqlite = params.db; + let sqlite = params.dbf(); let wotbInstance = params.wotb; let that = this; @@ -1143,86 +1142,17 @@ function FileDAL(params) { yield _.values(that.newDals).map((dal) => dal.cleanCache && dal.cleanCache()); }); - this.cleanDBData = () => co(function *() { - yield _.values(that.newDals).map((dal) => dal.cleanData && dal.cleanData()); - that.wotb.resetWoT(); - var files = ['stats', 'cores', 'current']; - var dirs = ['blocks', 'ud_history', 'branches', 'certs', 'txs', 'cores', 'sources', 'links', 'ms', 'identities', 'peers', 'indicators', 'leveldb']; - return resetFiles(files, dirs); - }); - this.close = () => co(function *() { yield _.values(that.newDals).map((dal) => dal.cleanCache && dal.cleanCache()); return Q.nbind(sqlite.close, sqlite); }); - this.resetAll = function(done) { - var files = ['stats', 'cores', 'current', 'conf', directory.UCOIN_DB_NAME, directory.UCOIN_DB_NAME + '.db', directory.WOTB_FILE]; - var dirs = ['blocks', 'ud_history', 'branches', 'certs', 'txs', 'cores', 'sources', 'links', 'ms', 'identities', 'peers', 'indicators', 'leveldb']; - return resetFiles(files, dirs, done); - }; - - this.resetData = function(done) { - var files = ['stats', 'cores', 'current', directory.UCOIN_DB_NAME, directory.UCOIN_DB_NAME + '.db', directory.WOTB_FILE]; - var dirs = ['blocks', 'ud_history', 'branches', 'certs', 'txs', 'cores', 'sources', 'links', 'ms', 'identities', 'peers', 'indicators', 'leveldb']; - return resetFiles(files, dirs, done); - }; - - this.resetConf = function(done) { - var files = ['conf']; - var dirs = []; - return resetFiles(files, dirs, done); - }; - - this.resetStats = function(done) { - var files = ['stats']; - var dirs = ['ud_history']; - return resetFiles(files, dirs, done); - }; - this.resetPeers = function(done) { - var files = []; - var dirs = ['peers']; return co(function *() { that.peerDAL.removeAll(); - yield resetFiles(files, dirs); return that.close(); }) .then(() => done && done()) .catch((err) => done && done(err)); }; - - this.resetTransactions = function(done) { - var files = []; - var dirs = ['txs']; - return resetFiles(files, dirs, done); - }; - - function resetFiles(files, dirs, done) { - return co(function *() { - for (let i = 0, len = files.length; i < len; i++) { - let fName = files[i]; - // JSON file? - let existsJSON = yield myFS.exists(rootPath + '/' + fName + '.json'); - if (existsJSON) { - yield myFS.remove(rootPath + '/' + fName + '.json'); - } else { - // Normal file? - let existsFile = yield myFS.exists(rootPath + '/' + fName); - if (existsFile) { - yield myFS.remove(rootPath + '/' + fName); - } - } - } - for (let i = 0, len = dirs.length; i < len; i++) { - let dirName = dirs[i]; - let existsDir = yield myFS.exists(rootPath + '/' + dirName); - if (existsDir) { - yield myFS.removeTree(rootPath + '/' + dirName); - } - } - done && done(); - }) - .catch((err) => done && done(err)); - } } diff --git a/app/lib/dal/fileDALs/confDAL.js b/app/lib/dal/fileDALs/confDAL.js index d6671dfb7227014cf9fb82688485e666e34b4fe2..44860d663f7e151f12a5b3dfb16c5652c15aa369 100644 --- a/app/lib/dal/fileDALs/confDAL.js +++ b/app/lib/dal/fileDALs/confDAL.js @@ -37,7 +37,7 @@ function ConfDAL(rootPath, qioFS, parentCore, localDAL, AbstractStorage) { "msWindow": parseInt(conf.msWindow,10), "xpercent": parseFloat(conf.xpercent,10), "msValidity": parseInt(conf.msValidity,10), - "stepMax": parseInt(3,10), // uCoin only handles 3 step currencies for now + "stepMax": parseInt(3,10), // Duniter only handles 3 step currencies for now "medianTimeBlocks": parseInt(conf.medianTimeBlocks,10), "avgGenTime": parseInt(conf.avgGenTime,10), "dtDiffEval": parseInt(conf.dtDiffEval,10), diff --git a/app/lib/dal/sqliteDAL/BlockDAL.js b/app/lib/dal/sqliteDAL/BlockDAL.js index 84844c8729e675c50206e0afd5215d74e7084bef..0cda1a19ca5fa8380b52f69bf950d1979bb30d5e 100644 --- a/app/lib/dal/sqliteDAL/BlockDAL.js +++ b/app/lib/dal/sqliteDAL/BlockDAL.js @@ -117,10 +117,11 @@ function BlockDAL(db) { }); this.saveBlock = (block) => co(function *() { + let saved = yield saveBlockAs(block, IS_NOT_FORK); if (!current || current.number < block.number) { current = block; } - return saveBlockAs(block, IS_NOT_FORK); + return saved; }); this.saveSideBlock = (block) => diff --git a/app/lib/directory.js b/app/lib/directory.js index 8c518686fc4ad23034a2a94b9a3803f96d572137..c33f6e840f621b3769998d7c25b389559a16fe69 100644 --- a/app/lib/directory.js +++ b/app/lib/directory.js @@ -41,11 +41,11 @@ let dir = module.exports = { let home = params.home; yield someDelayFix(); if (isMemory) { - params.db = new sqlite3.Database(':memory:'); + params.dbf = () => new sqlite3.Database(':memory:'); params.wotb = require('./wot').memoryInstance(); } else { let sqlitePath = path.join(home, dir.UCOIN_DB_NAME + '.db'); - params.db = new sqlite3.Database(sqlitePath); + params.dbf = () => new sqlite3.Database(sqlitePath); params.wotb = require('./wot').fileInstance(path.join(home, dir.WOTB_FILE)); } return params; diff --git a/app/lib/entity/configuration.js b/app/lib/entity/configuration.js index bd309fda4fb240d6175a3877c6012a0b67c96a01..1d4d9d3ace33f8510763ff3a1186fc543f42980b 100644 --- a/app/lib/entity/configuration.js +++ b/app/lib/entity/configuration.js @@ -15,7 +15,7 @@ var defaultConf = function() { "remoteport": constants.NETWORK.DEFAULT_PORT, "salt": "", "passwd": "", - "cpu": 0.9, + "cpu": constants.DEFAULT_CPU, "upInterval": 3600 * 1000, "c": constants.CONTRACT.DEFAULT.C, "dt": constants.CONTRACT.DEFAULT.DT, diff --git a/app/lib/network.js b/app/lib/network.js index 63a9b211432e231af97973ac82c0ef4bf8f684d7..b90e204b38e320fad04db7acbe5f110d8843c0d1 100644 --- a/app/lib/network.js +++ b/app/lib/network.js @@ -143,7 +143,7 @@ module.exports = { var listenings = interfaces.map(() => false); if (httpServers.length == 0){ - throw 'uCoin does not have any interface to listen to.'; + throw 'Duniter does not have any interface to listen to.'; } // Return API diff --git a/app/lib/proof.js b/app/lib/proof.js index 4a3040cbc129758df4e7a3d711d7e443993e62e8..8519b47c6deb11c850662d08975b128ddfee36cc 100644 --- a/app/lib/proof.js +++ b/app/lib/proof.js @@ -25,7 +25,7 @@ process.on('message', function(stuff){ var nbZeros = stuff.zeros; var pair = stuff.pair; var forcedTime = stuff.forcedTime; - var cpu = conf.cpu || 1; + var cpu = conf.cpu || constants.DEFAULT_CPU; var highMark = stuff.highMark; async.waterfall([ function(next) { diff --git a/app/lib/streams/bma.js b/app/lib/streams/bma.js index 298ded516472ae5f08dc7b9b8e54bc37c6abfa4c..3786961e2bbe55c910e185359ca47c553198608a 100644 --- a/app/lib/streams/bma.js +++ b/app/lib/streams/bma.js @@ -26,7 +26,7 @@ module.exports = function(server, interfaces, httpLogs) { } } - return network.createServersAndListen('uCoin server', interfaces, httpLogs, null, (app, httpMethods) => { + return network.createServersAndListen('Duniter server', interfaces, httpLogs, null, (app, httpMethods) => { var node = require('../../controllers/node')(server); var blockchain = require('../../controllers/blockchain')(server); @@ -93,8 +93,9 @@ module.exports = function(server, interfaces, httpLogs) { wssBlock.on('connection', function connection(ws) { co(function *() { currentBlock = yield server.dal.getCurrent(); - wssBlock.broadcast(JSON.stringify(sanitize(currentBlock, dtos.Block))); - ws.send(JSON.stringify(sanitize(currentBlock, dtos.Block))); + if (currentBlock) { + ws.send(JSON.stringify(sanitize(currentBlock, dtos.Block))); + } }); }); diff --git a/app/lib/streams/webmin.js b/app/lib/streams/webmin.js index 18acf760506786b49778a168089bc851d03a2633..9fe7f418cb742a4699d0972fb31ee0520fb8b96a 100644 --- a/app/lib/streams/webmin.js +++ b/app/lib/streams/webmin.js @@ -13,9 +13,9 @@ module.exports = function(dbConf, overConf, interfaces, httpLogs) { var webminCtrl = require('../../controllers/webmin.controller')(dbConf, overConf); - var fullPath = path.join(__dirname, '../../../ui/package/public'); + var fullPath = path.join(__dirname, '../../../web-ui/public'); - return network.createServersAndListen('uCoin web admin', interfaces, httpLogs, fullPath, (app, httpMethods) => { + let httpLayer = network.createServersAndListen('Duniter web admin', interfaces, httpLogs, fullPath, (app, httpMethods) => { httpMethods.httpGET( '/webmin/summary', webminCtrl.summary, dtos.AdminSummary); httpMethods.httpPOST( '/webmin/key/preview', webminCtrl.previewPubkey, dtos.PreviewPubkey); @@ -120,4 +120,9 @@ module.exports = function(dbConf, overConf, interfaces, httpLogs) { } })); }); + + return { + httpLayer: httpLayer, + webminCtrl: webminCtrl + }; }; diff --git a/app/lib/wizard.js b/app/lib/wizard.js index d519a7ff58cc85368c9131d1740f2c6d95ce1cfe..bde1f9d83c266329bb33fa0f8a6466fe2f6669c5 100644 --- a/app/lib/wizard.js +++ b/app/lib/wizard.js @@ -232,9 +232,9 @@ function upnpResolve(noupnp, done) { return co(function *() { try { let conf = yield network.upnpConf(noupnp); - done(null, false, conf); + done(null, true, conf); } catch (err) { - done(null, true, {}); + done(null, false, {}); } }); } @@ -483,6 +483,7 @@ function getRemoteNetworkOperations(conf, remoteipv4, remoteipv6, autoconf) { next(); }); } else { + conf.remoteipv6 = answers.remoteipv6; next(); } }); diff --git a/app/service/PeeringService.js b/app/service/PeeringService.js index c55afb203c5df2ccbdfb15d00efd3e213f4f25ec..4d0b5f8eaf0615f7ce5efe9d285c65924fe0a60b 100644 --- a/app/service/PeeringService.js +++ b/app/service/PeeringService.js @@ -220,26 +220,19 @@ function PeeringService(server) { block: targetBlock ? [targetBlock.number, targetBlock.hash].join('-') : constants.PEER.SPECIAL_BLOCK, endpoints: [endpoint] }; - var raw1 = dos2unix(new Peer(p1).getRaw()); var raw2 = dos2unix(new Peer(p2).getRaw()); - logger.info('External access:', new Peer(raw1 == raw2 ? p1 : p2).getURL()); - if (raw1 != raw2) { - logger.debug('Generating server\'s peering entry based on block#%s...', p2.block.split('-')[0]); - p2.signature = yield Q.nfcall(server.sign, raw2); - p2.pubkey = selfPubkey; - p2.documentType = 'peer'; - // Submit & share with the network - yield server.submitP(p2, false); - } else { - p1.documentType = 'peer'; - // Share with the network - server.push(p1); - } + logger.info('External access:', new Peer(p2).getURL()); + logger.debug('Generating server\'s peering entry based on block#%s...', p2.block.split('-')[0]); + p2.signature = yield Q.nfcall(server.sign, raw2); + p2.pubkey = selfPubkey; + p2.documentType = 'peer'; + // Submit & share with the network + yield server.submitP(p2, false); let selfPeer = yield dal.getPeer(selfPubkey); // Set peer's statut to UP selfPeer.documentType = 'selfPeer'; yield that.peer(selfPeer); - server.push(selfPeer); + server.streamPush(selfPeer); logger.info("Next peering signal in %s min", signalTimeInterval / 1000 / 60); done && done(); return selfPeer; @@ -309,6 +302,7 @@ function PeeringService(server) { try { logger.debug('Crawling peers of %s %s', aPeer.pubkey.substr(0, 6), aPeer.getNamedURL()); let node = yield aPeer.connectP(); + yield checkPeerValidity(aPeer, node); //let remotePeer = yield Q.nbind(node.network.peering.get)(); let json = yield Q.nbind(node.network.peering.peers.get, node)({ leaves: true }); for (let i = 0, len = json.leaves.length; i < len; i++) { @@ -345,6 +339,7 @@ function PeeringService(server) { // Now we test let node = yield Q.nfcall(p.connect); let peering = yield Q.nfcall(node.network.peering.get); + yield checkPeerValidity(p, node); // The node answered, it is no more DOWN! logger.info('Node %s (%s:%s) is UP!', p.pubkey.substr(0, 6), p.getHostPreferDNS(), p.getPort()); yield dal.setPeerUP(p.pubkey); @@ -404,6 +399,35 @@ function PeeringService(server) { return waitRemaining; } + function checkPeerValidity(p, node) { + return co(function *() { + try { + let document = yield Q.nfcall(node.network.peering.get); + let thePeer = Peer.statics.peerize(document); + let goodSignature = that.checkPeerSignature(thePeer); + if (!goodSignature) { + throw 'Signature from a peer must match'; + } + if (p.currency !== thePeer.currency) { + throw 'Currency has changed from ' + p.currency + ' to ' + thePeer.currency; + } + if (p.pubkey !== thePeer.pubkey) { + throw 'Public key of the peer has changed from ' + p.pubkey + ' to ' + thePeer.pubkey; + } + let sp1 = p.block.split('-'); + let sp2 = thePeer.block.split('-'); + let blockNumber1 = parseInt(sp1[0]); + let blockNumber2 = parseInt(sp2[0]); + if (blockNumber2 < blockNumber1) { + throw 'Signature date has changed from block ' + blockNumber1 + ' to older block ' + blockNumber2; + } + } catch (e) { + logger.warn(e); + throw { code: "E_DUNITER_PEER_CHANGED" }; + } + }); + } + function syncBlock(callback, pubkey) { currentSyncP = co(function *() { let current = yield dal.getCurrentBlockOrNull(); @@ -420,6 +444,7 @@ function PeeringService(server) { try { let node = yield Q.nfcall(p.connect); node.pubkey = p.pubkey; + yield checkPeerValidity(p, node); let dao = pulling.abstractDao({ // Get the local blockchain current block @@ -450,7 +475,10 @@ function PeeringService(server) { }), // Simulate the adding of a single new block on local blockchain - applyMainBranch: (block) => server.BlockchainService.submitBlock(block, true, constants.FORK_ALLOWED), + applyMainBranch: (block) => co(function *() { + let addedBlock = yield server.BlockchainService.submitBlock(block, true, constants.FORK_ALLOWED); + server.streamPush(addedBlock); + }), // Eventually remove forks later on removeForks: () => Q(), @@ -497,7 +525,8 @@ function PeeringService(server) { function isConnectionError(err) { return err && ( - err.code == "EINVAL" + err.code == "E_DUNITER_PEER_CHANGED" + || err.code == "EINVAL" || err.code == "ECONNREFUSED" || err.code == "ETIMEDOUT" || (err.httpCode !== undefined && err.httpCode !== 404)); diff --git a/appveyor.yml b/appveyor.yml index 177d9e74fbdcda1b7f9b362619800825d00fde3b..ea30e94d742cf25eb8300d35f95674f9b1b4182a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,11 +1,13 @@ environment: matrix: - nodejs_version: "5.9.1" + ADDON_VERSION: "47" platform: - x64 install: + - echo %ADDON_VERSION% - ps: Install-Product node $env:nodejs_version $env:platform - node -v - npm install -g npm @@ -18,36 +20,41 @@ test_script: build: off after_test: + - if [%APPVEYOR_REPO_TAG_NAME%] neq [] powershell .\\ci\\appveyor\\inno_setup.ps1 - if [%APPVEYOR_REPO_TAG_NAME%] neq [] rd /s /q %APPDATA%\..\Local\NuGet\Cache - - if [%APPVEYOR_REPO_TAG_NAME%] neq [] choco install -y 7zip InnoSetup - - if [%APPVEYOR_REPO_TAG_NAME%] neq [] set PATH="C:\Program Files\7-Zip";"C:\Program Files (x86)\Inno Setup 5";%PATH% - # GUI Version + - if [%APPVEYOR_REPO_TAG_NAME%] neq [] set PATH="C:\Program Files (x86)\Inno Setup 5";%PATH% + - if [%APPVEYOR_REPO_TAG_NAME%] neq [] git submodule init + - if [%APPVEYOR_REPO_TAG_NAME%] neq [] git submodule update + - if [%APPVEYOR_REPO_TAG_NAME%] neq [] cd "web-ui" + - if [%APPVEYOR_REPO_TAG_NAME%] neq [] dir + - if [%APPVEYOR_REPO_TAG_NAME%] neq [] npm install + - if [%APPVEYOR_REPO_TAG_NAME%] neq [] cd .. - if [%APPVEYOR_REPO_TAG_NAME%] neq [] set SRC=%cd% - if [%APPVEYOR_REPO_TAG_NAME%] neq [] echo %SRC% - if [%APPVEYOR_REPO_TAG_NAME%] neq [] copy misc\MSVSVersion.py %APPDATA%\npm\node_modules\nw-gyp\gyp\pylib\gyp\MSVSVersion.py - - if [%APPVEYOR_REPO_TAG_NAME%] neq [] set NW_VERSION=0.13.0-rc3 - - if [%APPVEYOR_REPO_TAG_NAME%] neq [] set NW_RELEASE=v0.13.0-rc3 + - if [%APPVEYOR_REPO_TAG_NAME%] neq [] set NW_VERSION=0.14.5 + - if [%APPVEYOR_REPO_TAG_NAME%] neq [] set NW_RELEASE=v0.14.5 - if [%APPVEYOR_REPO_TAG_NAME%] neq [] echo %NW_RELEASE% - if [%APPVEYOR_REPO_TAG_NAME%] neq [] cd node_modules/wotb - if [%APPVEYOR_REPO_TAG_NAME%] neq [] npm install --build-from-source - if [%APPVEYOR_REPO_TAG_NAME%] neq [] node-pre-gyp --runtime=node-webkit --target=%NW_VERSION% --msvs_version=2015 configure - if [%APPVEYOR_REPO_TAG_NAME%] neq [] node-pre-gyp --runtime=node-webkit --target=%NW_VERSION% --msvs_version=2015 build - - if [%APPVEYOR_REPO_TAG_NAME%] neq [] copy %cd%\lib\binding\Release\node-webkit-%NW_RELEASE%-win32-x64\wotb.node %cd%\lib\binding\Release\node-v47-win32-x64\wotb.node /Y + - if [%APPVEYOR_REPO_TAG_NAME%] neq [] copy %cd%\lib\binding\Release\node-webkit-%NW_RELEASE%-win32-x64\wotb.node %cd%\lib\binding\Release\node-v%ADDON_VERSION%-win32-x64\wotb.node /Y - if [%APPVEYOR_REPO_TAG_NAME%] neq [] cd ../naclb - if [%APPVEYOR_REPO_TAG_NAME%] neq [] npm install --build-from-source - if [%APPVEYOR_REPO_TAG_NAME%] neq [] node-pre-gyp --runtime=node-webkit --target=%NW_VERSION% --msvs_version=2015 configure - if [%APPVEYOR_REPO_TAG_NAME%] neq [] node-pre-gyp --runtime=node-webkit --target=%NW_VERSION% --msvs_version=2015 build - - if [%APPVEYOR_REPO_TAG_NAME%] neq [] copy %cd%\lib\binding\Release\node-webkit-%NW_RELEASE%-win32-x64\naclb.node %cd%\lib\binding\Release\node-v47-win32-x64\naclb.node /Y + - if [%APPVEYOR_REPO_TAG_NAME%] neq [] copy %cd%\lib\binding\Release\node-webkit-%NW_RELEASE%-win32-x64\naclb.node %cd%\lib\binding\Release\node-v%ADDON_VERSION%-win32-x64\naclb.node /Y - if [%APPVEYOR_REPO_TAG_NAME%] neq [] cd ../scryptb - if [%APPVEYOR_REPO_TAG_NAME%] neq [] npm install --build-from-source - if [%APPVEYOR_REPO_TAG_NAME%] neq [] node-pre-gyp --runtime=node-webkit --target=%NW_VERSION% --msvs_version=2015 configure - if [%APPVEYOR_REPO_TAG_NAME%] neq [] node-pre-gyp --runtime=node-webkit --target=%NW_VERSION% --msvs_version=2015 build - - if [%APPVEYOR_REPO_TAG_NAME%] neq [] copy %cd%\lib\binding\Release\node-webkit-%NW_RELEASE%-win32-x64\scryptb.node %cd%\lib\binding\Release\node-v47-win32-x64\scryptb.node /Y + - if [%APPVEYOR_REPO_TAG_NAME%] neq [] copy %cd%\lib\binding\Release\node-webkit-%NW_RELEASE%-win32-x64\scryptb.node %cd%\lib\binding\Release\node-v%ADDON_VERSION%-win32-x64\scryptb.node /Y - if [%APPVEYOR_REPO_TAG_NAME%] neq [] cd ../sqlite3b - if [%APPVEYOR_REPO_TAG_NAME%] neq [] npm install --build-from-source - if [%APPVEYOR_REPO_TAG_NAME%] neq [] node-pre-gyp --runtime=node-webkit --target=%NW_VERSION% --msvs_version=2015 configure - if [%APPVEYOR_REPO_TAG_NAME%] neq [] node-pre-gyp --runtime=node-webkit --target=%NW_VERSION% --msvs_version=2015 build - - if [%APPVEYOR_REPO_TAG_NAME%] neq [] copy %cd%\lib\binding\Release\node-webkit-%NW_RELEASE%-win32-x64\node_sqlite3.node %cd%\lib\binding\Release\node-v47-win32-x64\node_sqlite3.node /Y + - if [%APPVEYOR_REPO_TAG_NAME%] neq [] copy %cd%\lib\binding\Release\node-webkit-%NW_RELEASE%-win32-x64\node_sqlite3.node %cd%\lib\binding\Release\node-v%ADDON_VERSION%-win32-x64\node_sqlite3.node /Y - if [%APPVEYOR_REPO_TAG_NAME%] neq [] cd ../.. - if [%APPVEYOR_REPO_TAG_NAME%] neq [] npm prune --production - if [%APPVEYOR_REPO_TAG_NAME%] neq [] cd .. @@ -62,8 +69,8 @@ after_test: - if [%APPVEYOR_REPO_TAG_NAME%] neq [] mkdir %cd%\duniter_release\sources - if [%APPVEYOR_REPO_TAG_NAME%] neq [] xcopy %SRC%\gui\* %cd%\duniter_release\nw\ /s /e - if [%APPVEYOR_REPO_TAG_NAME%] neq [] xcopy %SRC%\* %cd%\duniter_release\sources\ /s /e - - if [%APPVEYOR_REPO_TAG_NAME%] neq [] rd /s /q %cd%\duniter_release\sources\ui\package\node_modules - - if [%APPVEYOR_REPO_TAG_NAME%] neq [] rd /s /q %cd%\duniter_release\sources\ui\package\bower_components + - if [%APPVEYOR_REPO_TAG_NAME%] neq [] rd /s /q %cd%\duniter_release\sources\web-ui\node_modules + - if [%APPVEYOR_REPO_TAG_NAME%] neq [] rd /s /q %cd%\duniter_release\sources\web-ui\bower_components - if [%APPVEYOR_REPO_TAG_NAME%] neq [] iscc %cd%\duniter_release\sources\duniter.iss /DROOT_PATH=%cd%\duniter_release - if [%APPVEYOR_REPO_TAG_NAME%] neq [] move %cd%\duniter_release\Duniter.exe %cd%\duniter\duniter-%APPVEYOR_REPO_TAG_NAME%-windows-x64.exe @@ -72,7 +79,7 @@ artifacts: name: Duniter deploy: - release: v0.20.0a57 + release: v0.20.0a84 provider: GitHub auth_token: secure: Vp/M0r0i1yhGR2nhrPWEbTiDIF6r0cmwbNDFZUzdFe5clWxPXtuC0lgIpOQI78zt diff --git a/bin/daemon b/bin/daemon index 48ffe57e80e6a8b0d9c09e7271f0241b3bd2d1fb..e0ba25c994380740dc5cf00d0fcaae7731263ca3 100755 --- a/bin/daemon +++ b/bin/daemon @@ -1,19 +1,16 @@ #!/usr/bin/env node "use strict"; -var directory = require('../app/lib/directory'); -var path = require('path'); +const directory = require('../app/lib/directory'); +const path = require('path'); +const spawn = require('child_process').spawn; -var daemon = require("daemonize2").setup({ - main: "ucoind", - name: directory.INSTANCE_NAME, - pidfile: path.join(directory.INSTANCE_HOME, "app.pid") -}); +var daemon = getDaemon('start'); switch (process.argv[2]) { case "start": - daemon.start(); + start(daemon); break; case "stop": @@ -21,18 +18,33 @@ switch (process.argv[2]) { break; case "restart": - daemon = require("daemonize2").setup({ - main: "ucoind", - name: directory.INSTANCE_NAME, - pidfile: path.join(directory.INSTANCE_HOME, "app.pid"), - - // We must redefine the main argument to 'start' because uCoin will receive it as command argument and does not - // know about 'restart' command. - argv: process.argv.slice(2).map((arg, index) => index == 0 ? 'start' : arg) + daemon = getDaemon('start'); + daemon.stop(function(err) { + err && console.error(err); + start(daemon); }); + break; + + case "webwait": + daemon = getDaemon('webwait'); + start(daemon); + break; + + case "webstart": + daemon = getDaemon('webstart'); + start(daemon); + break; + + case "webstop": + daemon = getDaemon(); + daemon.stop(); + break; + + case "webrestart": + daemon = getDaemon('webstart'); daemon.stop(function(err) { err && console.error(err); - daemon.start(); + start(daemon); }); break; @@ -42,5 +54,46 @@ switch (process.argv[2]) { break; default: - console.log("Usage: [start|stop|restart]"); + console.log("Usage: [webstart|webwait|webstop|webrestart|start|stop|restart]"); +} + +function getDaemon(overrideCommand) { + return require("daemonize2").setup({ + main: "ucoind", + name: directory.INSTANCE_NAME, + pidfile: path.join(directory.INSTANCE_HOME, "app.pid"), + + // We must redefine the main argument to 'start' because Duniter will receive it as command argument and does not + // know about 'restart' command. + argv: getCommand(overrideCommand) + }); +} + +function getCommand(overrideCommand) { + return process.argv.slice(2).map((arg, index) => index == 0 && overrideCommand ? overrideCommand : arg); +} + +function getFullCommand(overrideCommand) { + let ucoind = path.resolve(path.dirname(process.argv[1]), './ucoind'); + return [ucoind].concat(getCommand(overrideCommand)); +} + +function start(daemonToStart) { + let checkConf = spawn(process.argv[0], getFullCommand('check-config')); + let echos = []; + + // Error messages + checkConf.stdout.on('data', (data) => echos.push(data)); + // checkConf.stderr.on('data', (data) => console.error(data.toString('utf8'))); + + // Result + checkConf.on('close', (code) => { + if (code !== 0 && code !== '0' && code !== '' && code !== null && code !== undefined) { + console.log('Error code \'%s\'', code); + echos.forEach((echo) => console.log(echo.toString('utf8').replace(/\n$/, ''))); + console.log('You have configuration issues. Please fix them and retry to start your node with `duniter restart` or `duniter webrestart`.'); + } else { + daemonToStart.start(); + } + }); } diff --git a/bin/ucoind b/bin/ucoind index 7355a4be3e0ffeb25107d9b1ea15bab5202532ca..56c48ab2bb4dcc1982227a1d64c03e9d30d89c6a 100755 --- a/bin/ucoind +++ b/bin/ucoind @@ -23,7 +23,6 @@ var multicaster = require('../app/lib/streams/multicaster'); var signature = require('../app/lib/signature'); var crypto = require('../app/lib/crypto'); var base58 = require('../app/lib/base58'); -var Synchroniser = require('../app/lib/sync'); var pjson = require('../package.json'); var ucoin = require('./../index'); var Peer = require('../app/lib/entity/peer'); @@ -36,7 +35,7 @@ program .version(pjson.version) .usage('<command> [options]') - .option('--home <path>', 'Path to uCoin HOME (defaults to "$HOME/.config/duniter").') + .option('--home <path>', 'Path to Duniter HOME (defaults to "$HOME/.config/duniter").') .option('-d, --mdb <name>', 'Database name (defaults to "ucoin_default").') .option('--autoconf', 'With `init` command, will guess the best network and key options witout aksing for confirmation') @@ -95,12 +94,17 @@ program program .command('start') - .description('Start uCoin server.') + .description('Start Duniter server.') .action(service(serverStart)); +program + .command('webwait') + .description('Start Duniter web admin.') + .action(webWait); + program .command('webstart') - .description('Start uCoin server + web admin.') + .description('Start Duniter web admin + immediately start the server.') .action(webStart); program @@ -119,7 +123,7 @@ program program .command('sync [host] [port] [to]') - .description('Synchronize blockchain from a remote uCoin node') + .description('Synchronize blockchain from a remote Duniter node') .action(service(function (host, port, to, server, conf) { if (!host) { throw 'Host is required.'; @@ -129,7 +133,14 @@ program } return co(function *() { try { - yield sync(server, host, port, conf, to); + let cautious; + if (program.nocautious) { + cautious = false; + } + if (program.cautious) { + cautious = true; + } + yield server.synchronize(host, port, parseInt(to), 0, !program.nointeractive, cautious, program.nopeers); } catch (err) { logger.error(err.stack || err.message); } @@ -138,19 +149,6 @@ program }); })); -function sync(server, host, port, conf, to) { - // Synchronize - var remote = new Synchroniser(server, host, port, conf, !program.nointeractive); - let cautious; - if (program.nocautious) { - cautious = false; - } - if (program.cautious) { - cautious = true; - } - return remote.sync(parseInt(to), 0, cautious, program.nopeers); -} - program .command('peer [host] [port]') .description('Exchange peerings with another node') @@ -192,7 +190,7 @@ program program .command('forward [host] [port] [to]') - .description('Forward local blockchain to a remote uCoin node') + .description('Forward local blockchain to a remote Duniter node') .action(service(function (host, port, to, server) { var remoteCurrent; @@ -405,7 +403,7 @@ program .catch(function(err) { logger.warn(err.message || err); server.disconnect(); - process.exit(); + process.exit(1); }); })); @@ -438,45 +436,44 @@ program .command('reset [config|data|peers|tx|stats|all]') .description('Reset configuration, data, peers, transactions or everything in the database') .action((type) => { - let init = type != 'peers' ? connect : service; + let init = type == 'data' ? server : connect; init(function (server) { - if(!~['config', 'data', 'peers', 'tx', 'stats', 'all'].indexOf(type)){ - logger.error('Bad command: usage `reset config`, `reset data`, `reset peers`, `reset tx`, `reset stats` or `reset all`'); + if(!~['config', 'data', 'peers', 'stats', 'all'].indexOf(type)){ + logger.error('Bad command: usage `reset config`, `reset data`, `reset peers`, `reset stats` or `reset all`'); server.disconnect(); return; } - if(type == 'data'){ - server.resetData(resetDone(server, 'Data successfully reseted.')); - } - if(type == 'peers'){ - server.resetPeers(resetDone(server, 'Peers successfully reseted.')); - } - if(type == 'tx'){ - server.resetTxs(resetDone(server, 'Transactions successfully reseted.')); - } - if(type == 'stats'){ - server.resetStats(resetDone(server, 'Stats successfully reseted.')); - } - if(type == 'config'){ - server.resetConf(resetDone(server, 'Configuration successfully reseted.')); - } - if(type == 'all'){ - server.reset(resetDone(server, 'Data & Configuration successfully reseted.')); - } + co(function*(){ + try { + if(type == 'data'){ + yield server.resetData(); + logger.warn('Data successfully reseted.'); + } + if(type == 'peers'){ + yield server.resetPeers(); + logger.warn('Peers successfully reseted.'); + } + if(type == 'stats'){ + yield server.resetStats(); + logger.warn('Stats successfully reseted.'); + } + if(type == 'config'){ + yield server.resetConf(); + logger.warn('Configuration successfully reseted.'); + } + if(type == 'all'){ + yield server.resetAll(); + logger.warn('Data & Configuration successfully reseted.'); + } + } catch (e) { + logger.error(e); + } + server.disconnect(); + process.exit(); + }); }, type != 'peers')(type); }); -function resetDone(server, msg) { - return function(err) { - if(err) - logger.error(err); - else - logger.warn(msg); - server.disconnect(); - process.exit(); - } -} - function serverStart(server, conf) { return co(function *() { @@ -550,7 +547,7 @@ function getBootstrapOperations(host, port, server, conf) { ops = ops.concat([ function(next) { // Reset data - server.reset(next); + server.resetAll(next); }, function(next) { conf.upnp = !program.noupnp; @@ -732,6 +729,27 @@ function connect(callback, useDefaultConf) { }; } +/** + * Super basic server with only its home path set + * @param callback + * @param useDefaultConf + * @returns {Function} + */ +function server(callback, useDefaultConf) { + return function () { + var cbArgs = arguments; + var dbName = program.mdb || "duniter_default"; + var dbHome = program.home; + + var server = ucoin({ home: dbHome, name: dbName }, commandLineConf()); + + cbArgs.length--; + cbArgs[cbArgs.length++] = server; + cbArgs[cbArgs.length++] = server.conf; + callback.apply(this, cbArgs); + }; +} + function service(callback, nologs) { return function () { @@ -807,7 +825,26 @@ program process.exit(); }); +function webWait() { + return co(function *() { + let webminapi = yield webInit(); + return webminapi.httpLayer.openConnections(); + }) + .catch(mainError); +} + function webStart() { + return co(function *() { + let webminapi = yield webInit(); + yield webminapi.httpLayer.openConnections(); + yield webminapi.webminCtrl.startHTTP(); + yield webminapi.webminCtrl.startAllServices(); + yield webminapi.webminCtrl.regularUPnP(); + }) + .catch(mainError); +} + +function webInit() { return co(function *() { var dbName = program.mdb; var dbHome = program.home; @@ -818,9 +855,7 @@ function webStart() { // Add log files for this instance logger.addHomeLogs(params.home); } - let webminapi = yield ucoin.statics.enableHttpAdmin({ home: dbHome, name: dbName, memory: program.memory }, commandLineConf(), false, program.webmhost, program.webmport); - return webminapi.openConnections(); - + return yield ucoin.statics.enableHttpAdmin({ home: dbHome, name: dbName, memory: program.memory }, commandLineConf(), false, program.webmhost, program.webmport); }); } @@ -828,13 +863,18 @@ program.parse(process.argv); if (program.args.length == 0) { - console.log('No command given, using default: ucoind webstart'); + console.log('No command given, using default: ucoind webwait'); return co(function *() { try { - yield webStart(); + yield webWait(); } catch (e) { logger.error(e); process.exit(); } }); } + +function mainError(err) { + logger.error(err.code || err.message || err); + process.exit(); +} diff --git a/ci/appveyor/inno_setup.ps1 b/ci/appveyor/inno_setup.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..aaae7c7b59ca800483ed71353cf94bff00937f92 --- /dev/null +++ b/ci/appveyor/inno_setup.ps1 @@ -0,0 +1,6 @@ +$exePath = "$env:USERPROFILE\innosetup-5.5.9-unicode.exe" +Write-Host "Downloading InnoSetup 5.5.9..." +(New-Object Net.WebClient).DownloadFile('http://files.jrsoftware.org/is/5/innosetup-5.5.9-unicode.exe', $exePath) +Write-Host "Installing..." +cmd /c start /wait $exePath /silent +Write-Host "Installed InnoSetup 5.5.9" -ForegroundColor Green \ No newline at end of file diff --git a/ci/arm/make_arm_deb_package.sh b/ci/arm/make_arm_deb_package.sh new file mode 100644 index 0000000000000000000000000000000000000000..12e96bb7111d9269bde20235fd2236e390f02488 --- /dev/null +++ b/ci/arm/make_arm_deb_package.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +ARCH=$1 +DUNITER_VER=$2 +NVER="v5.9.1" +DUNITER_DEB_VER=" $DUNITER_VER" +echo "$ARCH" +echo "$NVER" +echo "$DUNITER_VER" +echo "$DUNITER_DEB_VER" + +echo "Downloading Nodejs" +wget http://nodejs.org/dist/${NVER}/node-${NVER}-linux-${ARCH}.tar.gz +echo "Extracting Nodejs" +tar xzf node-${NVER}-linux-${ARCH}.tar.gz +mv node-${NVER}-linux-${ARCH} node +rm node-${NVER}-linux-${ARCH}.tar.gz + +echo "npm install" +node/bin/npm install +node/bin/npm prune --production +SRC=`pwd` +echo $SRC +cd .. +mkdir -p duniter_release/sources +cp -R ${SRC}/* duniter_release/sources/ +rm -Rf duniter_release/sources/web-ui/node_modules +rm -Rf duniter_release/sources/web-ui/bower_components + +# Creating DEB packaging +mv duniter_release/sources/ci/travis/debian duniter-${ARCH} +mkdir -p duniter-${ARCH}/opt/duniter/ +chmod 755 duniter-${ARCH}/DEBIAN/post* +chmod 755 duniter-${ARCH}/DEBIAN/pre* +sed -i "s/Version:.*/Version:$DUNITER_DEB_VER/g" duniter-${ARCH}/DEBIAN/control +cd duniter_release/sources +pwd +rm -Rf .git +echo "Zipping..." +zip -qr ../duniter-desktop.nw * +cd ../.. +mv duniter_release/duniter-desktop.nw duniter-${ARCH}/opt/duniter/ +echo "Making deb package" +fakeroot dpkg-deb --build duniter-${ARCH} +mv duniter-${ARCH}.deb duniter-v${DUNITER_VER}-linux-${ARCH}.deb +echo "Uploading release..." +./github-release upload -u duniter -r duniter --tag v${DUNITER_VER} --name duniter-v${DUNITER_VER}-linux-${ARCH}.deb --file ./duniter-v${DUNITER_VER}-linux-${ARCH}.deb diff --git a/ci/travis/before_deploy.sh b/ci/travis/before_deploy.sh new file mode 100755 index 0000000000000000000000000000000000000000..afef3627f110eccc116cbd52730fb11a9384f26c --- /dev/null +++ b/ci/travis/before_deploy.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash + +if [[ ! -f before_deploy ]]; then + + # Process this only once + touch before_deploy + + # Clean testing packages + npm prune --production + + cd .. + cp -R duniter gh_duniter + + #### GitHub release + cd gh_duniter + + # Install UI + cd web-ui + git submodule init + git submodule update + npm install + cd .. + + # Download Node.js + NVER=`node -v` + DUNITER_VER=`git describe --exact-match --tags $(git log -n1 --pretty='%h') | grep -Po "\d.*"` + DUNITER_VER="0.20.0dev" + DUNITER_DEB_VER=" $DUNITER_VER" + wget http://nodejs.org/dist/${NVER}/node-${NVER}-linux-x64.tar.gz + tar xzf node-${NVER}-linux-x64.tar.gz + mv node-${NVER}-linux-x64 node + rm node-${NVER}-linux-x64.tar.gz + + tar czf ../../ucoin-x64.tar.gz ./ --exclude ".git" --exclude "coverage" --exclude "test" + SRC=`pwd` + cd .. + + # Install Nw.js + mkdir ucoin_release + NW_RELEASE="v0.14.5" + NW="nwjs-${NW_RELEASE}-linux-x64" + NW_GZ="${NW}.tar.gz" + wget http://dl.nwjs.io/${NW_RELEASE}/${NW_GZ} + tar xvzf ${NW_GZ} + mv ${NW} ucoin_release/nw + cp ${SRC}/gui/* ucoin_release/nw/ + cp -R ${SRC}/ ucoin_release/sources/ + rm -Rf ucoin_release/sources/web-ui/node_modules + rm -Rf ucoin_release/sources/web-ui/bower_components + cd ucoin_release + tar czf ../duniter-x64.tar.gz * --exclude ".git" --exclude "coverage" --exclude "test" + cd .. + + # Create .deb tree + package it + mv ucoin_release/sources/ci/travis/debian duniter-x64 + mkdir -p duniter-x64/opt/duniter/ + chmod 755 duniter-x64/DEBIAN/post* + chmod 755 duniter-x64/DEBIAN/pre* + sed -i "s/Version:.*/Version:$DUNITER_DEB_VER/g" duniter-x64/DEBIAN/control + cd ucoin_release/sources + rm -Rf .git + zip -qr ../duniter-desktop.nw * + cd ../nw + zip -qr ../nw.nwb * + cd ../.. + mv ucoin_release/duniter-desktop.nw duniter-x64/opt/duniter/ + mv ucoin_release/nw.nwb duniter-x64/opt/duniter/ + fakeroot dpkg-deb --build duniter-x64 + mv duniter-x64.deb ../duniter-${TRAVIS_TAG}-${TRAVIS_OS_NAME}-x64.deb + mv duniter-x64.tar.gz ../duniter-${TRAVIS_TAG}-${TRAVIS_OS_NAME}-x64.tar.gz + pwd + ls -al + pwd + ls -al ../ + + ###### NPM release + cd ../duniter +fi diff --git a/ci/travis/debian/DEBIAN/control b/ci/travis/debian/DEBIAN/control new file mode 100644 index 0000000000000000000000000000000000000000..c225dcf315d95863eee7a62d722ccabbab19ebe2 --- /dev/null +++ b/ci/travis/debian/DEBIAN/control @@ -0,0 +1,8 @@ +Package: duniter +Version: 0.4 +Section: misc +Priority: optional +Architecture: all +Installed-Size: 200000 +Maintainer: Cedric Moreau <cem.moreau@gmail.com> +Description: Duniter desktop version diff --git a/ci/travis/debian/DEBIAN/postinst b/ci/travis/debian/DEBIAN/postinst new file mode 100755 index 0000000000000000000000000000000000000000..0984d064a30e21031af0f3910d280146da7e7d4b --- /dev/null +++ b/ci/travis/debian/DEBIAN/postinst @@ -0,0 +1,32 @@ +#!/bin/bash + +DUN_ROOT=/opt/duniter +DUN_SOURCES=$DUN_ROOT/sources +DUN_NW=$DUN_ROOT/nw +mkdir -p $DUN_SOURCES + +# Duniter sources +if [[ -f $DUN_ROOT/duniter-desktop.nw ]]; then + unzip -d $DUN_SOURCES/ $DUN_ROOT/duniter-desktop.nw +fi + +# Duniter-Desktop +if [[ -f $DUN_ROOT/nw.nwb ]]; then + unzip -d $DUN_NW $DUN_ROOT/nw.nwb + cp -R $DUN_SOURCES/gui/* $DUN_NW/ + chmod +x $DUN_NW/nw $DUN_NW/lib $DUN_NW/locales + ln -s $DUN_NW/nw /usr/bin/duniter-desktop + sed -i "s/DEB_PACKAGING=.*/DEB_PACKAGING=true/g" $DUN_SOURCES/duniter.sh +fi + +# Duniter CLI executes with embedded node +if [[ -d $DUN_SOURCES/node ]]; then + chmod 755 $DUN_SOURCES/bin/ucoind + sed -i "s/usr\/bin\/env node/opt\/duniter\/sources\/node\/bin\/node/g" $DUN_SOURCES/bin/ucoind + sed -i "s/DEB_PACKAGING=.*/DEB_PACKAGING=true/g" $DUN_SOURCES/duniter.sh +fi +# Else will execute with environment node + +ln -s $DUN_SOURCES/duniter.sh /usr/bin/duniter + +chmod +r -R $DUN_ROOT diff --git a/ci/travis/debian/DEBIAN/prerm b/ci/travis/debian/DEBIAN/prerm new file mode 100755 index 0000000000000000000000000000000000000000..d86acf239b6248bfd0870ff7013ba3f0944ad586 --- /dev/null +++ b/ci/travis/debian/DEBIAN/prerm @@ -0,0 +1,5 @@ +#!/bin/bash + +rm /usr/bin/duniter +rm /usr/bin/duniter-desktop +rm -Rf /opt/duniter diff --git a/ci/travis/debian/usr/share/applications/duniter.desktop b/ci/travis/debian/usr/share/applications/duniter.desktop new file mode 100644 index 0000000000000000000000000000000000000000..9f25ff4657325871a94e5b0095f9ed149cadbb7c --- /dev/null +++ b/ci/travis/debian/usr/share/applications/duniter.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Name=Duniter Desktop +Exec=duniter-desktop +Icon=/opt/duniter/nw/duniter.png +Type=Application +Categories=Utility diff --git a/doc/HTTP_API.md b/doc/HTTP_API.md index 8dcfecdc221893363ccd8825ab24386fe9e8a02e..5fc80a116b24a870141b0a143d356c5afbb0eab9 100644 --- a/doc/HTTP_API.md +++ b/doc/HTTP_API.md @@ -1,4 +1,4 @@ -# uCoin HTTP API +# Duniter HTTP API ## Contents @@ -56,7 +56,7 @@ ## Overview -Data is made accessible through an HTTP API mainly inspired from [OpenUDC_exchange_formats draft](https://github.com/Open-UDC/open-udc/blob/master/docs/OpenUDC_exchange_formats.draft.txt), and has been adapted to fit uCoin specificities. +Data is made accessible through an HTTP API mainly inspired from [OpenUDC_exchange_formats draft](https://github.com/Open-UDC/open-udc/blob/master/docs/OpenUDC_exchange_formats.draft.txt), and has been adapted to fit Duniter specificities. http[s]://Node[:port]/... |-- wot/ @@ -106,9 +106,9 @@ Merkle URL is a special kind of URL applicable for resources: * `network/peering/peers (GET)` -Such kind of URL returns Merkle tree hashes informations. In uCoin, Merkle trees are an easy way to detect unsynced data and where the differences come from. For example, `network/peering/peers` is a Merkle tree whose leaves are peers' key fingerprint sorted ascending way. Thus, if any new peer is added, a branch of the tree will see its hash modified and propagated to the root hash. Change is then easy to detect. +Such kind of URL returns Merkle tree hashes informations. In Duniter, Merkle trees are an easy way to detect unsynced data and where the differences come from. For example, `network/peering/peers` is a Merkle tree whose leaves are peers' key fingerprint sorted ascending way. Thus, if any new peer is added, a branch of the tree will see its hash modified and propagated to the root hash. Change is then easy to detect. -For commodity issues, this URL uses query parameters to retrieve partial data of the tree, as most of the time all the data is not required. uCoin Merkle tree has a determined number of parent nodes (given a number of leaves), which allows to ask only for interval of them. +For commodity issues, this URL uses query parameters to retrieve partial data of the tree, as most of the time all the data is not required. Duniter Merkle tree has a determined number of parent nodes (given a number of leaves), which allows to ask only for interval of them. Here is an example of members Merkle tree with 5 members (taken from [Tree Hash EXchange format (THEX)](http://web.archive.org/web/20080316033726/http://www.open-content.net/specs/draft-jchapweske-thex-02.html)): @@ -128,9 +128,9 @@ Here is an example of members Merkle tree with 5 members (taken from [Tree Hash Where A,B,C,D,E are already hashed data. -With such a tree structure, uCoin consider the tree has exactly 6 nodes: `[ROOT,H,E,F,G,E]`. Nodes are just an array, and for a Lambda Server LS1, it is easy to ask for the values of another server LS2 for level 1 (`H` and `E`, the second level): it requires nodes interval `[1;2]`. +With such a tree structure, Duniter consider the tree has exactly 6 nodes: `[ROOT,H,E,F,G,E]`. Nodes are just an array, and for a Lambda Server LS1, it is easy to ask for the values of another server LS2 for level 1 (`H` and `E`, the second level): it requires nodes interval `[1;2]`. -Hence it is quite easy for anyone who wants to check if a `Z` member joined the uCoin community as it would alter the `E` branch of the tree: +Hence it is quite easy for anyone who wants to check if a `Z` member joined the Duniter community as it would alter the `E` branch of the tree: ROOT'=H(H+E') / \ @@ -197,7 +197,7 @@ Merkle URL result with `leaf=AE4F281DF5A5D0FF3CAD6371F76D5C29B6D953EC`. } ``` -### uCoin Merkle trees leaves +### Duniter Merkle trees leaves Each tree manages different data, and has a different goal. Hence, each tree has its own rules on how are generated and sorted tree leaves. Here is a summup of such rules: @@ -1109,7 +1109,7 @@ Top block of each branch, i.e. the last received block of each branch. An array ### network/* -This URL is used for uCoin Gossip protocol (exchanging UCG messages). +This URL is used for Duniter Gossip protocol (exchanging UCG messages). #### `network/peers` **Goal** diff --git a/doc/Protocol.md b/doc/Protocol.md index 69fc43d593657b63b143ab34ea7f58cc933b0d03..0becce7374d4796949e3bce4420b59caa1cdff5e 100644 --- a/doc/Protocol.md +++ b/doc/Protocol.md @@ -1,4 +1,4 @@ -# UCP - uCoin Protocol +# UCP - Duniter Protocol > This document is still regularly updated (as of February 2015) @@ -32,7 +32,7 @@ Word | Description --------------------- | ------------- -UCP | Acronym for *UCoin Protocol*. A set of rules to create uCoin based currencies. +UCP | Acronym for *UCoin Protocol*. A set of rules to create Duniter based currencies. Signature | The cryptographical act of certifying a document using a private key. WoT | Acronym for *Web of Trust*. A groupment of individuals recognizing each other's identity through public keys and certification mechanisms UD | Acronym for *Universal Dividend*. Means money issuance **directly** and **exclusively** by and to WoT members @@ -873,7 +873,7 @@ Field | Description PROTOCOL_NAME[ OPTIONS] [...] -For example, the first written uCoin peering protocol is BASIC_MERKLED_API, which defines an HTTP API. An endpoint of such protocol would look like: +For example, the first written Duniter peering protocol is BASIC_MERKLED_API, which defines an HTTP API. An endpoint of such protocol would look like: BASIC_MERKLED_API[ DNS][ IPv4][ IPv6] PORT @@ -1426,10 +1426,10 @@ TRUE UCP does not imposes a particular API to deal with UCP data. Instead, UCP prefers to allow for any API definition using [Peer](#peer) document, and then leting peers deal themselves with the API(s) they prefer. -At this stage, only [uCoin HTTP API](/HTTP_API.md) (named BASIC_MERKLED_API) is known as a valid UCP API. +At this stage, only [Duniter HTTP API](/HTTP_API.md) (named BASIC_MERKLED_API) is known as a valid UCP API. ## References * [Relative Money Theory](http://fr.wikipedia.org/wiki/Th%C3%A9orie_relative_de_la_monnaie), the theoretical reference behind Universal Dividend -* [OpenUDC](www.openudc.org), the inspiration project of uCoin +* [OpenUDC](www.openudc.org), the inspiration project of Duniter * [Bitcoin](https://github.com/bitcoin/bitcoin), the well known crypto-currency system \ No newline at end of file diff --git a/doc/architecture.md b/doc/architecture.md index c3a7c246f42a71e4e3250fcda11192e85d69e382..e878af908e4cf8c276420c0c11ba0d47186b5d2a 100644 --- a/doc/architecture.md +++ b/doc/architecture.md @@ -2,14 +2,14 @@ ``` ------------- - | BMA | uCoin Basic Merkled API (HTTP) + | BMA | Duniter Basic Merkled API (HTTP) ------------- ▲ | Trought | ------------- - | ucoin | The software + | duniter | The software ------------- | | @@ -17,7 +17,7 @@ | ▼ ----------------- - | UCP | uCoin protocol + | UCP | Duniter protocol | ------------- | | | Ed25519 | | Cryptography features ----------------- @@ -27,7 +27,7 @@ More details on each layer: Layer | Role ----- | ---- -uCoin | The software that implements UCP. May implement UCP throught BMA or any other future protocol. +Duniter | The software that implements UCP. May implement UCP throught BMA or any other future protocol. [BMA](./HTTP_API.md) | A communication protocol to exchange HDC and Network messages over HTTP. -[UCP](./Protocol.md) | A protocol defining how to handle Network and HDC messages in order to build a uCoin network. +[UCP](./Protocol.md) | A protocol defining how to handle Network and HDC messages in order to build a Duniter network. Ed25519 | Cryptography format providing authentication features. diff --git a/doc/manual-config.md b/doc/manual-config.md index 091a7fdd594d1d0cbaa32f9f486caf1280ebb377..94e74a3db7ebbe6913af1a0c1e3d961865c714fb 100644 --- a/doc/manual-config.md +++ b/doc/manual-config.md @@ -1,6 +1,6 @@ # Manual configuration -To add mnually configuration parameters to uCoin, use `config` command: +To add mnually configuration parameters to Duniter, use `config` command: ```bash $ ucoind config @@ -8,7 +8,7 @@ $ ucoind config ## Currency -First of all, tell uCoin which currency to be used through command: +First of all, tell Duniter which currency to be used through command: ```bash $ ucoind config --currency mycurrency @@ -45,13 +45,13 @@ Or both: $ ucoind config -p 8888 --ipv4 127.0.0.1 --ipv6 ::1 ``` -Launching uCoin (when completely configured) will results: +Launching Duniter (when completely configured) will results: ```bash $ ucoind start -uCoin server listening on 127.0.0.1 port 8888 -uCoin server listening on ::1 port 8888 +Duniter server listening on 127.0.0.1 port 8888 +Duniter server listening on ::1 port 8888 ``` Note too that listening to multiple interfaces doesn't imply mutiple program instances: only *one* is running on multiple interfaces. @@ -60,7 +60,7 @@ Note too that listening to multiple interfaces doesn't imply mutiple program ins ### Peering informations -uCoin protocol uses peering mecanisms, hence needs any ucoin node to be reachable through the network. +Duniter protocol uses peering mecanisms, hence needs any ucoin node to be reachable through the network. As the server may be behind a reverse proxy, or because hosts may change of address, remote informations are likely to be different from listening host and port parameters. ucoin software defines 4 remote parameters you need to precise for your ucoin instance to be working: @@ -75,17 +75,17 @@ You must define at least `--remote4` and `--remotep` not to have any error. Here $ ucoind config --remoteh "some.remote.url" --remotep "8844" --remote4 "11.11.11.11" --remote6 "::1" ``` -Note that this is not required and may be removed in the future, as uCoin protocol already include peering mecanisms giving network informations. +Note that this is not required and may be removed in the future, as Duniter protocol already include peering mecanisms giving network informations. ### Authentication -uCoin protocol requires your responses to be signed in order to be interpreted. Such a feature is very important to authenticate nodes' messages. To use this feature, just configure uCoin using `--pgpkey` parameter: +Duniter protocol requires your responses to be signed in order to be interpreted. Such a feature is very important to authenticate nodes' messages. To use this feature, just configure Duniter using `--pgpkey` parameter: ```bash $ ucoind config --pgpkey /path/to/private/key ``` -Eventually, you might need to give a password, otherwise uCoin will crash: +Eventually, you might need to give a password, otherwise Duniter will crash: ```bash $ ucoind config --pgppasswd "ultr[A]!%HiGhly-s3cuR3-p4ssw0d" @@ -97,6 +97,6 @@ Resulting in: $ ucoind start Signed requests with PGP: enabled. -uCoin server listening on 127.0.0.1 port 8888 -uCoin server listening on ::1 port 8888 +Duniter server listening on 127.0.0.1 port 8888 +Duniter server listening on ::1 port 8888 ``` \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..6218444e3dc6c27cd61463a109d1e6f703bf8e62 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,16 @@ +FROM debian:jessie + +RUN apt update +RUN apt -y install curl git build-essential python +RUN useradd --create-home -ms /bin/bash duser + +USER duser +WORKDIR /home/duser + +RUN curl -kL https://raw.githubusercontent.com/duniter/duniter/master/install.sh | bash + +EXPOSE 8999 + +ADD go /home/duser/go + +CMD ["bash", "-i", "/home/duser/go"] diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000000000000000000000000000000000000..aaab6df2f3299ac6c26403a341b5977af10b3558 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,15 @@ +# Duniter in a Docker container + +Download `Dockerfile` and `go` files in a repository. + +#### Build a container + +```sh +docker build -t="duniter" . +``` + +#### Execute the container + +```sh +docker run -p 8999:8999 -dt duniter +```` diff --git a/docker/go b/docker/go new file mode 100644 index 0000000000000000000000000000000000000000..504245885ee44025d74608f341f29f8efc1e38ef --- /dev/null +++ b/docker/go @@ -0,0 +1,10 @@ +#!/bin/bash + +locale_ip=`awk 'NR==7 {print $1}' /etc/hosts` +remote_ip=`curl -s https://4.ifcfg.me/` + +duniter init --autoconf +duniter config --noupnp --remote4 $remote_ip --ipv4 $locale_ip +duniter sync twiced.fr 9330 +duniter start +tail -f /dev/null diff --git a/duniter.sh b/duniter.sh index c40887935f66bef5c74f6f391194a4546368623f..6c30501200e8982e8df456c49c3455e27ac097c2 100755 --- a/duniter.sh +++ b/duniter.sh @@ -6,6 +6,12 @@ # Wraps bin/ucoind.js that is called with Node.js # +DEB_PACKAGING= + +if [[ $DEB_PACKAGING ]]; then + DUNITER_DIR=/opt/duniter/sources/ +fi + duniter() { local NODE @@ -16,6 +22,9 @@ duniter() { ### Production mode if [[ -d $DUNITER_DIR/node ]]; then NODE=$DUNITER_DIR/node/bin/node + else + echo "Node.js is not embedded in this version of Duniter" + return fi; else @@ -26,8 +35,8 @@ duniter() { VERSION=`$NODE -v` - if [[ $VERSION != v5* ]]; then - echo "$NODE v5 is required"; + if [[ $VERSION != v5* && $VERSION != v6* ]]; then + echo "$NODE v5 or v6 is required"; else case "$1" in @@ -36,10 +45,14 @@ duniter() { # UCOIN DAEMON MANAGEMENT #--------------------------------- - start|stop|restart) + webwait|webstart|webstop|webrestart|start|stop|restart) $NODE "$DUNITER_DIR/bin/daemon" $* ;; + direct_start) + $NODE "$DUNITER_DIR/bin/ucoind" start ${@:2} + ;; + logs) LOGS_FILE=`$NODE "$DUNITER_DIR/bin/daemon" $*` tail -f -n 500 "$LOGS_FILE" @@ -57,7 +70,7 @@ duniter() { fi; } -# If the script was launched with parameters, try to launch the uCoin command +# If the script was launched with parameters, try to launch the Duniter command if [ ! -z $1 ]; then duniter $* fi diff --git a/gui/index.html b/gui/index.html index c0c6de2ce1f247eb76a5721057e1216eaa712a8f..8a23ac8a04cd9170e830efcac14376baf775c118 100644 --- a/gui/index.html +++ b/gui/index.html @@ -3,7 +3,7 @@ <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> - <title>Duniter 0.20.0a57</title> + <title>Duniter 0.20.0a84</title> <style> html { font-family: "Courier New", Courier, monospace; diff --git a/gui/package.json b/gui/package.json index c33128399551a1fb6fdb4802c28aee95321ada82..698cafd629658ec0be165bcb902533e854d1a040 100644 --- a/gui/package.json +++ b/gui/package.json @@ -1,10 +1,10 @@ { - "name": "v0.20.0a57", + "name": "v0.20.0a84", "main": "index.html", "node-main": "../sources/bin/ucoind", "window": { "icon": "duniter.png", - "title": "v0.20.0a57", + "title": "v0.20.0a84", "width": 800, "height": 800, "min_width": 750, diff --git a/index.js b/index.js index d8e001ba1ebb509acaa0bb0fec93bd11cccd2341..6ee44fcab20c650e083eaa1f025bc6d67135a9a9 100644 --- a/index.js +++ b/index.js @@ -4,8 +4,6 @@ var co = require('co'); var Server = require('./server'); var bma = require('./app/lib/streams/bma'); var webmin = require('./app/lib/streams/webmin'); -var upnp = require('./app/lib/upnp'); -var multicaster = require('./app/lib/streams/multicaster'); var logger = require('./app/lib/logger')('ucoin'); module.exports = function (dbConf, overConf) { @@ -27,13 +25,7 @@ module.exports.statics = { let bmapi = yield bma(server, null, conf.httplogs); // Routing documents - server - // The router asks for multicasting of documents - .pipe(server.router()) - // The documents get sent to peers - .pipe(multicaster(server.conf)) - // The multicaster may answer 'unreachable peer' - .pipe(server.router()); + server.routing(); // Services yield module.exports.statics.startServices(server); @@ -57,7 +49,7 @@ module.exports.statics = { if (server.upnpAPI) { server.upnpAPI.stopRegular(); } - server.upnpAPI = yield upnp(server.conf.port, server.conf.remoteport); + yield server.upnp(); server.upnpAPI.startRegular(); } catch (e) { logger.warn(e); diff --git a/install.sh b/install.sh index 50d92c66643fd16b82fa2dff83bdf88ba111e2f9..b701d927dce38064918bff83fcccb20e00b658bb 100755 --- a/install.sh +++ b/install.sh @@ -11,7 +11,7 @@ if [ -z "$DUNITER_DIR" ]; then fi latest_version() { - echo "v0.20.0a57" + echo "v0.20.0a84" } repo_url() { @@ -161,7 +161,7 @@ do_install() { exit 1 fi if ! is_installed "python"; then - echo "=> python is not available. You will likely need to install 'build-essential' package." + echo "=> python is not available. You will likely need to install 'python' package." exit 1 fi diff --git a/package.json b/package.json index 75d37827889bec32a0115864b1e4495ca4dae017..e103d409a35d1d12e5044e4e26638b2fb5cd8ce1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "ucoin", - "version": "0.20.0a57", + "name": "duniter", + "version": "0.20.0a84", "engines": { "node": ">=4.2.0", "npm": ">=2.11" @@ -8,30 +8,30 @@ "engineStrict": true, "private": false, "description": "Crypto-currency software allowing to build P2P free currencies", - "main": "bin/ucoin", + "main": "index.js", "directories": { "test": "test" }, "scripts": { "test": "mocha --growl --timeout 20000 test test/fast test/fast/block test/integration test/", "start": "node bin/ucoind start", - "test-travis": "node ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec --timeout 20000 test test/fast test/fast/block test/integration test/", - "postinstall": "cd ui && npm pack ucoin-ui@0.1.24 && tar xzf ucoin-ui-0.1.24.tgz && cd package && npm install && cd .. && rm ucoin-ui-0.1.24.tgz" + "test-travis": "node ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec --timeout 20000 test test/fast test/fast/block test/integration test/" }, "repository": { "type": "git", - "url": "git@github.com:ucoin-io/ucoin.git" + "url": "git@github.com:duniter/duniter.git" }, "keywords": [ - "openudc", + "duniter", "ucoin", + "openudc", "crypto-currency" ], "author": "Cedric Moreau <cem.moreau@gmail.com>", "license": "GPLv3", - "readmeFilename": "readme.md", + "readmeFilename": "README.md", "bugs": { - "url": "https://github.com/ucoin-io/ucoin/issues" + "url": "https://github.com/duniter/duniter/issues" }, "dependencies": { "async": "1.5.2", @@ -52,7 +52,7 @@ "moment": "2.6.0", "morgan": "1.6.1", "multimeter": "0.1.1", - "naclb": "1.3.1", + "naclb": "1.3.2", "nnupnp": "1.0.1", "optimist": "0.6.1", "q": "1.1.2", @@ -64,9 +64,9 @@ "superagent": "1.4.0", "tweetnacl": "0.14.1", "underscore": "1.8.3", - "vucoin": "0.30.3", + "vucoin": "0.30.4", "winston": "2.1.1", - "wotb": "0.4.9", + "wotb": "0.4.10", "ws": "1.0.1" }, "devDependencies": { diff --git a/release.sh b/release.sh index 91e4a55f49b4f6e300ad029e4844902dc9992c0c..35954b5bcef249cfc446abd28e46d38870f3f7fb 100755 --- a/release.sh +++ b/release.sh @@ -17,6 +17,9 @@ if [[ $2 =~ ^[0-9]+.[0-9]+.[0-9]+((a|b)[0-9]+)?$ ]]; then sed -i "s/title\": .*/title\": \"v$2\",/g" gui/package.json sed -i "s/<title>Duniter.*<\/title>/<title>Duniter $2<\/title>/g" gui/index.html + # Bump the install.sh + sed -i "s/echo \"v$current\"/echo \"v$2\"/g" install.sh + # (pre)-release management if [[ "$1" =~ ^rel$ ]]; then # This is RELEASE: change the version in public installer + add the RELEASE flag @@ -37,10 +40,10 @@ if [[ $2 =~ ^[0-9]+.[0-9]+.[0-9]+((a|b)[0-9]+)?$ ]]; then git reset HEAD case "$1" in rel) - git add package.json .travis.yml appveyor.yml test/integration/branches.js gui/package.json gui/index.html install.sh + git add install.sh package.json .travis.yml appveyor.yml test/integration/branches.js gui/package.json gui/index.html install.sh ;; pre) - git add package.json .travis.yml appveyor.yml test/integration/branches.js gui/package.json gui/index.html + git add install.sh package.json .travis.yml appveyor.yml test/integration/branches.js gui/package.json gui/index.html ;; esac git commit -m "v$2" diff --git a/server.js b/server.js index 380b9ad54d7dc2071b35e124561ce6e873f23c11..ff3003105b1d639df99717dc4a2fa4cdf6aa330f 100644 --- a/server.js +++ b/server.js @@ -2,6 +2,7 @@ var stream = require('stream'); var async = require('async'); var util = require('util'); +var path = require('path'); var co = require('co'); var _ = require('underscore'); var Q = require('q'); @@ -15,14 +16,20 @@ var crypto = require('./app/lib/crypto'); var signature = require('./app/lib/signature'); var directory = require('./app/lib/directory'); var dos2unix = require('./app/lib/dos2unix'); +var Synchroniser = require('./app/lib/sync'); +var multicaster = require('./app/lib/streams/multicaster'); +var upnp = require('./app/lib/upnp'); +var bma = require('./app/lib/streams/bma'); +var rawer = require('./app/lib/rawer'); function Server (dbConf, overrideConf) { stream.Duplex.call(this, { objectMode: true }); let home = directory.getHome(dbConf.name, dbConf.home); - var logger = require('./app/lib/logger')('server'); - var that = this; + let paramsP = directory.getHomeParams(dbConf && dbConf.memory, home); + let logger = require('./app/lib/logger')('server'); + let that = this; that.conf = null; that.dal = null; that.version = jsonpckg.version; @@ -37,13 +44,13 @@ function Server (dbConf, overrideConf) { // Create document mapping let documentsMapping = { - 'identity': that.IdentityService.submitIdentity, - 'certification': that.IdentityService.submitCertification, - 'revocation': that.IdentityService.submitRevocation, - 'membership': that.MembershipService.submitMembership, - 'peer': that.PeeringService.submitP, - 'transaction': that.TransactionsService.processTx, - 'block': _.partial(that.BlockchainService.submitBlock, _, true, constants.NO_FORK_ALLOWED) + 'identity': { action: that.IdentityService.submitIdentity, parser: parsers.parseIdentity }, + 'certification': { action: that.IdentityService.submitCertification, parser: parsers.parseCertification}, + 'revocation': { action: that.IdentityService.submitRevocation, parser: parsers.parseRevocation }, + 'membership': { action: that.MembershipService.submitMembership, parser: parsers.parseMembership }, + 'peer': { action: that.PeeringService.submitP, parser: parsers.parsePeer }, + 'transaction': { action: that.TransactionsService.processTx, parser: parsers.parseTransaction }, + 'block': { action: _.partial(that.BlockchainService.submitBlock, _, true, constants.NO_FORK_ALLOWED), parser: parsers.parseBlock } }; // Unused, but made mandatory by Duplex interface @@ -51,9 +58,19 @@ function Server (dbConf, overrideConf) { this._write = (obj, enc, writeDone) => that.submit(obj, false, () => writeDone); + /** + * Facade method to control what is pushed to the stream (we don't want it to be closed) + * @param obj An object to be pushed to the stream. + */ + this.streamPush = (obj) => { + if (obj) { + that.push(obj); + } + }; + this.plugFileSystem = () => co(function *() { logger.debug('Plugging file system...'); - var params = yield directory.getHomeParams(dbConf && dbConf.memory, home); + let params = yield paramsP; that.dal = fileDAL(params); }); @@ -61,7 +78,7 @@ function Server (dbConf, overrideConf) { logger.debug('Soft data reset... [cache]'); yield that.dal.cleanCaches(); logger.debug('Soft data reset... [data]'); - yield that.dal.cleanDBData(); + yield that.cleanDBData(); }); this.loadConf = (useDefaultConf) => co(function *() { @@ -71,7 +88,7 @@ function Server (dbConf, overrideConf) { var defaultValues = { remoteipv6: that.conf.ipv6, remoteport: that.conf.port, - cpu: 1, + cpu: constants.DEFAULT_CPU, c: constants.CONTRACT.DEFAULT.C, dt: constants.CONTRACT.DEFAULT.DT, ud0: constants.CONTRACT.DEFAULT.UD0, @@ -143,7 +160,7 @@ function Server (dbConf, overrideConf) { throw 'Document type not given'; } try { - let action = documentsMapping[obj.documentType]; + let action = documentsMapping[obj.documentType].action; let res; if (typeof action == 'function') { // Handle the incoming object @@ -154,7 +171,7 @@ function Server (dbConf, overrideConf) { if (res) { // Only emit valid documents that.emit(obj.documentType, _.clone(res)); - that.push(_.clone(res)); + that.streamPush(_.clone(res)); } if (done) { isInnerWrite ? done(null, res) : done(); @@ -282,29 +299,78 @@ function Server (dbConf, overrideConf) { }); }; - this.reset = function(done) { - return that.dal.resetAll(done); + this.resetAll = function(done) { + var files = ['stats', 'cores', 'current', 'conf', directory.UCOIN_DB_NAME, directory.UCOIN_DB_NAME + '.db', directory.WOTB_FILE]; + var dirs = ['blocks', 'ud_history', 'branches', 'certs', 'txs', 'cores', 'sources', 'links', 'ms', 'identities', 'peers', 'indicators', 'leveldb']; + return resetFiles(files, dirs, done); }; this.resetData = function(done) { - return that.dal.resetData(done); + return co(function*(){ + var files = ['stats', 'cores', 'current', directory.UCOIN_DB_NAME, directory.UCOIN_DB_NAME + '.db', directory.UCOIN_DB_NAME + '.log', directory.WOTB_FILE]; + var dirs = ['blocks', 'ud_history', 'branches', 'certs', 'txs', 'cores', 'sources', 'links', 'ms', 'identities', 'peers', 'indicators', 'leveldb']; + yield resetFiles(files, dirs, done); + }); + }; + + this.resetConf = function(done) { + var files = ['conf']; + var dirs = []; + return resetFiles(files, dirs, done); }; this.resetStats = function(done) { - return that.dal.resetStats(done); + var files = ['stats']; + var dirs = ['ud_history']; + return resetFiles(files, dirs, done); }; this.resetPeers = function(done) { return that.dal.resetPeers(done); }; - this.resetTxs = function(done) { - that.dal.resetTransactions(done); - }; + this.cleanDBData = () => co(function *() { + yield _.values(that.dal.newDals).map((dal) => dal.cleanData && dal.cleanData()); + that.dal.wotb.resetWoT(); + var files = ['stats', 'cores', 'current']; + var dirs = ['blocks', 'ud_history', 'branches', 'certs', 'txs', 'cores', 'sources', 'links', 'ms', 'identities', 'peers', 'indicators', 'leveldb']; + return resetFiles(files, dirs); + }); - this.resetConf = function(done) { - return that.dal.resetConf(done); - }; + function resetFiles(files, dirs, done) { + return co(function *() { + let params = yield paramsP; + let myFS = params.fs; + let rootPath = params.home; + for (let i = 0, len = files.length; i < len; i++) { + let fName = files[i]; + // JSON file? + let existsJSON = yield myFS.exists(rootPath + '/' + fName + '.json'); + if (existsJSON) { + yield myFS.remove(rootPath + '/' + fName + '.json'); + } else { + // Normal file? + let normalFile = path.join(rootPath, fName); + let existsFile = yield myFS.exists(normalFile); + if (existsFile) { + yield myFS.remove(normalFile); + } + } + } + for (let i = 0, len = dirs.length; i < len; i++) { + let dirName = dirs[i]; + let existsDir = yield myFS.exists(rootPath + '/' + dirName); + if (existsDir) { + yield myFS.removeTree(rootPath + '/' + dirName); + } + } + done && done(); + }) + .catch((err) => { + done && done(err); + throw err; + }); + } this.disconnect = function() { return that.dal && that.dal.close(); @@ -344,6 +410,64 @@ function Server (dbConf, overrideConf) { return theRouter; }; + /** + * Synchronize the server with another server. + * + * If local server's blockchain is empty, process a fast sync: **no block is verified in such a case**, unless + * you force value `askedCautious` to true. + * + * @param onHost Syncs on given host. + * @param onPort Syncs on given port. + * @param upTo Sync up to this number, if `upTo` value is a positive integer. + * @param chunkLength Length of each chunk of blocks to download. Kind of buffer size. + * @param interactive Tell if the loading bars should be used for console output. + * @param askedCautious If true, force the verification of each downloaded block. This is the right way to have a valid blockchain for sure. + * @param nopeers If true, sync will omit to retrieve peer documents. + */ + this.synchronize = (onHost, onPort, upTo, chunkLength, interactive, askedCautious, nopeers) => { + let remote = new Synchroniser(that, onHost, onPort, that.conf, interactive === true); + let syncPromise = remote.sync(upTo, chunkLength, askedCautious, nopeers); + return { + flow: remote, + syncPromise: syncPromise + }; + }; + + /** + * Enable routing features: + * - The server will try to send documents to the network + * - The server will eventually be notified of network failures + */ + this.routing = () => { + // The router asks for multicasting of documents + this.pipe(this.router()) + // The documents get sent to peers + .pipe(multicaster(this.conf)) + // The multicaster may answer 'unreachable peer' + .pipe(this.router()); + }; + + this.upnp = () => co(function *() { + let upnpAPI = yield upnp(that.conf.port, that.conf.remoteport); + that.upnpAPI = upnpAPI; + return upnpAPI; + }); + + this.listenToTheWeb = (showLogs) => co(function *() { + let bmapi = yield bma(that, [{ + ip: that.conf.ipv4, + port: that.conf.port + }], showLogs); + return bmapi.openConnections(); + }); + + this.rawer = rawer; + + this.writeRaw = (raw, type) => co(function *() { + let parser = documentsMapping[type] && documentsMapping[type].parser; + let obj = parser.syncWrite(raw); + return yield that.singleWritePromise(obj); + }); } util.inherits(Server, stream.Duplex); diff --git a/test/fast/tx_format.js b/test/fast/tx_format.js new file mode 100644 index 0000000000000000000000000000000000000000..95c346820f09fb3ec40f6a7184b04d31a023fea8 --- /dev/null +++ b/test/fast/tx_format.js @@ -0,0 +1,28 @@ +"use strict"; +var async = require('async'); +var should = require('should'); +var parsers = require('../../app/lib/streams/parsers/doc'); + +var raw = "Version: 2\n" + + "Type: Transaction\n" + + "Currency: test_net\n" + + "Locktime: 0\n" + + "Issuers:\n" + + "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk\n" + + "Inputs:\n" + + "D:HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:3428\n" + + "Unlocks:\n" + + "0:SIG(0)\n" + + "Outputs:\n" + + "1000:0:SIG(yGKRRB18B4eaZQdksWBZubea4VJKFSSpii2okemP7x1)\n" + + "99000:0:SIG(HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk)\n" + + "Comment: reessai\n" + + "P6MxJ/2SdkvNDyIyWuOkTz3MUwsgsfo70j+rpWeQWcm6GdvKQsbplB8482Ar1HMz2q0h5V3tfMqjCuAeWVQ+Ag==\n"; + +describe("Transaction format", function(){ + + var parser = parsers.parseTransaction; + + it('a valid block should be well formatted', () => parser.syncWrite(raw)); + +}); diff --git a/test/integration/branches.js b/test/integration/branches.js index 88a178b8b1f701c14c1eee61ba27ce67ef799be1..8a07e5acfbc0cd8f7b356e54ff5a5ce9860dd7ba 100644 --- a/test/integration/branches.js +++ b/test/integration/branches.js @@ -39,7 +39,7 @@ describe("Branches", function() { it('should have a 3 blocks fork window size', function() { return expectAnswer(rp('http://127.0.0.1:7778/node/summary', { json: true }), function(res) { res.should.have.property('duniter').property('software').equal('duniter'); - res.should.have.property('duniter').property('version').equal('0.20.0a57'); + res.should.have.property('duniter').property('version').equal('0.20.0a84'); res.should.have.property('duniter').property('forkWindowSize').equal(3); }); }); diff --git a/test/integration/lookup.js b/test/integration/lookup.js new file mode 100644 index 0000000000000000000000000000000000000000..18fe6ee3f1f97b7ace927f4f1e6329a964adf0f2 --- /dev/null +++ b/test/integration/lookup.js @@ -0,0 +1,72 @@ +"use strict"; + +var _ = require('underscore'); +var co = require('co'); +var ucoin = require('./../../index'); +var bma = require('./../../app/lib/streams/bma'); +var user = require('./tools/user'); +var rp = require('request-promise'); +var httpTest = require('./tools/http'); + +var MEMORY_MODE = true; +var commonConf = { + ipv4: '127.0.0.1', + currency: 'bb' +}; + +var s1 = ucoin({ + memory: MEMORY_MODE, + name: 'bb12' +}, _.extend({ + port: '4452', + pair: { + pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', + sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' + } +}, commonConf)); + +var cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 }); +var tic1 = user('tic1', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 }); +var tic2 = user('tic2', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 }); + +describe("Lookup identity grouping", () => { + + before(() => co(function *() { + // Server initialization + yield s1.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections()); + + // cat is publishing its identity, no problem + yield cat.selfCertPromise(); + + // tic1 is publishing its identity + yield tic1.selfCertPromise(); + + // tic2 is publishing its identity, but he has **the same pubkey as tic1**. + // This is OK on the protocol side, but the lookup should group the 2 identities + // under the same pubkey + yield tic2.selfCertPromise(); + })); + + it('cat should have only 1 identity in 1 pubkey', () => httpTest.expectAnswer(rp('http://127.0.0.1:4452/wot/lookup/cat', { json: true }), (res) => { + res.should.have.property('results').length(1); + // cat pubkey + res.results[0].should.have.property('pubkey').equal('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd'); + // only 1 UID + res.results[0].should.have.property('uids').length(1); + // which is cat + res.results[0].uids[0].should.have.property('uid').equal('cat'); + })); + + it.skip('tic should have only 2 identities in 1 pubkey', () => httpTest.expectAnswer(rp('http://127.0.0.1:4452/wot/lookup/tic', { json: true }), (res) => { + // We want to have only 1 result for the 2 identities + res.should.have.property('results').length(1); + // because they share the same pubkey + res.results[0].should.have.property('pubkey').equal('DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV'); + // but got 2 UIDs + res.results[0].should.have.property('uids').length(2); + // which are tic1 + res.results[0].uids[0].should.have.property('uid').equal('tic1'); + // which are tic2 + res.results[0].uids[1].should.have.property('uid').equal('tic2'); + })); +}); diff --git a/test/integration/tools/node.js b/test/integration/tools/node.js index 78642ac8055e0a242aec070f36a471728108ffc9..4078037671f2cc5336a81721e14ee592b63f4036 100644 --- a/test/integration/tools/node.js +++ b/test/integration/tools/node.js @@ -7,7 +7,6 @@ var async = require('async'); var request = require('request'); var vucoin = require('vucoin'); var ucoin = require('../../../index'); -var bma = require('../../../app/lib/streams/bma'); var multicaster = require('../../../app/lib/streams/multicaster'); var Configuration = require('../../../app/lib/entity/configuration'); var Peer = require('../../../app/lib/entity/peer'); @@ -221,13 +220,7 @@ function Node (dbName, options) { done && done(err); }); }) - .then(function(server){ - return bma(server, [{ - ip: server.conf.ipv4, - port: server.conf.port - }]) - .then((bmapi) => bmapi.openConnections()); - }); + .then((server) => server.listenToTheWeb()); }; function service(callback) { diff --git a/ui/notice.md b/ui/notice.md deleted file mode 100644 index bb3eea8a56b43f85ad4ce7ece1dd53d9c9d4e1c0..0000000000000000000000000000000000000000 --- a/ui/notice.md +++ /dev/null @@ -1,3 +0,0 @@ -# About this directory - -It aims at containing ucoin-ui, a web graphical interface which is deployed on NPM and is installed through `npm install` \ No newline at end of file diff --git a/web-ui b/web-ui new file mode 160000 index 0000000000000000000000000000000000000000..da54b8f0c948b18fbadb9fe7e21513186917323e --- /dev/null +++ b/web-ui @@ -0,0 +1 @@ +Subproject commit da54b8f0c948b18fbadb9fe7e21513186917323e