From 483efdced6f971dff5388bd17d7f5f10bb67c643 Mon Sep 17 00:00:00 2001 From: cgeek <cem.moreau@gmail.com> Date: Mon, 24 Jul 2017 13:51:31 +0200 Subject: [PATCH] [fix] #1037 Migrate duniter-common back into duniter core --- .eslintignore | 3 + app/common/index.js | 14 + app/common/lib/buid.js | 39 ++ app/common/lib/constants.js | 228 +++++++++++ app/common/lib/crypto/base58.js | 9 + app/common/lib/crypto/keyring.js | 75 ++++ app/common/lib/crypto/nacl-util.js | 50 +++ app/common/lib/document/block.js | 356 ++++++++++++++++++ app/common/lib/document/certification.js | 149 ++++++++ app/common/lib/document/identity.js | 187 +++++++++ app/common/lib/document/index.js | 10 + app/common/lib/document/membership.js | 123 ++++++ app/common/lib/document/peer.js | 200 ++++++++++ app/common/lib/document/transaction.js | 189 ++++++++++ app/common/lib/dos2unix.js | 23 ++ app/common/lib/hashf.js | 8 + app/common/lib/parsers/GenericParser.js | 108 ++++++ app/common/lib/parsers/block.js | 249 ++++++++++++ app/common/lib/parsers/certification.js | 43 +++ app/common/lib/parsers/identity.js | 47 +++ app/common/lib/parsers/index.js | 11 + app/common/lib/parsers/membership.js | 75 ++++ app/common/lib/parsers/peer.js | 105 ++++++ app/common/lib/parsers/revocation.js | 52 +++ app/common/lib/parsers/transaction.js | 128 +++++++ app/common/lib/rawer.js | 92 +++++ app/common/lib/regex.js | 5 + app/common/lib/txunlock.js | 73 ++++ app/lib/blockchain/DuniterBlockchain.ts | 2 +- .../{common => common-libs}/crypto/base58.ts | 0 .../{common => common-libs}/crypto/keyring.ts | 0 .../crypto/nacl-util.ts | 0 app/lib/common.ts | 2 +- app/lib/constants.ts | 2 +- app/lib/dal/fileDAL.ts | 2 +- app/lib/dal/sqliteDAL/MetaDAL.ts | 4 +- app/lib/dal/sqliteDAL/index/CIndexDAL.ts | 2 +- app/lib/dal/sqliteDAL/index/SIndexDAL.ts | 2 +- app/lib/indexer.ts | 4 +- app/lib/rules/global_rules.ts | 4 +- app/lib/rules/helpers.ts | 2 +- app/lib/rules/index.ts | 2 +- app/lib/rules/local_rules.ts | 4 +- app/modules/bma/lib/controllers/blockchain.ts | 2 +- .../bma/lib/controllers/transactions.ts | 2 +- app/modules/bma/lib/controllers/wot.ts | 2 +- app/modules/crawler/index.ts | 2 +- app/modules/crawler/lib/constants.ts | 2 +- app/modules/crawler/lib/crawler.ts | 2 +- app/modules/crawler/lib/req2fwd.ts | 2 +- app/modules/crawler/lib/sandbox.ts | 4 +- app/modules/crawler/lib/sync.ts | 2 +- app/modules/keypair/index.ts | 2 +- app/modules/keypair/lib/scrypt.ts | 4 +- app/modules/prover/index.ts | 2 +- app/modules/prover/lib/blockGenerator.ts | 4 +- app/modules/prover/lib/blockProver.ts | 2 +- app/modules/prover/lib/permanentProver.ts | 2 +- app/modules/prover/lib/proof.ts | 6 +- app/service/BlockchainService.ts | 2 +- app/service/IdentityService.ts | 2 +- app/service/MembershipService.ts | 2 +- app/service/PeeringService.ts | 8 +- package.json | 2 +- server.ts | 6 +- test/fast/block_format.js | 2 +- test/fast/block_local.js | 4 +- test/fast/common/crypto.js | 6 +- test/fast/common/randomKey.js | 4 +- test/fast/modules/common/crypto.js | 50 +++ test/fast/modules/common/grammar-test.js | 70 ++++ test/fast/modules/common/peering.js | 63 ++++ test/fast/modules/common/randomKey.js | 35 ++ test/fast/modules/common/tx_format.js | 28 ++ test/fast/v1.0-local-index.js | 4 +- test/integration/cli.js | 2 +- test/integration/server-sandbox.js | 2 +- test/integration/tools/user.js | 4 +- test/integration/transactions-chaining.js | 2 +- test/integration/transactions-pruning.js | 2 +- test/integration/v1.0-modules-api.js | 2 +- 81 files changed, 2962 insertions(+), 65 deletions(-) create mode 100644 app/common/index.js create mode 100644 app/common/lib/buid.js create mode 100644 app/common/lib/constants.js create mode 100644 app/common/lib/crypto/base58.js create mode 100644 app/common/lib/crypto/keyring.js create mode 100644 app/common/lib/crypto/nacl-util.js create mode 100644 app/common/lib/document/block.js create mode 100644 app/common/lib/document/certification.js create mode 100644 app/common/lib/document/identity.js create mode 100644 app/common/lib/document/index.js create mode 100644 app/common/lib/document/membership.js create mode 100644 app/common/lib/document/peer.js create mode 100644 app/common/lib/document/transaction.js create mode 100644 app/common/lib/dos2unix.js create mode 100644 app/common/lib/hashf.js create mode 100644 app/common/lib/parsers/GenericParser.js create mode 100644 app/common/lib/parsers/block.js create mode 100644 app/common/lib/parsers/certification.js create mode 100644 app/common/lib/parsers/identity.js create mode 100644 app/common/lib/parsers/index.js create mode 100644 app/common/lib/parsers/membership.js create mode 100644 app/common/lib/parsers/peer.js create mode 100644 app/common/lib/parsers/revocation.js create mode 100644 app/common/lib/parsers/transaction.js create mode 100644 app/common/lib/rawer.js create mode 100644 app/common/lib/regex.js create mode 100644 app/common/lib/txunlock.js rename app/lib/{common => common-libs}/crypto/base58.ts (100%) rename app/lib/{common => common-libs}/crypto/keyring.ts (100%) rename app/lib/{common => common-libs}/crypto/nacl-util.ts (100%) create mode 100644 test/fast/modules/common/crypto.js create mode 100644 test/fast/modules/common/grammar-test.js create mode 100644 test/fast/modules/common/peering.js create mode 100644 test/fast/modules/common/randomKey.js create mode 100644 test/fast/modules/common/tx_format.js diff --git a/.eslintignore b/.eslintignore index 0601c0236..7775dfc5e 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,7 @@ app/cli.js +app/common/*.js +app/common/lib/*.js +app/common/lib/document/*.js app/lib/blockchain/*.js app/lib/blockchain/interfaces/*.js app/lib/computation/*.js diff --git a/app/common/index.js b/app/common/index.js new file mode 100644 index 000000000..1606e4446 --- /dev/null +++ b/app/common/index.js @@ -0,0 +1,14 @@ +"use strict"; + +module.exports = { + hashf: require('./lib/hashf'), + dos2unix: require('./lib/dos2unix'), + keyring: require('./lib/crypto/keyring'), + base58: require('./lib/crypto/base58'), + rawer: require('./lib/rawer'), + buid: require('./lib/buid'), + document: require('./lib/document'), + constants: require('./lib/constants'), + txunlock: require('./lib/txunlock'), + parsers: require('./lib/parsers') +} diff --git a/app/common/lib/buid.js b/app/common/lib/buid.js new file mode 100644 index 000000000..55d12bb47 --- /dev/null +++ b/app/common/lib/buid.js @@ -0,0 +1,39 @@ +"use strict"; +const hashf = require('./hashf'); + +const BLOCK_UID = /^(0|[1-9]\d{0,18})-[A-F0-9]{64}$/; + +const buidFunctions = function(number, hash) { + if (arguments.length === 2) { + return [number, hash].join('-'); + } + if (arguments[0]) { + return [arguments[0].number, arguments[0].hash].join('-'); + } + return '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'; +}; + +buidFunctions.fromTS = (line) => line.match(/TS:(.*)/)[1]; +buidFunctions.fromIdty = (idty) => this(idty.ts_number, idty.ts_hash); + +module.exports = { + + format: { + + hashf: (value) => hashf(String(value)).toUpperCase(), + + isBuid: (value) => { + return (typeof value === 'string') && value.match(BLOCK_UID) ? true : false; + }, + + buid: buidFunctions, + + obuid: (line) => { + let sp = this.buid.fromTS(line).split('-'); + return { + number: sp[0], + hash: sp[1] + }; + } + } +}; diff --git a/app/common/lib/constants.js b/app/common/lib/constants.js new file mode 100644 index 000000000..93b798990 --- /dev/null +++ b/app/common/lib/constants.js @@ -0,0 +1,228 @@ +"use strict"; + +const CURRENCY = "[a-zA-Z0-9-_ ]{2,50}" +const BASE58 = "[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+" +const PUBKEY = "[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}" +const SIGNATURE = "[A-Za-z0-9+\\/=]{87,88}" +const USER_ID = "[A-Za-z0-9_-]{2,100}" +const INTEGER = "(0|[1-9]\\d{0,18})" +const FINGERPRINT = "[A-F0-9]{64}" +const BLOCK_UID = INTEGER + "-" + FINGERPRINT +const BLOCK_VERSION = "(10)" +const TX_VERSION = "(10)" +const DIVIDEND = "[1-9][0-9]{0,5}" +const ZERO_OR_POSITIVE_INT = "0|[1-9][0-9]{0,18}" +const RELATIVE_INTEGER = "(0|-?[1-9]\\d{0,18})" +const FLOAT = "\\d+\.\\d+" +const POSITIVE_INT = "[1-9][0-9]{0,18}" +const TIMESTAMP = "[1-9][0-9]{0,18}" +const BOOLEAN = "[01]" +const SPECIAL_BLOCK = '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855' +const META_TS = "META:TS:" + BLOCK_UID +const COMMENT = "[ a-zA-Z0-9-_:/;*\\[\\]()?!^\\+=@&~#{}|\\\\<>%.]{0,255}" +const CLTV_INTEGER = "([0-9]{1,10})"; +const CSV_INTEGER = "([0-9]{1,8})"; +const XUNLOCK = "[a-zA-Z0-9]{1,64}"; +const UNLOCK = "(SIG\\(" + INTEGER + "\\)|XHX\\(" + XUNLOCK + "\\))" +const CONDITIONS = "(&&|\\|\\|| |[()]|(SIG\\(" + PUBKEY + "\\)|(XHX\\([A-F0-9]{64}\\)|CLTV\\(" + CLTV_INTEGER + "\\)|CSV\\(" + CSV_INTEGER + "\\))))*" + +const BMA_REGEXP = /^BASIC_MERKLED_API( ([a-z_][a-z0-9-_.]*))?( ([0-9.]+))?( ([0-9a-f:]+))?( ([0-9]+))$/ + +const MAXIMUM_LEN_OF_COMPACT_TX = 100 +const MAXIMUM_LEN_OF_OUTPUT = 2000 +const MAXIMUM_LEN_OF_UNLOCK = MAXIMUM_LEN_OF_OUTPUT + +module.exports = { + + FORMATS: { + CURRENCY, + PUBKEY, + INTEGER, + FINGERPRINT + }, + + BLOCK_GENERATED_VERSION: 10, + LAST_VERSION_FOR_TX: 10, + TRANSACTION_VERSION: 10, + DOCUMENTS_VERSION: 10, + TRANSACTION_MAX_TRIES: 10, + + BMA_REGEXP, + PUBLIC_KEY: exact(PUBKEY), + INTEGER: /^\d+$/, + BASE58: exact(BASE58), + FINGERPRINT: exact(FINGERPRINT), + SIG: exact(SIGNATURE), + BLOCK_UID: exact(BLOCK_UID), + USER_ID: exact(USER_ID), // Any format, by default + + DOCUMENTS_VERSION_REGEXP: /^10$/, + BLOCKSTAMP_REGEXP: new RegExp("^" + BLOCK_UID + "$"), + DOCUMENTS_BLOCK_VERSION_REGEXP: new RegExp("^" + BLOCK_VERSION + "$"), + DOCUMENTS_TRANSACTION_VERSION_REGEXP: /^(10)$/, + SPECIAL_BLOCK, + SPECIAL_HASH: 'E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855', + MAXIMUM_LEN_OF_COMPACT_TX, + MAXIMUM_LEN_OF_OUTPUT, + MAXIMUM_LEN_OF_UNLOCK, + + PROOF_OF_WORK: { + UPPER_BOUND: [ + '9A-F', + '9A-E', + '9A-D', + '9A-C', + '9A-B', + '9A', + '9', + '8', + '7', + '6', + '5', + '4', + '3', + '2', + '1', + '1' // In case remainder 15 happens for some reason + ] + }, + + ERRORS: { + // Technical errors + WRONG_DOCUMENT: { httpCode: 400, uerr: { ucode: 1005, message: "Document has unkown fields or wrong line ending format" }}, + + // Business errors + WRONG_UNLOCKER: { httpCode: 400, uerr: { ucode: 2013, message: "Wrong unlocker in transaction" }}, + LOCKTIME_PREVENT: { httpCode: 400, uerr: { ucode: 2014, message: "Locktime not elapsed yet" }}, + SOURCE_ALREADY_CONSUMED: { httpCode: 400, uerr: { ucode: 2015, message: "Source already consumed" }}, + WRONG_AMOUNTS: { httpCode: 400, uerr: { ucode: 2016, message: "Sum of inputs must equal sum of outputs" }}, + WRONG_OUTPUT_BASE: { httpCode: 400, uerr: { ucode: 2017, message: "Wrong unit base for outputs" }}, + CANNOT_ROOT_BLOCK_NO_MEMBERS: { httpCode: 400, uerr: { ucode: 2018, message: "Wrong new block: cannot make a root block without members" }}, + IDENTITY_WRONGLY_SIGNED: { httpCode: 400, uerr: { ucode: 2019, message: "Weird, the signature is wrong and in the database." }}, + TOO_OLD_IDENTITY: { httpCode: 400, uerr: { ucode: 2020, message: "Identity has expired and cannot be written in the blockchain anymore." }}, + TX_INPUTS_OUTPUTS_NOT_EQUAL: { httpCode: 400, uerr: { ucode: 2024, message: "Transaction inputs sum must equal outputs sum" }}, + TX_OUTPUT_SUM_NOT_EQUALS_PREV_DELTAS: { httpCode: 400, uerr: { ucode: 2025, message: "Transaction output base amount does not equal previous base deltas" }}, + BLOCKSTAMP_DOES_NOT_MATCH_A_BLOCK: { httpCode: 400, uerr: { ucode: 2026, message: "Blockstamp does not match a block" }}, + A_TRANSACTION_HAS_A_MAX_SIZE: { httpCode: 400, uerr: { ucode: 2027, message: 'A transaction has a maximum size of ' + MAXIMUM_LEN_OF_COMPACT_TX + ' lines' }}, + TOO_OLD_MEMBERSHIP: { httpCode: 400, uerr: { ucode: 2029, message: "Too old membership." }}, + MAXIMUM_LEN_OF_OUTPUT: { httpCode: 400, uerr: { ucode: 2032, message: 'A transaction output has a maximum size of ' + MAXIMUM_LEN_OF_OUTPUT + ' characters' }}, + MAXIMUM_LEN_OF_UNLOCK: { httpCode: 400, uerr: { ucode: 2033, message: 'A transaction unlock has a maximum size of ' + MAXIMUM_LEN_OF_UNLOCK + ' characters' }}, + + WRONG_SIGNATURE_FOR_CERT: { httpCode: 400, uerr: { ucode: 3000, message: 'Wrong signature for certification' }}, + }, + + // INDEXES + M_INDEX: 'MINDEX', + I_INDEX: 'IINDEX', + S_INDEX: 'SINDEX', + C_INDEX: 'CINDEX', + IDX_CREATE: 'CREATE', + IDX_UPDATE: 'UPDATE', + + // Protocol fixed values + NB_DIGITS_UD: 4, + REVOCATION_FACTOR: 2, + TX_WINDOW: 3600 * 24 * 7, + POW_DIFFICULTY_RANGE_RATIO: 1.189, // deduced from Hexadecimal relation between 2 chars ~= 16^(1/16) + ACCOUNT_MINIMUM_CURRENT_BASED_AMOUNT: 100, + + + DOCUMENTS: { + DOC_VERSION: find('Version: (10)'), + DOC_CURRENCY: find('Currency: (' + CURRENCY + ')'), + DOC_ISSUER: find('Issuer: (' + PUBKEY + ')'), + TIMESTAMP: find('Timestamp: (' + BLOCK_UID + ')') + }, + IDENTITY: { + INLINE: exact(PUBKEY + ":" + SIGNATURE + ":" + BLOCK_UID + ":" + USER_ID), + IDTY_TYPE: find('Type: (Identity)'), + IDTY_UID: find('UniqueID: (' + USER_ID + ')') + }, + BLOCK: { + NONCE: find("Nonce: (" + ZERO_OR_POSITIVE_INT + ")"), + VERSION: find("Version: " + BLOCK_VERSION), + TYPE: find("Type: (Block)"), + CURRENCY: find("Currency: (" + CURRENCY + ")"), + BNUMBER: find("Number: (" + ZERO_OR_POSITIVE_INT + ")"), + POWMIN: find("PoWMin: (" + ZERO_OR_POSITIVE_INT + ")"), + TIME: find("Time: (" + TIMESTAMP + ")"), + MEDIAN_TIME: find("MedianTime: (" + TIMESTAMP + ")"), + UD: find("UniversalDividend: (" + DIVIDEND + ")"), + UNIT_BASE: find("UnitBase: (" + INTEGER + ")"), + PREV_HASH: find("PreviousHash: (" + FINGERPRINT + ")"), + PREV_ISSUER: find("PreviousIssuer: (" + PUBKEY + ")"), + MEMBERS_COUNT:find("MembersCount: (" + ZERO_OR_POSITIVE_INT + ")"), + BLOCK_ISSUER:find('Issuer: (' + PUBKEY + ')'), + BLOCK_ISSUERS_FRAME:find('IssuersFrame: (' + INTEGER + ')'), + BLOCK_ISSUERS_FRAME_VAR:find('IssuersFrameVar: (' + RELATIVE_INTEGER + ')'), + DIFFERENT_ISSUERS_COUNT:find('DifferentIssuersCount: (' + INTEGER + ')'), + PARAMETERS: find("Parameters: (" + FLOAT + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + FLOAT + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + FLOAT + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ")"), + JOINER: exact(PUBKEY + ":" + SIGNATURE + ":" + BLOCK_UID + ":" + BLOCK_UID + ":" + USER_ID), + ACTIVE: exact(PUBKEY + ":" + SIGNATURE + ":" + BLOCK_UID + ":" + BLOCK_UID + ":" + USER_ID), + LEAVER: exact(PUBKEY + ":" + SIGNATURE + ":" + BLOCK_UID + ":" + BLOCK_UID + ":" + USER_ID), + REVOCATION: exact(PUBKEY + ":" + SIGNATURE), + EXCLUDED: exact(PUBKEY), + INNER_HASH: find("InnerHash: (" + FINGERPRINT + ")"), + SPECIAL_HASH: 'E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855', + SPECIAL_BLOCK + }, + CERT: { + SELF: { + UID: exact("UID:" + USER_ID), + META: exact(META_TS) + }, + REVOKE: exact("UID:REVOKE"), + OTHER: { + META: exact(META_TS), + INLINE: exact(PUBKEY + ":" + PUBKEY + ":" + INTEGER + ":" + SIGNATURE) + } + }, + CERTIFICATION: { + CERT_TYPE: find('Type: (Certification)'), + IDTY_ISSUER: find('IdtyIssuer: (' + PUBKEY + ')'), + IDTY_UID: find('IdtyUniqueID: (' + USER_ID + ')'), + IDTY_TIMESTAMP: find('IdtyTimestamp: (' + BLOCK_UID + ')'), + IDTY_SIG: find('IdtySignature: (' + SIGNATURE + ')'), + CERT_TIMESTAMP: find('CertTimestamp: (' + BLOCK_UID + ')') + }, + REVOCATION: { + REVOC_TYPE: find('Type: (Certification)'), + IDTY_ISSUER: find('IdtyIssuer: (' + PUBKEY + ')'), + IDTY_UID: find('IdtyUniqueID: (' + USER_ID + ')'), + IDTY_TIMESTAMP: find('IdtyTimestamp: (' + BLOCK_UID + ')'), + IDTY_SIG: find('IdtySignature: (' + SIGNATURE + ')') + }, + MEMBERSHIP: { + BLOCK: find('Block: (' + BLOCK_UID + ')'), + VERSION: find('Version: (10)'), + CURRENCY: find('Currency: (' + CURRENCY + ')'), + ISSUER: find('Issuer: (' + PUBKEY + ')'), + MEMBERSHIP: find('Membership: (IN|OUT)'), + USERID: find('UserID: (' + USER_ID + ')'), + CERTTS: find('CertTS: (' + BLOCK_UID + ')') + }, + TRANSACTION: { + HEADER: exact("TX:" + TX_VERSION + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + INTEGER + ":" + BOOLEAN + ":" + INTEGER), + SENDER: exact(PUBKEY), + SOURCE_V3: exact("(" + POSITIVE_INT + ":" + INTEGER + ":T:" + FINGERPRINT + ":" + INTEGER + "|" + POSITIVE_INT + ":" + INTEGER + ":D:" + PUBKEY + ":" + POSITIVE_INT + ")"), + UNLOCK: exact(INTEGER + ":" + UNLOCK + "( (" + UNLOCK + "))*"), + TARGET: exact(POSITIVE_INT + ":" + INTEGER + ":" + CONDITIONS), + BLOCKSTAMP:find('Blockstamp: (' + BLOCK_UID + ')'), + COMMENT: find("Comment: (" + COMMENT + ")"), + LOCKTIME:find("Locktime: (" + INTEGER + ")"), + INLINE_COMMENT: exact(COMMENT), + OUTPUT_CONDITION: exact(CONDITIONS) + }, + PEER: { + BLOCK: find("Block: (" + INTEGER + "-" + FINGERPRINT + ")"), + SPECIAL_BLOCK + }, +} + +function exact (regexpContent) { + return new RegExp("^" + regexpContent + "$"); +} + +function find (regexpContent) { + return new RegExp(regexpContent); +} diff --git a/app/common/lib/crypto/base58.js b/app/common/lib/crypto/base58.js new file mode 100644 index 000000000..db14015cb --- /dev/null +++ b/app/common/lib/crypto/base58.js @@ -0,0 +1,9 @@ +"use strict" + +const assert = require('assert') +const bs58 = require('bs58') + +module.exports = { + encode: (bytes) => bs58.encode(bytes), + decode: (data) => new Uint8Array(bs58.decode(data)) +}; diff --git a/app/common/lib/crypto/keyring.js b/app/common/lib/crypto/keyring.js new file mode 100644 index 000000000..ca74b8450 --- /dev/null +++ b/app/common/lib/crypto/keyring.js @@ -0,0 +1,75 @@ +"use strict"; +const nacl = require('tweetnacl'); +const base58 = require('./base58'); +const seedrandom = require('seedrandom'); +const naclBinding = require('naclb'); + +nacl.util = require('./nacl-util'); + +const crypto_sign_BYTES = 64; + +/** + * Verify a signature against data & public key. + * Return true of false as callback argument. + */ +function verify(rawMsg, rawSig, rawPub) { + const msg = nacl.util.decodeUTF8(rawMsg); + const sig = nacl.util.decodeBase64(rawSig); + const pub = base58.decode(rawPub); + const m = new Uint8Array(crypto_sign_BYTES + msg.length); + const sm = new Uint8Array(crypto_sign_BYTES + msg.length); + let i; + for (i = 0; i < crypto_sign_BYTES; i++) sm[i] = sig[i]; + for (i = 0; i < msg.length; i++) sm[i+crypto_sign_BYTES] = msg[i]; + + // Call to verification lib... + return naclBinding.verify(m, sm, pub); +} + +function Key(pub, sec) { + /***************************** + * + * GENERAL CRYPTO + * + *****************************/ + + this.publicKey = pub; + this.secretKey = sec; + + const rawSec = () => base58.decode(this.secretKey); + + this.json = () => { return { + pub: this.publicKey, + sec: this.secretKey + }}; + + this.sign = (msg) => Promise.resolve(this.signSync(msg)); + + this.signSync = (msg) => { + const m = nacl.util.decodeUTF8(msg); + const signedMsg = naclBinding.sign(m, rawSec()); + const sig = new Uint8Array(crypto_sign_BYTES); + for (let i = 0; i < sig.length; i++) { + sig[i] = signedMsg[i]; + } + return nacl.util.encodeBase64(sig); + }; +} + +function randomKey() { + const byteseed = new Uint8Array(32) + for (let i = 0; i < 32; i++) { + byteseed[i] = Math.floor(seedrandom()() * 255) + 1 + } + const keypair = nacl.sign.keyPair.fromSeed(byteseed) + return new Key( + base58.encode(keypair.publicKey), + base58.encode(keypair.secretKey) + ) +} + +module.exports ={ + randomKey, + Key: (pub, sec) => new Key(pub, sec), + verify: verify +}; diff --git a/app/common/lib/crypto/nacl-util.js b/app/common/lib/crypto/nacl-util.js new file mode 100644 index 000000000..05cebc0f4 --- /dev/null +++ b/app/common/lib/crypto/nacl-util.js @@ -0,0 +1,50 @@ +// Written in 2014-2016 by Dmitry Chestnykh and Devi Mandiri. +// Public domain. +(function(root, f) { + 'use strict'; + if (typeof module !== 'undefined' && module.exports) module.exports = f(); + else if (root.nacl) root.nacl.util = f(); + else { + root.nacl = {}; + root.nacl.util = f(); + } +}(this, function() { + 'use strict'; + + let util = {}; + + util.decodeUTF8 = function(s) { + let i, d = unescape(encodeURIComponent(s)), b = new Uint8Array(d.length); + for (i = 0; i < d.length; i++) b[i] = d.charCodeAt(i); + return b; + }; + + util.encodeUTF8 = function(arr) { + let i, s = []; + for (i = 0; i < arr.length; i++) s.push(String.fromCharCode(arr[i])); + return decodeURIComponent(escape(s.join(''))); + }; + + util.encodeBase64 = function(arr) { + if (typeof btoa === 'undefined' || !window) { + return (new Buffer(arr)).toString('base64'); + } else { + let i, s = [], len = arr.length; + for (i = 0; i < len; i++) s.push(String.fromCharCode(arr[i])); + return btoa(s.join('')); + } + }; + + util.decodeBase64 = function(s) { + if (typeof atob === 'undefined' || !window) { + return new Uint8Array(Array.prototype.slice.call(new Buffer(s, 'base64'), 0)); + } else { + let i, d = atob(s), b = new Uint8Array(d.length); + for (i = 0; i < d.length; i++) b[i] = d.charCodeAt(i); + return b; + } + }; + + return util; + +})); diff --git a/app/common/lib/document/block.js b/app/common/lib/document/block.js new file mode 100644 index 000000000..c0c3f9eea --- /dev/null +++ b/app/common/lib/document/block.js @@ -0,0 +1,356 @@ +"use strict"; +const _ = require('underscore') +const constants = require('../constants'); +const regex = require('../regex'); +const hashf = require('../hashf'); +const Transaction = require('./transaction'); + +// Constants +const SIGNED = false +const UNSIGNED = !SIGNED + +module.exports = class Block { + + constructor( + version, + nonce, + number, + powMin, + time, + medianTime, + membersCount, + monetaryMass, + unitbase, + issuersCount, + issuersFrame, + issuersFrameVar, + len, + currency, + issuer, + signature, + hash, + parameters, + previousHash, + previousIssuer, + inner_hash, + dividend, + identities, + joiners, + actives, + leavers, + revoked, + excluded, + certifications, + transactions + )Â { + this.version = parseInt(version) + this.nonce = parseInt(nonce) + this.number = parseInt(number) + this.powMin = parseInt(powMin) + this.time = parseInt(time) + this.medianTime = parseInt(medianTime) + this.membersCount = parseInt(membersCount) + this.monetaryMass = parseInt(monetaryMass) + this.unitbase = parseInt(unitbase) + this.issuersCount = parseInt(issuersCount) + this.issuersFrame = parseInt(issuersFrame) + this.issuersFrameVar = parseInt(issuersFrameVar) + this.len = parseInt(len) + this.currency = currency || "" + this.issuer = issuer || "" + this.signature = signature || "" + this.hash = hash || "" + this.parameters = parameters || "" + this.previousHash = previousHash || null + this.previousIssuer = previousIssuer || null + this.inner_hash = inner_hash || null + this.dividend = parseInt(dividend) || null + this.identities = (identities || []).slice() + this.joiners = (joiners || []).slice() + this.actives = (actives || []).slice() + this.leavers = (leavers || []).slice() + this.revoked = (revoked || []).slice() + this.excluded = (excluded || []).slice() + this.certifications = (certifications || []).slice() + this.transactions = (transactions || []).slice() + } + + /** + * Aliases + */ + + get pub() { + return this.pubkey + } + + get statics() { + return { + } + } + + /** + * Methods + */ + + json() { + return { + version: this.version, + nonce: this.nonce, + number: this.number, + powMin: this.powMin, + time: this.time, + medianTime: this.medianTime, + membersCount: this.membersCount, + monetaryMass: this.monetaryMass, + unitbase: this.unitbase, + issuersCount: this.issuersCount, + issuersFrame: this.issuersFrame, + issuersFrameVar: this.issuersFrameVar, + len: this.len, + currency: this.currency, + issuer: this.issuer, + signature: this.signature, + hash: this.hash, + parameters: this.parameters, + previousHash: this.previousHash, + previousIssuer: this.previousIssuer, + inner_hash: this.inner_hash, + dividend: this.dividend, + identities: this.identities, + joiners: this.joiners, + actives: this.actives, + leavers: this.leavers, + revoked: this.revoked, + excluded: this.excluded, + certifications: this.certifications, + transactions: this.transactions.map((tx) => _.omit(tx, 'raw', 'certifiers', 'hash')) + } + } + + getRawSigned() { + return Block.toRAWinnerPartWithHashAndNonce(this, SIGNED) + } + + getRawInnerPart() { + return Block.toRAWInnerPart(this) + } + + getSignedPart() { + return Block.toRAWHashAndNonce(this, UNSIGNED) + } + + static getLen(block) { + return block.identities.length + + block.joiners.length + + block.actives.length + + block.leavers.length + + block.revoked.length + + block.certifications.length + + block.transactions.reduce((sum, tx) => sum + Transaction.getLen(tx), 0) + } + + static getHash(json) { + const raw = Block.toRAWHashAndNonce(json) + return hashf(raw).toUpperCase() + } + + static fromJSON(json) { + // Returns a new Peer only if `json` is defined and not null + if (!json) return null + return new Block( + json.version || constants.DOCUMENTS_VERSION, + json.nonce, + json.number, + json.powMin, + json.time, + json.medianTime, + json.membersCount, + json.monetaryMass, + json.unitbase, + json.issuersCount, + json.issuersFrame, + json.issuersFrameVar, + json.len, + json.currency, + json.issuer, + json.signature, + json.hash, + json.parameters, + json.previousHash, + json.previousIssuer, + json.inner_hash, + json.dividend, + json.identities, + json.joiners, + json.actives, + json.leavers, + json.revoked, + json.excluded, + json.certifications, + json.transactions + ) + } + + static toRAW(json, unsigned) { + const block = Block.fromJSON(json) + let raw = ""; + raw += "Version: " + block.version + "\n"; + raw += "Type: Block\n"; + raw += "Currency: " + block.currency + "\n"; + raw += "Number: " + block.number + "\n"; + raw += "PoWMin: " + block.powMin + "\n"; + raw += "Time: " + block.time + "\n"; + raw += "MedianTime: " + block.medianTime + "\n"; + if (block.dividend) + raw += "UniversalDividend: " + block.dividend + "\n"; + raw += "UnitBase: " + block.unitbase + "\n"; + raw += "Issuer: " + block.issuer + "\n"; + raw += "IssuersFrame: " + block.issuersFrame + "\n"; + raw += "IssuersFrameVar: " + block.issuersFrameVar + "\n"; + raw += "DifferentIssuersCount: " + block.issuersCount + "\n"; + if(block.previousHash) + raw += "PreviousHash: " + block.previousHash + "\n"; + if(block.previousIssuer) + raw += "PreviousIssuer: " + block.previousIssuer + "\n"; + if(block.parameters) + raw += "Parameters: " + block.parameters + "\n"; + raw += "MembersCount: " + block.membersCount + "\n"; + raw += "Identities:\n"; + for (const idty of (block.identities || [])){ + raw += idty + "\n"; + } + raw += "Joiners:\n"; + for (const joiner of (block.joiners || [])){ + raw += joiner + "\n"; + } + raw += "Actives:\n"; + for (const active of (block.actives || [])){ + raw += active + "\n"; + } + raw += "Leavers:\n"; + for (const leaver of (block.leavers || [])){ + raw += leaver + "\n"; + } + raw += "Revoked:\n"; + for (const revoked of (block.revoked || [])){ + raw += revoked + "\n"; + } + raw += "Excluded:\n"; + for (const excluded of (block.excluded || [])){ + raw += excluded + "\n"; + } + raw += "Certifications:\n"; + for (const cert of (block.certifications || [])){ + raw += cert + "\n"; + } + raw += "Transactions:\n"; + for (const tx of (block.transactions || [])){ + raw += tx.raw || Block.getCompactTransaction(tx); + } + if (!unsigned) { + raw += block.signature + '\n' + } + return raw + } + + static toRAWInnerPart(json) { + const block = Block.fromJSON(json) + let raw = ""; + raw += "Version: " + block.version + "\n"; + raw += "Type: Block\n"; + raw += "Currency: " + block.currency + "\n"; + raw += "Number: " + block.number + "\n"; + raw += "PoWMin: " + block.powMin + "\n"; + raw += "Time: " + block.time + "\n"; + raw += "MedianTime: " + block.medianTime + "\n"; + if (block.dividend) + raw += "UniversalDividend: " + block.dividend + "\n"; + raw += "UnitBase: " + block.unitbase + "\n"; + raw += "Issuer: " + block.issuer + "\n"; + raw += "IssuersFrame: " + block.issuersFrame + "\n"; + raw += "IssuersFrameVar: " + block.issuersFrameVar + "\n"; + raw += "DifferentIssuersCount: " + block.issuersCount + "\n"; + if(block.previousHash) + raw += "PreviousHash: " + block.previousHash + "\n"; + if(block.previousIssuer) + raw += "PreviousIssuer: " + block.previousIssuer + "\n"; + if(block.parameters) + raw += "Parameters: " + block.parameters + "\n"; + raw += "MembersCount: " + block.membersCount + "\n"; + raw += "Identities:\n"; + for (const idty of (block.identities || [])){ + raw += idty + "\n"; + } + raw += "Joiners:\n"; + for (const joiner of (block.joiners || [])){ + raw += joiner + "\n"; + } + raw += "Actives:\n"; + for (const active of (block.actives || [])){ + raw += active + "\n"; + } + raw += "Leavers:\n"; + for (const leaver of (block.leavers || [])){ + raw += leaver + "\n"; + } + raw += "Revoked:\n"; + for (const revoked of (block.revoked || [])){ + raw += revoked + "\n"; + } + raw += "Excluded:\n"; + for (const excluded of (block.excluded || [])){ + raw += excluded + "\n"; + } + raw += "Certifications:\n"; + for (const cert of (block.certifications || [])){ + raw += cert + "\n"; + } + raw += "Transactions:\n"; + for (const tx of (block.transactions || [])){ + raw += tx.raw || Transaction.getCompactTransaction(tx); + } + return raw + } + + static toRAWinnerPartWithHashAndNonce(json, unsigned) { + let raw = Block.toRAWInnerPart(json); + raw += "InnerHash: " + json.inner_hash + "\n" + raw += "Nonce: " + json.nonce + "\n" + if (unsigned === false) { + raw += json.signature + "\n" + } + return raw + }; + + static toRAWHashAndNonce(json, unsigned) { + let raw = "" + + "InnerHash: " + json.inner_hash + "\n" + + "Nonce: " + json.nonce + "\n" + if (!unsigned) { + raw += json.signature + "\n" + } + return raw + } + + static getTransactions(json) { + const block = Block.fromJSON(json) + return block.transactions.slice().map((tx) => { + tx.inputs = tx.inputs.map((i) => { + if (typeof i === 'string') { + return Transaction.inputStr2Obj(i) + } else { + return i + } + }) + tx.outputs = tx.outputs.map((o) => { + if (typeof o === 'string') { + return Transaction.outputStr2Obj(o) + } else { + return o + } + }) + return tx + }) + }; + +} diff --git a/app/common/lib/document/certification.js b/app/common/lib/document/certification.js new file mode 100644 index 000000000..3cc70e69e --- /dev/null +++ b/app/common/lib/document/certification.js @@ -0,0 +1,149 @@ +"use strict"; +const constants = require('../constants'); +const regex = require('../regex'); +const hashf = require('../hashf'); + +// Constants +const SIGNED = true +const UNSIGNED = !SIGNED + +module.exports = class Certification { + + constructor( + version, + currency, + issuer, + blockstamp, + idty_issuer, + idty_uid, + idty_buid, + idty_sig, + signature)Â { + this.version = version + this.currency = currency + this.issuer = issuer + this.blockstamp = blockstamp + this.idty_issuer = idty_issuer + this.idty_uid = idty_uid + this.idty_buid = idty_buid + this.idty_sig = idty_sig + this.signature = signature + } + + /** + * Aliases + */ + + get buid() { + return this.blockstamp + } + + set buid(buid) { + this.blockstamp = buid + } + + get sig() { + return this.signature + } + + set sig(sig) { + this.signature = sig + } + + get pubkey() { + return this.issuer + } + + set pubkey(pubkey) { + this.issuer = pubkey + } + + get to() { + return this.idty_issuer + } + + set to(to) { + this.idty_issuer = to + } + + get blockNumber() { + if (!this.blockstamp) { + return null + } + return parseInt(this.blockstamp.split('-')[0]) + } + + get block_number() { + return this.blockNumber + } + + get blockHash() { + if (!this.blockstamp) { + return null + } + return this.blockstamp.split('-')[1] + } + + get block_hash() { + return this.blockHash + } + + /** + * Methods + */ + + getRaw() { + return Certification.toRAW(this); + } + + inline() { + return [this.pubkey, this.to, this.block_number, this.sig].join(':') + } + + /** + * Statics + */ + + static fromJSON(json) { + const buid = [json.block_number, json.block_hash].join('-') + return new Certification( + json.version || constants.DOCUMENTS_VERSION, + json.currency, + json.issuer || json.pubkey || json.from, + json.blockstamp || json.buid ||Â buid, + json.idty_issuer || json.to, + json.idty_uid, + json.idty_buid, + json.idty_sig, + json.signature || json.sig + ) + } + + static toRAW(json, withSig = true) { + const cert = Certification.fromJSON(json) + let raw = ""; + raw += "Version: " + cert.version + "\n"; + raw += "Type: Certification\n"; + raw += "Currency: " + cert.currency + "\n"; + raw += "Issuer: " + cert.issuer + "\n"; + raw += "IdtyIssuer: " + cert.idty_issuer + '\n'; + raw += "IdtyUniqueID: " + cert.idty_uid + '\n'; + raw += "IdtyTimestamp: " + cert.idty_buid + '\n'; + raw += "IdtySignature: " + cert.idty_sig + '\n'; + raw += "CertTimestamp: " + cert.buid + '\n'; + if (cert.sig && withSig) { + raw += cert.sig + '\n' + } + return raw + } + + static fromInline(inline) { + const sp = inline.split(':') + return Certification.fromJSON({ + pubkey: sp[0], + to: sp[1], + block_number: parseInt(sp[2]), + sig: sp[3] + }) + } +} diff --git a/app/common/lib/document/identity.js b/app/common/lib/document/identity.js new file mode 100644 index 000000000..2792fe73e --- /dev/null +++ b/app/common/lib/document/identity.js @@ -0,0 +1,187 @@ +"use strict"; +const constants = require('../constants'); +const regex = require('../regex'); +const hashf = require('../hashf'); + +// Constants +const SIGNED = true +const UNSIGNED = !SIGNED + +module.exports = class Identity { + + constructor( + version, + currency, + pubkey, + uid, + blockstamp, + signature, + revoked, + revoked_on, + revocation_sig)Â { + this.version = version || constants.DOCUMENTS_VERSION + this.currency = currency + this.pubkey = pubkey + this.uid = uid + this.blockstamp = blockstamp + this.signature = signature + this.revoked = revoked + this.revoked_on = revoked_on + this.revocation_sig = revocation_sig + this.certs = [] + } + + /** + * Aliases + */ + + get buid() { + return this.blockstamp + } + + set buid(buid) { + this.blockstamp = buid + } + + get sig() { + return this.signature + } + + set sig(sig) { + this.signature = sig + } + + get blockNumber() { + if (!this.blockstamp) { + return null + } + return parseInt(this.blockstamp.split('-')[0]) + } + + get blockHash() { + if (!this.blockstamp) { + return null + } + return this.blockstamp.split('-')[1] + } + + get hash() { + return this.getTargetHash() + } + + /** + * Methods + */ + + getTargetHash() { + return hashf(this.uid + this.buid + this.pubkey).toUpperCase(); + } + + inline() { + return [this.pubkey, this.sig, this.buid, this.uid].join(':'); + } + + rawWithoutSig() { + return Identity.toRAW(this, UNSIGNED) + } + + json() { + const others = []; + this.certs.forEach((cert) => { + others.push({ + "pubkey": cert.from, + "meta": { + "block_number": cert.block_number, + "block_hash": cert.block_hash + }, + "uids": cert.uids, + "isMember": cert.isMember, + "wasMember": cert.wasMember, + "signature": cert.sig + }); + }); + const uids = [{ + "uid": this.uid, + "meta": { + "timestamp": this.buid + }, + "revoked": this.revoked, + "revoked_on": this.revoked_on, + "revocation_sig": this.revocation_sig, + "self": this.sig, + "others": others + }]; + const signed = []; + this.signed.forEach((cert) => { + signed.push({ + "uid": cert.idty.uid, + "pubkey": cert.idty.pubkey, + "meta": { + "timestamp": cert.idty.buid + }, + "cert_time": { + "block": cert.block_number, + "block_hash": cert.block_hash + }, + "isMember": cert.idty.member, + "wasMember": cert.idty.wasMember, + "signature": cert.sig + }); + }); + return { + "pubkey": this.pubkey, + "uids": uids, + "signed": signed + } + } + + /** + * Statics + */ + + static fromJSON(json) { + return new Identity( + json.version || constants.DOCUMENTS_VERSION, + json.currency, + json.pubkey || json.issuer, + json.uid, + json.blockstamp || json.buid, + json.signature || json.sig, + json.revoked, + json.revoked_on, + json.revocation_sig) + } + + static toRAW(json, withSig) { + const idty = Identity.fromJSON(json) + let raw = "" + raw += "Version: " + idty.version + "\n" + raw += "Type: Identity\n" + raw += "Currency: " + idty.currency + "\n" + raw += "Issuer: " + (idty.issuer || idty.pubkey) + "\n" + raw += "UniqueID: " + idty.uid + '\n' + raw += "Timestamp: " + idty.buid + '\n' + if (idty.sig && withSig) { + raw += idty.sig + '\n' + } + return raw + } + + static fromInline(inline) { + const sp = inline.split(':') + return Identity.fromJSON({ + pubkey: sp[0], + sig: sp[1], + buid: sp[2], + uid: sp[3] + }) + } + + static revocationFromInline(inline) { + const sp = inline.split(':') + return { + pubkey: sp[0], + sig: sp[1] + } + } +} diff --git a/app/common/lib/document/index.js b/app/common/lib/document/index.js new file mode 100644 index 000000000..c7c42fb9c --- /dev/null +++ b/app/common/lib/document/index.js @@ -0,0 +1,10 @@ +"use strict"; + +module.exports = { + Peer: require('./peer'), + Block: require('./block'), + Membership: require('./membership'), + Transaction: require('./transaction'), + Identity: require('./identity'), + Certification: require('./certification') +} \ No newline at end of file diff --git a/app/common/lib/document/membership.js b/app/common/lib/document/membership.js new file mode 100644 index 000000000..14844db8c --- /dev/null +++ b/app/common/lib/document/membership.js @@ -0,0 +1,123 @@ +"use strict"; +const constants = require('../constants'); +const regex = require('../regex'); + +// Constants +const SIGNED = false +const UNSIGNED = !SIGNED + +module.exports = class Membership { + + constructor( + version, + currency, + issuer, + membership, + userid, + blockstamp, + certts, + signature)Â { + this.version = version + this.currency = currency + this.issuer = issuer + this.membership = membership + this.userid = userid + this.blockstamp = blockstamp + this.certts = certts + this.signature = signature + } + + /** + * Aliases + */ + + get number() { + return this.blockNumber + } + + get fpr() { + return this.blockHash + } + + get block() { + return this.blockstamp + } + + get blockNumber() { + if (!this.blockstamp) { + return null + } + return parseInt(this.blockstamp.split('-')[0]) + } + + get blockHash() { + if (!this.blockstamp) { + return null + } + return this.blockstamp.split('-')[1] + } + + /** + * Methods + */ + + getRaw() { + return Membership.toRAW(this) + } + + inline() { + return [ + this.issuer, + this.signature, + this.blockstamp, + this.certts, + this.userid + ].join(':') + } + + /** + * Statics + */ + + static toRAW(json) { + const ms = Membership.fromJSON(json) + let raw = "" + raw += "Version: " + ms.version + "\n" + raw += "Type: Membership\n" + raw += "Currency: " + ms.currency + "\n" + raw += "Issuer: " + ms.issuer + "\n" + raw += "Block: " + ms.block + "\n" + raw += "Membership: " + ms.membership + "\n" + if (ms.userid) + raw += "UserID: " + ms.userid + "\n" + if (ms.certts) + raw += "CertTS: " + ms.certts + "\n" + return raw + } + + static fromJSON(json) { + return new Membership( + json.version || constants.DOCUMENTS_VERSION, + json.currency, + json.issuer, + json.membership, + json.userid, + json.blockstamp || json.block, + json.certts, + json.signature) + } + + static fromInline(inline, type, currency) { + const sp = inline.split(':') + return Membership.fromJSON({ + version: constants.DOCUMENTS_VERSION, + currency: currency, + issuer: sp[0], + membership: type, + userid: sp[4], + blockstamp: sp[2], + certts: sp[3], + signature: sp[1] + }) + } +} diff --git a/app/common/lib/document/peer.js b/app/common/lib/document/peer.js new file mode 100644 index 000000000..92c0ce055 --- /dev/null +++ b/app/common/lib/document/peer.js @@ -0,0 +1,200 @@ +"use strict"; +const constants = require('../constants'); +const regex = require('../regex'); + +// Constants +const SIGNED = false +const UNSIGNED = !SIGNED + +module.exports = class Peer { + + constructor(version, currency, pubkey, endpoints, blockstamp, signature)Â { + + this.version = version + this.currency = currency + this.pubkey = pubkey + this.endpoints = endpoints || []; + this.blockstamp = blockstamp; + this.signature = signature; + } + + /** + * Aliases + */ + + get pub() { + return this.pubkey + } + + set pub(pub) { + this.pubkey = pub + } + + get block() { + return this.blockstamp + } + + get statics() { + return { + peerize: (json) => Peer.fromJSON(json), + fromJSON: (json) => Peer.fromJSON(json) + } + } + + /** + * Methods + */ + + getDns() { + const bma = this.getBMA(); + return bma.dns ? bma.dns : null; + } + + getIPv4() { + const bma = this.getBMA(); + return bma.ipv4 ? bma.ipv4 : null; + } + + getIPv6() { + const bma = this.getBMA(); + return bma.ipv6 ? bma.ipv6 : null; + } + + getPort() { + const bma = this.getBMA(); + return bma.port ? bma.port : null; + } + + getBMA() { + let bma = null; + this.endpoints.forEach((ep) => { + const matches = !bma && ep.match(regex.BMA_REGEXP); + if (matches) { + bma = { + "dns": matches[2] || '', + "ipv4": matches[4] || '', + "ipv6": matches[6] || '', + "port": matches[8] || 9101 + }; + } + }); + return bma || {}; + } + + getURL() { + const bma = this.getBMA(); + let base = this.getHostPreferDNS(); + if(bma.port) + base += ':' + bma.port; + return base; + } + + getHostPreferDNS() { + const bma = this.getBMA(); + return (bma.dns ? bma.dns : + (bma.ipv4 ? bma.ipv4 : + (bma.ipv6 ? bma.ipv6 : ''))); + } + + getRaw() { + return Peer.toRAW(this, SIGNED) + } + + getRawUnsigned() { + return Peer.toRAW(this, UNSIGNED) + } + + endpointSum() { + return this.endpoints.join('_') + } + + blockNumber() { + return this.blockstamp.match(/^(\d+)-/)[1] + } + + static fromJSON(json) { + // Returns a new Peer only if `json` is defined and not null + if (!json) return null + return new Peer( + json.version || constants.DOCUMENTS_VERSION, + json.currency, + json.pubkey || json.pub || "", + json.endpoints, + json.blockstamp || json.block, + json.signature + ) + } + + static toRAW(json, unsigned) { + const p = Peer.fromJSON(json) + let raw = "" + raw += "Version: " + p.version + "\n" + raw += "Type: Peer\n" + raw += "Currency: " + p.currency + "\n" + raw += "PublicKey: " + p.pubkey + "\n" + raw += "Block: " + p.blockstamp + "\n" + raw += "Endpoints:" + "\n" + for(const ep of p.endpoints) { + raw += ep + "\n" + } + if (!unsigned) { + raw += json.signature + '\n' + } + return raw + } + + static endpoint2host(endpoint) { + return Peer.fromJSON({ endpoints: [endpoint] }).getURL() + } + + static endpointSum(json) { + return Peer.fromJSON(json).endpointSum() + } + + static blockNumber(json) { + const p = Peer.fromJSON(json) + return p ? p.blockNumber() : -1 + } + + keyID () { + return this.pubkey && this.pubkey.length > 10 ? this.pubkey.substring(0, 10) : "Unknown" + } + + copyValues(to) { + ["version", "currency", "pub", "endpoints", "hash", "status", "statusTS", "block", "signature"].forEach((key)=> { + to[key] = this[key]; + }); + } + + // this.copyValuesFrom = (from) => { + // ["version", "currency", "pub", "endpoints", "block", "signature"].forEach((key) => { + // this[key] = from[key]; + // }); + // }; + // + // this.json = () => { + // const json = {}; + // ["version", "currency", "endpoints", "status", "block", "signature"].forEach((key) => { + // json[key] = this[key]; + // }); + // json.raw = this.getRaw(); + // json.pubkey = this.pubkey; + // return json; + // }; + + // this.hasValid4 = (bma) => !!(bma.ipv4 && !bma.ipv4.match(/^127.0/) && !bma.ipv4.match(/^192.168/)); + // + // this.getNamedURL = () => this.getURL(); + // + // this.getRaw = () => rawer.getPeerWithoutSignature(this); + // + // this.getRawSigned = () => rawer.getPeer(this); + // + // this.isReachable = () => { + // return !!this.getURL(); + // }; + // + // this.containsEndpoint = (ep) => this.endpoints.reduce((found, endpoint) => found || endpoint === ep, false); + // + // +} diff --git a/app/common/lib/document/transaction.js b/app/common/lib/document/transaction.js new file mode 100644 index 000000000..aad1e08c7 --- /dev/null +++ b/app/common/lib/document/transaction.js @@ -0,0 +1,189 @@ +"use strict"; +const constants = require('../constants'); +const regex = require('../regex'); +const hashf = require('../hashf'); + +// Constants +const SIGNED = false +const UNSIGNED = !SIGNED + +module.exports = class Transaction { + + constructor( + version, + currency, + issuers, + signatures, + inputs, + unlocks, + outputs, + comment, + blockstamp, + blockstampTime, + locktime, + hash + )Â { + this.version = version + this.currency = currency + this.issuers = (issuers || []) + this.signatures = (signatures || []) + this.inputsRAW = (inputs || []).map(i => typeof i === 'string' ? i : Transaction.inputObj2Str(i)) + this.outputsRAW = (outputs || []).map(o => typeof o === 'string' ? o : Transaction.outputObj2Str(o)) + this.inputs = this.inputsRAW.map(i => Transaction.inputStr2Obj(i)) + this.outputs = this.outputsRAW.map(o => Transaction.outputStr2Obj(o)) + this.unlocks = (unlocks || []) + this.output_amount = this.outputs.reduce((sum, output) => sum + output.amount * Math.pow(10, output.base), 0) + this.output_base = this.outputs.reduce((maxBase, output) => Math.max(maxBase, parseInt(output.base)), 0) + this.comment = comment + this.blockstamp = blockstamp + this.blockstampTime = blockstampTime + this.locktime = locktime || 0 + this.hash = hash + } + + json() { + return { + 'version': parseInt(this.version, 10), + 'currency': this.currency, + 'issuers': this.issuers.slice(), + 'inputs': this.inputsRAW.slice(), + 'unlocks': this.unlocks.slice(), + 'outputs': this.outputsRAW.slice(), + 'comment': this.comment, + 'locktime': this.locktime, + 'blockstamp': this.blockstamp, + 'blockstampTime': this.blockstampTime, + 'signatures': this.signatures.slice(), + 'raw': Transaction.toRAW(this), + 'hash': this.hash + } + } + + compact() { + return Transaction.getCompactTransaction(this) + } + + getHash(json) { + const raw = Transaction.toRAW(this) + return hashf(raw).toUpperCase() + } + + /** + * Aliases + */ + + /** + * Methods + */ + + static getLen(tx) { + return 2 // header + blockstamp + + tx.issuers.length * 2 // issuers + signatures + + tx.inputs.length * 2 // inputs + unlocks + + (tx.comment ? 1 : 0) + + tx.outputs.length + } + + static fromJSON(json) { + return new Transaction( + json.version || constants.DOCUMENTS_VERSION, + json.currency, + json.issuers, + json.signatures, + json.inputsRAW || json.inputs, + json.unlocks, + json.outputsRAW || json.outputs, + json.comment, + json.blockstamp, + json.blockstampTime, + json.locktime, + json.hash + ) + } + + static toRAW(json) { + const tx = Transaction.fromJSON(json) + let raw = "" + raw += "Version: " + (tx.version) + "\n" + raw += "Type: Transaction\n" + raw += "Currency: " + tx.currency + "\n" + raw += "Blockstamp: " + tx.blockstamp + "\n" + raw += "Locktime: " + tx.locktime + "\n" + raw += "Issuers:\n"; + (tx.issuers || []).forEach((issuer) => { + raw += issuer + '\n' + }) + raw += "Inputs:\n"; + (tx.inputsRAW || []).forEach((input) => { + raw += input + '\n' + }) + raw += "Unlocks:\n"; + (tx.unlocks || []).forEach((unlock) => { + raw += unlock + '\n' + }) + raw += "Outputs:\n"; + (tx.outputsRAW || []).forEach((output) => { + raw += output + '\n' + }) + raw += "Comment: " + (tx.comment || "") + "\n"; + (tx.signatures || []).forEach((signature) => { + raw += signature + '\n' + }) + return raw + } + + static inputObj2Str(inputObj) { + return [inputObj.amount, inputObj.base, inputObj.type, inputObj.identifier, inputObj.pos].join(':') + } + + static outputObj2Str(oupoutObj) { + return [oupoutObj.amount, oupoutObj.base, oupoutObj.conditions].join(':') + } + + static inputStr2Obj(inputStr) { + const sp = inputStr.split(':') + return { + amount: sp[0], + base: sp[1], + type: sp[2], + identifier: sp[3], + pos: parseInt(sp[4]), + raw: inputStr + } + } + + static outputStr2Obj(outputStr) { + const sp = outputStr.split(':') + return { + amount: parseInt(sp[0]), + base: parseInt(sp[1]), + conditions: sp[2], + raw: outputStr + } + } + + static getCompactTransaction(json) { + const tx = Transaction.fromJSON(json) + let issuers = tx.issuers; + let raw = ["TX", tx.version, issuers.length, tx.inputs.length, tx.unlocks.length, tx.outputs.length, tx.comment ? 1 : 0, tx.locktime || 0].join(':') + '\n'; + raw += tx.blockstamp + "\n"; + (issuers || []).forEach((issuer) => { + raw += issuer + '\n'; + }); + (tx.inputsRAW || []).forEach((input) => { + raw += input + '\n'; + }); + (tx.unlocks || []).forEach((input) => { + raw += input + '\n'; + }); + (tx.outputsRAW || []).forEach((output) => { + raw += output + '\n'; + }); + if (tx.comment) + raw += tx.comment + '\n'; + (tx.signatures || []).forEach((signature) => { + raw += signature + '\n' + }) + return raw + } +} diff --git a/app/common/lib/dos2unix.js b/app/common/lib/dos2unix.js new file mode 100644 index 000000000..32fdce593 --- /dev/null +++ b/app/common/lib/dos2unix.js @@ -0,0 +1,23 @@ +"use strict"; +const util = require('util'); +const stream = require('stream'); + +module.exports = function (str) { + if (str) + return dos2unix(str); + else + return new Dos2UnixStream(); +}; + +const dos2unix = (str) => str.replace(/\r\n/g, '\n'); + +function Dos2UnixStream () { + stream.Transform.apply(this); + + this._write = function (str, enc, done) { + this.push(dos2unix(str.toString())); + done(); + } +} + +util.inherits(Dos2UnixStream, stream.Transform); diff --git a/app/common/lib/hashf.js b/app/common/lib/hashf.js new file mode 100644 index 000000000..f1a1bd2ee --- /dev/null +++ b/app/common/lib/hashf.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = function (str){ + return require("crypto") + .createHash("sha256") + .update(str) + .digest("hex"); +}; diff --git a/app/common/lib/parsers/GenericParser.js b/app/common/lib/parsers/GenericParser.js new file mode 100644 index 000000000..a1424acee --- /dev/null +++ b/app/common/lib/parsers/GenericParser.js @@ -0,0 +1,108 @@ +"use strict"; +const util = require('util'); +const stream = require('stream'); +const hashf = require('../hashf'); +const constants = require('../constants'); + +module.exports = GenericParser; + +function GenericParser (captures, multipleLinesFields, rawerFunc) { + + stream.Transform.call(this, { decodeStrings: false, objectMode: true }); + + this.rawerFunc = rawerFunc; + + this._simpleLineExtraction = (pr, rawEntry, cap) => { + const fieldValue = rawEntry.match(cap.regexp); + if(fieldValue && fieldValue.length >= 2){ + pr[cap.prop] = cap.parser ? cap.parser(fieldValue[1], pr) : fieldValue[1]; + } + return; + }; + + this._multipleLinesExtraction = (am, wholeAmend, cap) => { + const fieldValue = wholeAmend.match(cap.regexp); + let line = 0; + am[cap.prop] = []; + if(fieldValue && fieldValue.length >= 2) + { + const lines = fieldValue[1].split(/\n/); + if(lines[lines.length - 1].match(/^$/)){ + for (let i = 0; i < lines.length - 1; i++) { + line = lines[i]; + let fprChange = line.match(/([+-][A-Z\d]{40})/); + if(fprChange && fprChange.length === 2){ + am[cap.prop].push(fprChange[1]); + } + else{ + return "Wrong structure for line: '" + line + "'"; + } + } + } + else return "Wrong structure for line: '" + line + "'"; + } + }; + + this.syncWrite = (str, logger) => { + let error; + const obj = {}; + this._parse(str, obj); + this._clean(obj); + if (!error) { + error = this._verify(obj); + } + if (!error) { + const raw = this.rawerFunc(obj); + if (hashf(str) !== hashf(raw)) + error = constants.ERRORS.WRONG_DOCUMENT; + if (error) { + logger && logger.trace(error); + logger && logger.trace('-----------------'); + logger && logger.trace('Written: %s', JSON.stringify({ str: str })); + logger && logger.trace('Extract: %s', JSON.stringify({ raw: raw })); + logger && logger.trace('-----------------'); + } + } + if (error){ + logger && logger.trace(error); + throw constants.ERRORS.WRONG_DOCUMENT; + } + return obj; + }; + + this._parse = (str, obj) => { + let error; + if(!str){ + error = "No document given"; + } else { + error = ""; + obj.hash = hashf(str).toUpperCase(); + // Divide in 2 parts: document & signature + const sp = str.split('\n'); + if (sp.length < 3) { + error = "Wrong document: must have at least 2 lines"; + } + else { + const endOffset = str.match(/\n$/) ? 2 : 1; + obj.signature = sp[sp.length - endOffset]; + obj.hash = hashf(str).toUpperCase(); + obj.raw = sp.slice(0, sp.length - endOffset).join('\n') + '\n'; + const docLF = obj.raw.replace(/\r\n/g, "\n"); + if(docLF.match(/\n$/)){ + captures.forEach((cap) => { + if(~multipleLinesFields.indexOf(multipleLinesFields)) + error = this._multipleLinesExtraction(obj, docLF, cap); + else + this._simpleLineExtraction(obj, docLF, cap); + }); + } + else{ + error = "Bad document structure: no new line character at the end of the document."; + } + } + } + return error; + }; +} + +util.inherits(GenericParser, stream.Transform); diff --git a/app/common/lib/parsers/block.js b/app/common/lib/parsers/block.js new file mode 100644 index 000000000..31118f50f --- /dev/null +++ b/app/common/lib/parsers/block.js @@ -0,0 +1,249 @@ +"use strict"; +const util = require('util'); +const GenericParser = require('./GenericParser'); +const Block = require('../../../../app/common/lib/document/block'); +const hashf = require('../../../../app/common/lib/hashf'); +const rawer = require('../../../../app/common/lib/rawer'); +const constants = require('../../../../app/common/lib/constants'); + +module.exports = BlockParser; + +function BlockParser (onError) { + + const captures = [ + {prop: "version", regexp: constants.BLOCK.VERSION}, + {prop: "type", regexp: constants.BLOCK.TYPE}, + {prop: "currency", regexp: constants.BLOCK.CURRENCY}, + {prop: "number", regexp: constants.BLOCK.BNUMBER}, + {prop: "powMin", regexp: constants.BLOCK.POWMIN}, + {prop: "time", regexp: constants.BLOCK.TIME}, + {prop: "medianTime", regexp: constants.BLOCK.MEDIAN_TIME}, + {prop: "dividend", regexp: constants.BLOCK.UD}, + {prop: "unitbase", regexp: constants.BLOCK.UNIT_BASE}, + {prop: "issuer", regexp: constants.BLOCK.BLOCK_ISSUER}, + {prop: "issuersFrame", regexp: constants.BLOCK.BLOCK_ISSUERS_FRAME}, + {prop: "issuersFrameVar", regexp: constants.BLOCK.BLOCK_ISSUERS_FRAME_VAR}, + {prop: "issuersCount", regexp: constants.BLOCK.DIFFERENT_ISSUERS_COUNT}, + {prop: "parameters", regexp: constants.BLOCK.PARAMETERS}, + {prop: "previousHash", regexp: constants.BLOCK.PREV_HASH}, + {prop: "previousIssuer", regexp: constants.BLOCK.PREV_ISSUER}, + {prop: "membersCount", regexp: constants.BLOCK.MEMBERS_COUNT}, + {prop: "identities", regexp: /Identities:\n([\s\S]*)Joiners/, parser: splitAndMatch('\n', constants.IDENTITY.INLINE)}, + {prop: "joiners", regexp: /Joiners:\n([\s\S]*)Actives/, parser: splitAndMatch('\n', constants.BLOCK.JOINER)}, + {prop: "actives", regexp: /Actives:\n([\s\S]*)Leavers/, parser: splitAndMatch('\n', constants.BLOCK.ACTIVE)}, + {prop: "leavers", regexp: /Leavers:\n([\s\S]*)Excluded/, parser: splitAndMatch('\n', constants.BLOCK.LEAVER)}, + {prop: "revoked", regexp: /Revoked:\n([\s\S]*)Excluded/, parser: splitAndMatch('\n', constants.BLOCK.REVOCATION)}, + {prop: "excluded", regexp: /Excluded:\n([\s\S]*)Certifications/, parser: splitAndMatch('\n', constants.PUBLIC_KEY)}, + {prop: "certifications", regexp: /Certifications:\n([\s\S]*)Transactions/, parser: splitAndMatch('\n', constants.CERT.OTHER.INLINE)}, + {prop: "transactions", regexp: /Transactions:\n([\s\S]*)/, parser: extractTransactions}, + {prop: "inner_hash", regexp: constants.BLOCK.INNER_HASH}, + {prop: "nonce", regexp: constants.BLOCK.NONCE} + ]; + const multilineFields = []; + GenericParser.call(this, captures, multilineFields, rawer.getBlock, onError); + + this._clean = (obj) => { + obj.documentType = 'block'; + obj.identities = obj.identities || []; + obj.joiners = obj.joiners || []; + obj.actives = obj.actives || []; + obj.leavers = obj.leavers || []; + obj.revoked = obj.revoked || []; + obj.excluded = obj.excluded || []; + obj.certifications = obj.certifications || []; + obj.transactions = obj.transactions || []; + obj.version = obj.version || ''; + obj.type = obj.type || ''; + obj.hash = hashf(rawer.getBlockInnerHashAndNonceWithSignature(obj)).toUpperCase(); + obj.inner_hash = obj.inner_hash || ''; + obj.currency = obj.currency || ''; + obj.nonce = obj.nonce || ''; + obj.number = obj.number || ''; + obj.time = obj.time || ''; + obj.medianTime = obj.medianTime || ''; + obj.dividend = obj.dividend || null; + obj.unitbase = obj.unitbase || ''; + obj.issuer = obj.issuer || ''; + obj.parameters = obj.parameters || ''; + obj.previousHash = obj.previousHash || ''; + obj.previousIssuer = obj.previousIssuer || ''; + obj.membersCount = obj.membersCount || ''; + obj.transactions.map((tx) => { + tx.currency = obj.currency; + tx.hash = hashf(rawer.getTransaction(tx)).toUpperCase(); + }); + obj.len = Block.getLen(obj); + }; + + this._verify = (obj) => { + let err = null; + const codes = { + 'BAD_VERSION': 150, + 'BAD_CURRENCY': 151, + 'BAD_NUMBER': 152, + 'BAD_TYPE': 153, + 'BAD_NONCE': 154, + 'BAD_RECIPIENT_OF_NONTRANSFERT': 155, + 'BAD_PREV_HASH_PRESENT': 156, + 'BAD_PREV_HASH_ABSENT': 157, + 'BAD_PREV_ISSUER_PRESENT': 158, + 'BAD_PREV_ISSUER_ABSENT': 159, + 'BAD_DIVIDEND': 160, + 'BAD_TIME': 161, + 'BAD_MEDIAN_TIME': 162, + 'BAD_INNER_HASH': 163, + 'BAD_MEMBERS_COUNT': 164, + 'BAD_UNITBASE': 165 + }; + if(!err){ + // Version + if(!obj.version || !obj.version.match(constants.DOCUMENTS_BLOCK_VERSION_REGEXP)) + err = {code: codes.BAD_VERSION, message: "Version unknown"}; + } + if(!err){ + // Type + if(!obj.type || !obj.type.match(/^Block$/)) + err = {code: codes.BAD_TYPE, message: "Not a Block type"}; + } + if(!err){ + // Nonce + if(!obj.nonce || !obj.nonce.match(constants.INTEGER)) + err = {code: codes.BAD_NONCE, message: "Nonce must be an integer value"}; + } + if(!err){ + // Number + if(!obj.number || !obj.number.match(constants.INTEGER)) + err = {code: codes.BAD_NUMBER, message: "Incorrect Number field"}; + } + if(!err){ + // Time + if(!obj.time || !obj.time.match(constants.INTEGER)) + err = {code: codes.BAD_TIME, message: "Time must be an integer"}; + } + if(!err){ + // MedianTime + if(!obj.medianTime || !obj.medianTime.match(constants.INTEGER)) + err = {code: codes.BAD_MEDIAN_TIME, message: "MedianTime must be an integer"}; + } + if(!err){ + if(obj.dividend && !obj.dividend.match(constants.INTEGER)) + err = {code: codes.BAD_DIVIDEND, message: "Incorrect UniversalDividend field"}; + } + if(!err){ + if(obj.unitbase && !obj.unitbase.match(constants.INTEGER)) + err = {code: codes.BAD_UNITBASE, message: "Incorrect UnitBase field"}; + } + if(!err){ + if(!obj.issuer || !obj.issuer.match(constants.BASE58)) + err = {code: codes.BAD_ISSUER, message: "Incorrect Issuer field"}; + } + if(!err){ + // MembersCount + if(!obj.nonce || !obj.nonce.match(constants.INTEGER)) + err = {code: codes.BAD_MEMBERS_COUNT, message: "MembersCount must be an integer value"}; + } + if(!err){ + // InnerHash + if(!obj.inner_hash || !obj.inner_hash.match(constants.FINGERPRINT)) + err = {code: codes.BAD_INNER_HASH, message: "InnerHash must be a hash value"}; + } + return err && err.message; + }; +} + +function splitAndMatch (separator, regexp) { + return function (raw) { + const lines = raw.split(new RegExp(separator)); + const kept = []; + lines.forEach(function(line){ + if (line.match(regexp)) + kept.push(line); + }); + return kept; + }; +} + +function extractTransactions(raw) { + const regexps = { + "issuers": constants.TRANSACTION.SENDER, + "inputs": constants.TRANSACTION.SOURCE_V3, + "unlocks": constants.TRANSACTION.UNLOCK, + "outputs": constants.TRANSACTION.TARGET, + "comments": constants.TRANSACTION.INLINE_COMMENT, + "signatures": constants.SIG + }; + const transactions = []; + const lines = raw.split(/\n/); + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + // On each header + if (line.match(constants.TRANSACTION.HEADER)) { + // Parse the transaction + const currentTX = { raw: line + '\n' }; + const sp = line.split(':'); + const version = parseInt(sp[1]); + const nbIssuers = parseInt(sp[2]); + const nbInputs = parseInt(sp[3]); + const nbUnlocks = parseInt(sp[4]); + const nbOutputs = parseInt(sp[5]); + const hasComment = parseInt(sp[6]); + const start = 2; + currentTX.version = version; + currentTX.blockstamp = lines[i + 1]; + currentTX.raw += currentTX.blockstamp + '\n'; + currentTX.locktime = parseInt(sp[7]); + const linesToExtract = { + issuers: { + start: start, + end: (start - 1) + nbIssuers + }, + inputs: { + start: start + nbIssuers, + end: (start - 1) + nbIssuers + nbInputs + }, + unlocks: { + start: start + nbIssuers + nbInputs, + end: (start - 1) + nbIssuers + nbInputs + nbUnlocks + }, + outputs: { + start: start + nbIssuers + nbInputs + nbUnlocks, + end: (start - 1) + nbIssuers + nbInputs + nbUnlocks + nbOutputs + }, + comments: { + start: start + nbIssuers + nbInputs + nbUnlocks + nbOutputs, + end: (start - 1) + nbIssuers + nbInputs + nbUnlocks + nbOutputs + hasComment + }, + signatures: { + start: start + nbIssuers + nbInputs + nbUnlocks + nbOutputs + hasComment, + end: (start - 1) + 2 * nbIssuers + nbInputs + nbUnlocks + nbOutputs + hasComment + } + }; + ['issuers', 'inputs', 'unlocks', 'outputs', 'comments', 'signatures'].forEach((prop) => { + currentTX[prop] = currentTX[prop] || []; + for (let j = linesToExtract[prop].start; j <= linesToExtract[prop].end; j++) { + const line = lines[i + j]; + if (line.match(regexps[prop])) { + currentTX.raw += line + '\n'; + currentTX[prop].push(line); + } + } + }); + // Comment + if (hasComment) { + currentTX.comment = currentTX.comments[0]; + } else { + currentTX.comment = ''; + } + currentTX.hash = hashf(rawer.getTransaction(currentTX)).toUpperCase(); + // Add to txs array + transactions.push(currentTX); + i = i + 1 + 2 * nbIssuers + nbInputs + nbUnlocks + nbOutputs + hasComment; + } else { + // Not a transaction header, stop reading + i = lines.length; + } + } + return transactions; +} + +util.inherits(BlockParser, GenericParser); diff --git a/app/common/lib/parsers/certification.js b/app/common/lib/parsers/certification.js new file mode 100644 index 000000000..11d0321f5 --- /dev/null +++ b/app/common/lib/parsers/certification.js @@ -0,0 +1,43 @@ +"use strict"; +const GenericParser = require('./GenericParser'); +const util = require('util'); +const rawer = require('../../../../app/common/lib/rawer'); +const constants = require('../../../../app/common/lib/constants'); + +module.exports = CertificationParser; + +function CertificationParser (onError) { + + const captures = [ + {prop: "version", regexp: constants.DOCUMENTS.DOC_VERSION }, + {prop: "type", regexp: constants.CERTIFICATION.CERT_TYPE }, + {prop: "currency", regexp: constants.DOCUMENTS.DOC_CURRENCY }, + {prop: "issuer", regexp: constants.DOCUMENTS.DOC_ISSUER }, + {prop: "idty_issuer", regexp: constants.CERTIFICATION.IDTY_ISSUER }, + {prop: "idty_sig", regexp: constants.CERTIFICATION.IDTY_SIG }, + {prop: "idty_buid", regexp: constants.CERTIFICATION.IDTY_TIMESTAMP}, + {prop: "idty_uid", regexp: constants.CERTIFICATION.IDTY_UID }, + {prop: "buid", regexp: constants.CERTIFICATION.CERT_TIMESTAMP } + ]; + const multilineFields = []; + GenericParser.call(this, captures, multilineFields, rawer.getOfficialCertification, onError); + + this._clean = (obj) => { + obj.documentType = 'certification'; + obj.sig = obj.signature; + obj.block = obj.buid; + if (obj.block) { + obj.number = obj.block.split('-')[0]; + obj.fpr = obj.block.split('-')[1]; + } else { + obj.number = '0'; + obj.fpr = ''; + } + }; + + this._verify = (obj) => ["version", "type", "currency", "issuer", "idty_issuer", "idty_sig", "idty_buid", "idty_uid", "block"].reduce(function (p, field) { + return p || (!obj[field] && "Wrong format for certification"); + }, null); +} + +util.inherits(CertificationParser, GenericParser); diff --git a/app/common/lib/parsers/identity.js b/app/common/lib/parsers/identity.js new file mode 100644 index 000000000..4f0c41ff6 --- /dev/null +++ b/app/common/lib/parsers/identity.js @@ -0,0 +1,47 @@ +"use strict"; +const GenericParser = require('./GenericParser'); +const util = require('util'); +const rawer = require('../../../../app/common/lib/rawer'); +const hashf = require('../../../../app/common/lib/hashf'); +const constants = require('../../../../app/common/lib/constants'); + +module.exports = IdentityParser; + +function IdentityParser (onError) { + + const captures = [ + {prop: "version", regexp: constants.DOCUMENTS.DOC_VERSION }, + {prop: "type", regexp: constants.IDENTITY.IDTY_TYPE}, + {prop: "currency", regexp: constants.DOCUMENTS.DOC_CURRENCY }, + {prop: "pubkey", regexp: constants.DOCUMENTS.DOC_ISSUER }, + {prop: "uid", regexp: constants.IDENTITY.IDTY_UID }, + {prop: "buid", regexp: constants.DOCUMENTS.TIMESTAMP } + ]; + const multilineFields = []; + GenericParser.call(this, captures, multilineFields, rawer.getOfficialIdentity, onError); + + this._clean = (obj) => { + obj.documentType = 'identity'; + obj.sig = obj.signature; + if (obj.uid && obj.buid && obj.pubkey) { + obj.hash = hashf(obj.uid + obj.buid + obj.pubkey).toUpperCase(); + } + }; + + this._verify = (obj) => { + if (!obj.pubkey) { + return "No pubkey found"; + } + if (!obj.uid) { + return "Wrong user id format"; + } + if (!obj.buid) { + return "Could not extract block uid"; + } + if (!obj.sig) { + return "No signature found for self-certification"; + } + }; +} + +util.inherits(IdentityParser, GenericParser); diff --git a/app/common/lib/parsers/index.js b/app/common/lib/parsers/index.js new file mode 100644 index 000000000..7eec548c6 --- /dev/null +++ b/app/common/lib/parsers/index.js @@ -0,0 +1,11 @@ +"use strict"; + +module.exports = { + parseIdentity: (new (require('./identity'))), + parseCertification:(new (require('./certification'))), + parseRevocation: (new (require('./revocation'))), + parseTransaction: (new (require('./transaction'))), + parsePeer: (new (require('./peer'))), + parseMembership: (new (require('./membership'))), + parseBlock: (new (require('./block'))) +}; diff --git a/app/common/lib/parsers/membership.js b/app/common/lib/parsers/membership.js new file mode 100644 index 000000000..2435ae526 --- /dev/null +++ b/app/common/lib/parsers/membership.js @@ -0,0 +1,75 @@ +"use strict"; +const GenericParser = require('./GenericParser'); +const ucp = require('../buid'); +const rawer = require('../rawer'); +const util = require('util'); +const constants = require('../constants'); + +module.exports = MembershipParser; + +function MembershipParser (onError) { + + const captures = [ + {prop: "version", regexp: constants.MEMBERSHIP.VERSION }, + {prop: "currency", regexp: constants.MEMBERSHIP.CURRENCY }, + {prop: "issuer", regexp: constants.MEMBERSHIP.ISSUER }, + {prop: "membership", regexp: constants.MEMBERSHIP.MEMBERSHIP }, + {prop: "userid", regexp: constants.MEMBERSHIP.USERID }, + {prop: "block", regexp: constants.MEMBERSHIP.BLOCK}, + {prop: "certts", regexp: constants.MEMBERSHIP.CERTTS} + ]; + const multilineFields = []; + GenericParser.call(this, captures, multilineFields, rawer.getMembership, onError); + + this._clean = (obj) => { + obj.documentType = 'membership'; + if (obj.block) { + obj.number = obj.block.split('-')[0]; + obj.fpr = obj.block.split('-')[1]; + } else { + obj.number = '0'; + obj.fpr = ''; + } + }; + + this._verify = (obj) => { + let err = null; + const codes = { + 'BAD_VERSION': 150, + 'BAD_CURRENCY': 151, + 'BAD_ISSUER': 152, + 'BAD_MEMBERSHIP': 153, + 'BAD_REGISTRY_TYPE': 154, + 'BAD_BLOCK': 155, + 'BAD_USERID': 156, + 'BAD_CERTTS': 157 + }; + if(!err){ + if(!obj.version || !obj.version.match(constants.DOCUMENTS_VERSION_REGEXP)) + err = {code: codes.BAD_VERSION, message: "Version unknown"}; + } + if(!err){ + if(obj.issuer && !obj.issuer.match(constants.BASE58)) + err = {code: codes.BAD_ISSUER, message: "Incorrect issuer field"}; + } + if(!err){ + if(!(obj.membership || "").match(/^(IN|OUT)$/)) + err = {code: codes.BAD_MEMBERSHIP, message: "Incorrect Membership field: must be either IN or OUT"}; + } + if(!err){ + if(obj.block && !obj.block.match(constants.BLOCK_UID)) + err = {code: codes.BAD_BLOCK, message: "Incorrect Block field: must be a positive or zero integer, a dash and an uppercased SHA1 hash"}; + } + if(!err){ + if(obj.userid && !obj.userid.match(constants.USER_ID)) + err = {code: codes.BAD_USERID, message: "UserID must match udid2 format"}; + } + if(!err){ + if(!ucp.format.isBuid(obj.certts)) + err = {code: codes.BAD_CERTTS, message: "CertTS must be a valid timestamp"}; + } + return err && err.message; + }; +} + +util.inherits(MembershipParser, GenericParser); diff --git a/app/common/lib/parsers/peer.js b/app/common/lib/parsers/peer.js new file mode 100644 index 000000000..122f6ee34 --- /dev/null +++ b/app/common/lib/parsers/peer.js @@ -0,0 +1,105 @@ +"use strict"; +const GenericParser = require('./GenericParser'); +const rawer = require('../rawer'); +const util = require('util'); +const constants = require('../constants'); + +module.exports = PeerParser; + +function PeerParser (onError) { + + const captures = [ + {prop: "version", regexp: /Version: (.*)/}, + {prop: "currency", regexp: /Currency: (.*)/}, + {prop: "pubkey", regexp: /PublicKey: (.*)/}, + {prop: "block", regexp: constants.PEER.BLOCK}, + { + prop: "endpoints", regexp: /Endpoints:\n([\s\S]*)/, parser: (str) => str.split("\n") + } + ]; + const multilineFields = []; + GenericParser.call(this, captures, multilineFields, rawer.getPeer, onError); + + this._clean = (obj) => { + obj.documentType = 'peer'; + obj.endpoints = obj.endpoints || []; + // Removes trailing space + if (obj.endpoints.length > 0) + obj.endpoints.splice(obj.endpoints.length - 1, 1); + obj.getBMA = function() { + let bma = null; + obj.endpoints.forEach((ep) => { + let matches = !bma && ep.match(constants.BMA_REGEXP); + if (matches) { + bma = { + "dns": matches[2] || '', + "ipv4": matches[4] || '', + "ipv6": matches[6] || '', + "port": matches[8] || 9101 + }; + } + }); + return bma || {}; + }; + }; + + this._verify = (obj) => { + let err = null; + const codes = { + 'BAD_VERSION': 150, + 'BAD_CURRENCY': 151, + 'BAD_DNS': 152, + 'BAD_IPV4': 153, + 'BAD_IPV6': 154, + 'BAD_PORT': 155, + 'BAD_FINGERPRINT': 156, + 'BAD_BLOCK': 157, + 'NO_IP_GIVEN': 158 + }; + if(!err){ + // Version + if(!obj.version || !obj.version.match(constants.DOCUMENTS_VERSION_REGEXP)) + err = {code: codes.BAD_VERSION, message: "Version unknown"}; + } + if(!err){ + // PublicKey + if(!obj.pubkey || !obj.pubkey.match(constants.BASE58)) + err = {code: codes.BAD_FINGERPRINT, message: "Incorrect PublicKey field"}; + } + if(!err){ + // Block + if(!obj.block) + err = {code: codes.BAD_BLOCK, message: "Incorrect Block field"}; + } + // Basic Merkled API requirements + let bma = obj.getBMA(); + if(!err){ + // DNS + if(bma.dns && !bma.dns.match(/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/)) + err = {code: codes.BAD_DNS, message: "Incorrect Dns field"}; + } + if(!err){ + // IPv4 + if(bma.ipv4 && !bma.ipv4.match(/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/)) + err = {code: codes.BAD_IPV4, message: "Incorrect IPv4 field"}; + } + if(!err){ + // IPv6 + if(bma.ipv6 && !bma.ipv6.match(/^((([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}:))$/)) + err = {code: codes.BAD_IPV6, message: "Incorrect IPv6 field"}; + } + if(!err){ + // IP + if(!bma.dns && !bma.ipv4 && !bma.ipv6) + err = {code: codes.NO_IP_GIVEN, message: "It must be given at least DNS or one IP, either v4 or v6"}; + } + if(!err){ + // Port + if(bma.port && !(bma.port + "").match(/^\d+$/)) + err = {code: codes.BAD_PORT, message: "Port must be provided and match an integer format"}; + } + return err && err.message; + }; +} + +util.inherits(PeerParser, GenericParser); diff --git a/app/common/lib/parsers/revocation.js b/app/common/lib/parsers/revocation.js new file mode 100644 index 000000000..f86528bde --- /dev/null +++ b/app/common/lib/parsers/revocation.js @@ -0,0 +1,52 @@ +"use strict"; +const GenericParser = require('./GenericParser'); +const util = require('util'); +const rawer = require('../rawer'); +const hashf = require('../hashf'); +const constants = require('../constants'); + +module.exports = RevocationParser; + +function RevocationParser (onError) { + + const captures = [ + {prop: "version", regexp: constants.DOCUMENTS.DOC_VERSION }, + {prop: "type", regexp: constants.REVOCATION.REVOC_TYPE }, + {prop: "currency", regexp: constants.DOCUMENTS.DOC_CURRENCY }, + {prop: "issuer", regexp: constants.DOCUMENTS.DOC_ISSUER }, + {prop: "sig", regexp: constants.REVOCATION.IDTY_SIG }, + {prop: "buid", regexp: constants.REVOCATION.IDTY_TIMESTAMP}, + {prop: "uid", regexp: constants.REVOCATION.IDTY_UID } + ]; + const multilineFields = []; + GenericParser.call(this, captures, multilineFields, rawer.getOfficialRevocation, onError); + + this._clean = (obj) => { + obj.documentType = 'revocation'; + obj.pubkey = obj.issuer; + obj.revocation = obj.signature; + if (obj.uid && obj.buid && obj.pubkey) { + obj.hash = hashf(obj.uid + obj.buid + obj.pubkey).toUpperCase(); + } + }; + + this._verify = (obj) => { + if (!obj.pubkey) { + return "No pubkey found"; + } + if (!obj.uid) { + return "Wrong user id format"; + } + if (!obj.buid) { + return "Could not extract block uid"; + } + if (!obj.sig) { + return "No signature found for identity"; + } + if (!obj.revocation) { + return "No revocation signature found"; + } + }; +} + +util.inherits(RevocationParser, GenericParser); diff --git a/app/common/lib/parsers/transaction.js b/app/common/lib/parsers/transaction.js new file mode 100644 index 000000000..727607e19 --- /dev/null +++ b/app/common/lib/parsers/transaction.js @@ -0,0 +1,128 @@ +"use strict"; +const GenericParser = require('./GenericParser'); +const rawer = require('../rawer'); +const constants = require('../constants'); +const util = require('util'); + +module.exports = TransactionParser; + +function TransactionParser (onError) { + + const captures = [ + {prop: "version", regexp: /Version: (.*)/}, + {prop: "currency", regexp: /Currency: (.*)/}, + {prop: "issuers", regexp: /Issuers:\n([\s\S]*)Inputs/, parser: extractIssuers }, + {prop: "inputs", regexp: /Inputs:\n([\s\S]*)Unlocks/, parser: extractInputs }, + {prop: "unlocks", regexp: /Unlocks:\n([\s\S]*)Outputs/,parser: extractUnlocks }, + {prop: "outputs", regexp: /Outputs:\n([\s\S]*)/, parser: extractOutputs }, + {prop: "comment", regexp: constants.TRANSACTION.COMMENT }, + {prop: "locktime", regexp: constants.TRANSACTION.LOCKTIME }, + {prop: "blockstamp", regexp: constants.TRANSACTION.BLOCKSTAMP }, + {prop: "signatures", regexp: /Outputs:\n([\s\S]*)/, parser: extractSignatures } + ]; + const multilineFields = []; + GenericParser.call(this, captures, multilineFields, rawer.getTransaction, onError); + + this._clean = (obj) => { + obj.documentType = 'transaction'; + obj.comment = obj.comment || ""; + obj.locktime = parseInt(obj.locktime) || 0; + obj.signatures.push(obj.signature); + const compactSize = 2 // Header + blockstamp + + obj.issuers.length + + obj.inputs.length + + obj.unlocks.length + + obj.outputs.length + + (obj.comment ? 1 : 0) + + obj.signatures; + if (compactSize > 100) { + throw 'A transaction has a maximum size of 100 lines'; + } + }; + + this._verify = (obj) => { + let err = null; + const codes = { + 'BAD_VERSION': 150, + 'NO_BLOCKSTAMP': 151 + }; + if(!err){ + // Version + if(!obj.version || !obj.version.match(constants.DOCUMENTS_TRANSACTION_VERSION_REGEXP)) + err = {code: codes.BAD_VERSION, message: "Version unknown"}; + // Blockstamp + if(!obj.blockstamp || !obj.blockstamp.match(constants.BLOCKSTAMP_REGEXP)) + err = {code: codes.BAD_VERSION, message: "Blockstamp is required"}; + } + return err && err.message; + }; +} + +function extractIssuers(raw) { + const issuers = []; + const lines = raw.split(/\n/); + for (const line of lines) { + if (line.match(constants.TRANSACTION.SENDER)) { + issuers.push(line); + } else { + // Not a pubkey, stop reading + break; + } + } + return issuers; +} + +function extractInputs(raw) { + const inputs = []; + const lines = raw.split(/\n/); + for (const line of lines) { + if (line.match(constants.TRANSACTION.SOURCE_V3)) { + inputs.push(line); + } else { + // Not a transaction input, stop reading + break; + } + } + return inputs; +} + +function extractUnlocks(raw) { + const unlocks = []; + const lines = raw.split(/\n/); + for (const line of lines) { + if (line.match(constants.TRANSACTION.UNLOCK)) { + unlocks.push(line); + } else { + // Not a transaction unlock, stop reading + break; + } + } + return unlocks; +} + +function extractOutputs(raw) { + const outputs = []; + const lines = raw.split(/\n/); + for (const line of lines) { + if (line.match(constants.TRANSACTION.TARGET)) { + outputs.push(line); + } else { + // Not a transaction input, stop reading + break; + } + } + return outputs; +} + +function extractSignatures(raw) { + const signatures = []; + const lines = raw.split(/\n/); + for (const line of lines) { + if (line.match(constants.SIG)) { + signatures.push(line); + } + } + return signatures; +} + +util.inherits(TransactionParser, GenericParser); diff --git a/app/common/lib/rawer.js b/app/common/lib/rawer.js new file mode 100644 index 000000000..a99aa2a11 --- /dev/null +++ b/app/common/lib/rawer.js @@ -0,0 +1,92 @@ +"use strict"; +const dos2unix = require('./dos2unix'); +const document = require('./document'); + +const DOCUMENTS_VERSION = 10; +const SIGNED = false +const UNSIGNED = true + +module.exports = new function() { + + this.getOfficialIdentity = (json, withSig) => { + return document.Identity.toRAW(json, withSig !== false) // Defaut with sig + }; + + this.getOfficialCertification = (json) => { + let raw = getNormalHeader('Certification', json); + raw += "IdtyIssuer: " + json.idty_issuer + '\n'; + raw += "IdtyUniqueID: " + json.idty_uid + '\n'; + raw += "IdtyTimestamp: " + json.idty_buid + '\n'; + raw += "IdtySignature: " + json.idty_sig + '\n'; + raw += "CertTimestamp: " + json.buid + '\n'; + if (json.sig) { + raw += json.sig + '\n'; + } + return dos2unix(raw); + }; + + this.getOfficialRevocation = (json) => { + let raw = getNormalHeader('Revocation', json); + raw += "IdtyUniqueID: " + json.uid + '\n'; + raw += "IdtyTimestamp: " + json.buid + '\n'; + raw += "IdtySignature: " + json.sig + '\n'; + if (json.revocation) { + raw += json.revocation + '\n'; + } + return dos2unix(raw); + }; + + this.getPeerWithoutSignature = (json) => document.Peer.fromJSON(json).getRawUnsigned() + + this.getPeer = (json) => document.Peer.fromJSON(json).getRaw() + + this.getMembershipWithoutSignature = (json) => { + return document.Membership.toRAW(json) + }; + + this.getMembership = (json) => { + return dos2unix(signed(this.getMembershipWithoutSignature(json), json)); + }; + + this.getBlockInnerPart = (json) => { + return document.Block.toRAWInnerPart(json) + }; + + this.getBlockWithInnerHashAndNonce = (json) => { + return document.Block.toRAWinnerPartWithHashAndNonce(json) + }; + + this.getBlockInnerHashAndNonce = (json) => { + return document.Block.toRAWHashAndNonce(json, UNSIGNED) + }; + + this.getBlockInnerHashAndNonceWithSignature = (json) => { + return document.Block.toRAWHashAndNonce(json, SIGNED) + }; + + this.getBlock = (json) => { + return dos2unix(signed(this.getBlockWithInnerHashAndNonce(json), json)); + }; + + this.getTransaction = (json) => { + return document.Transaction.toRAW(json) + }; + + this.getCompactTransaction = (json) => { + return document.Transaction.getCompactTransaction(json) + }; + + let getNormalHeader = (doctype, json) => { + let raw = ""; + raw += "Version: " + (json.version || DOCUMENTS_VERSION) + "\n"; + raw += "Type: " + doctype + "\n"; + raw += "Currency: " + json.currency + "\n"; + raw += "Issuer: " + json.issuer + "\n"; + return raw; + }; + + let signed = (raw, json) => { + raw += json.signature + '\n'; + return raw; + }; +}; diff --git a/app/common/lib/regex.js b/app/common/lib/regex.js new file mode 100644 index 000000000..a0ae4bdb5 --- /dev/null +++ b/app/common/lib/regex.js @@ -0,0 +1,5 @@ +"use strict"; + +module.exports = { + BMA_REGEXP: /^BASIC_MERKLED_API( ([a-z_][a-z0-9-_.]*))?( ([0-9.]+))?( ([0-9a-f:]+))?( ([0-9]+))$/ +} \ No newline at end of file diff --git a/app/common/lib/txunlock.js b/app/common/lib/txunlock.js new file mode 100644 index 000000000..e423355f0 --- /dev/null +++ b/app/common/lib/txunlock.js @@ -0,0 +1,73 @@ +"use strict"; + +let Parser = require("jison").Parser; +let buid = require('./buid'); + +let grammar = { + "lex": { + "rules": [ + ["\\s+", "/* skip whitespace */"], + ["\\&\\&", "return 'AND';"], + ["\\|\\|", "return 'OR';"], + ["\\(", "return '(';"], + ["\\)", "return ')';"], + ["[0-9A-Za-z]{40,64}", "return 'PARAMETER';"], + ["[0-9]{1,10}", "return 'PARAMETER';"], + ["SIG", "return 'SIG';"], + ["XHX", "return 'XHX';"], + ["CLTV", "return 'CLTV';"], + ["CSV", "return 'CSV';"], + ["$", "return 'EOF';"] + ] + }, + + "operators": [ + ["left", "AND", "OR"] + ], + + "bnf": { + "expressions" :[ + [ "e EOF", "return $1;" ] + ], + + "e" :[ + [ "e AND e", "$$ = $1 && $3;" ], + [ "e OR e", "$$ = $1 || $3;" ], + [ "SIG ( e )","$$ = yy.sig($3);"], + [ "XHX ( e )","$$ = yy.xHx($3);"], + [ "CLTV ( e )","$$ = yy.cltv($3);"], + [ "CSV ( e )","$$ = yy.csv($3);"], + [ "PARAMETER", "$$ = $1;" ], + [ "( e )", "$$ = $2;" ] + ] + } +}; + +module.exports = function unlock(conditionsStr, executions, metadata) { + + let parser = new Parser(grammar); + + parser.yy = { + i: 0, + sig: function (pubkey) { + let sigParam = executions[this.i++]; + return (sigParam && pubkey === sigParam.pubkey && sigParam.sigOK) || false; + }, + xHx: function(hash) { + let xhxParam = executions[this.i++]; + return buid.format.hashf(xhxParam) === hash; + }, + cltv: function(deadline) { + return metadata.currentTime && metadata.currentTime >= parseInt(deadline); + }, + csv: function(amountToWait) { + return metadata.elapsedTime && metadata.elapsedTime >= parseInt(amountToWait); + } + }; + + try { + return parser.parse(conditionsStr); + } catch(e) { + return false; + } +}; diff --git a/app/lib/blockchain/DuniterBlockchain.ts b/app/lib/blockchain/DuniterBlockchain.ts index 5f2739205..6ea0513af 100644 --- a/app/lib/blockchain/DuniterBlockchain.ts +++ b/app/lib/blockchain/DuniterBlockchain.ts @@ -13,7 +13,7 @@ import {MembershipDTO} from "../dto/MembershipDTO" import {TransactionDTO} from "../dto/TransactionDTO" const _ = require('underscore') -const common = require('duniter-common') +const common = require('../../../app/common') export class DuniterBlockchain extends MiscIndexedBlockchain { diff --git a/app/lib/common/crypto/base58.ts b/app/lib/common-libs/crypto/base58.ts similarity index 100% rename from app/lib/common/crypto/base58.ts rename to app/lib/common-libs/crypto/base58.ts diff --git a/app/lib/common/crypto/keyring.ts b/app/lib/common-libs/crypto/keyring.ts similarity index 100% rename from app/lib/common/crypto/keyring.ts rename to app/lib/common-libs/crypto/keyring.ts diff --git a/app/lib/common/crypto/nacl-util.ts b/app/lib/common-libs/crypto/nacl-util.ts similarity index 100% rename from app/lib/common/crypto/nacl-util.ts rename to app/lib/common-libs/crypto/nacl-util.ts diff --git a/app/lib/common.ts b/app/lib/common.ts index a16cf1bdc..edf0ef092 100644 --- a/app/lib/common.ts +++ b/app/lib/common.ts @@ -1,4 +1,4 @@ -const common = require('duniter-common') +const common = require('../../app/common') export function hashf(str:string) { return common.hashf(str).toUpperCase() diff --git a/app/lib/constants.ts b/app/lib/constants.ts index 92fef4c10..a410214f9 100644 --- a/app/lib/constants.ts +++ b/app/lib/constants.ts @@ -1,6 +1,6 @@ "use strict"; -const common = require('duniter-common') +const common = require('../../app/common') const UDID2 = "udid2;c;([A-Z-]*);([A-Z-]*);(\\d{4}-\\d{2}-\\d{2});(e\\+\\d{2}\\.\\d{2}(\\+|-)\\d{3}\\.\\d{2});(\\d+)(;?)"; const PUBKEY = common.constants.FORMATS.PUBKEY diff --git a/app/lib/dal/fileDAL.ts b/app/lib/dal/fileDAL.ts index ab9e576f5..f02c58a8d 100644 --- a/app/lib/dal/fileDAL.ts +++ b/app/lib/dal/fileDAL.ts @@ -19,7 +19,7 @@ const fs = require('fs') const path = require('path') const readline = require('readline') const _ = require('underscore'); -const common = require('duniter-common'); +const common = require('../../../app/common'); const indexer = require('../indexer').Indexer const logger = require('../logger').NewLogger('filedal'); const constants = require('../constants'); diff --git a/app/lib/dal/sqliteDAL/MetaDAL.ts b/app/lib/dal/sqliteDAL/MetaDAL.ts index 52047f2f6..8dccc0eef 100644 --- a/app/lib/dal/sqliteDAL/MetaDAL.ts +++ b/app/lib/dal/sqliteDAL/MetaDAL.ts @@ -14,8 +14,8 @@ import {IdentityDTO} from "../../dto/IdentityDTO" const _ = require('underscore') const logger = require('../../logger').NewLogger('metaDAL'); -const common = require('duniter-common'); -const rawer = require('duniter-common').rawer; +const common = require('../../../../app/common'); +const rawer = require('../../../../app/common').rawer; const constants = require('./../../constants'); export interface DBMeta { diff --git a/app/lib/dal/sqliteDAL/index/CIndexDAL.ts b/app/lib/dal/sqliteDAL/index/CIndexDAL.ts index 60c0bf938..8cea74334 100644 --- a/app/lib/dal/sqliteDAL/index/CIndexDAL.ts +++ b/app/lib/dal/sqliteDAL/index/CIndexDAL.ts @@ -3,7 +3,7 @@ import {SQLiteDriver} from "../../drivers/SQLiteDriver"; import {CindexEntry} from "../../../indexer"; const constants = require('./../../../constants'); -const common = require('duniter-common'); +const common = require('../../../../../app/common'); const indexer = require('../../../indexer').Indexer export class CIndexDAL extends AbstractIndex<CindexEntry> { diff --git a/app/lib/dal/sqliteDAL/index/SIndexDAL.ts b/app/lib/dal/sqliteDAL/index/SIndexDAL.ts index 13b0c04e3..b1d97503b 100644 --- a/app/lib/dal/sqliteDAL/index/SIndexDAL.ts +++ b/app/lib/dal/sqliteDAL/index/SIndexDAL.ts @@ -2,7 +2,7 @@ import {Indexer, SindexEntry} from "../../../indexer"; import {SQLiteDriver} from "../../drivers/SQLiteDriver"; import {AbstractIndex} from "../AbstractIndex"; const _ = require('underscore'); -const common = require('duniter-common'); +const common = require('../../../../../app/common'); const constants = require('../../../constants'); export class SIndexDAL extends AbstractIndex<SindexEntry> { diff --git a/app/lib/indexer.ts b/app/lib/indexer.ts index dad52458f..cb2f4b22e 100644 --- a/app/lib/indexer.ts +++ b/app/lib/indexer.ts @@ -7,11 +7,11 @@ import {CertificationDTO} from "./dto/CertificationDTO" import {TransactionDTO} from "./dto/TransactionDTO" import {DBHead} from "./db/DBHead" import {LOCAL_RULES_HELPERS} from "./rules/local_rules" -import {verify} from "./common/crypto/keyring" +import {verify} from "./common-libs/crypto/keyring"; const co = require('co'); const _ = require('underscore'); -const common = require('duniter-common'); +const common = require('../../app/common'); const constants = common.constants const rawer = common.rawer diff --git a/app/lib/rules/global_rules.ts b/app/lib/rules/global_rules.ts index 4a6acb1b5..ca4eff2c4 100644 --- a/app/lib/rules/global_rules.ts +++ b/app/lib/rules/global_rules.ts @@ -5,10 +5,10 @@ import {DBBlock} from "../db/DBBlock" import {TransactionDTO} from "../dto/TransactionDTO" import * as local_rules from "./local_rules" import {BlockDTO} from "../dto/BlockDTO" -import {verify} from "../common/crypto/keyring" +import {verify} from "../common-libs/crypto/keyring" const _ = require('underscore'); -const common = require('duniter-common'); +const common = require('../../../app/common'); const indexer = require('../indexer').Indexer const constants = common.constants diff --git a/app/lib/rules/helpers.ts b/app/lib/rules/helpers.ts index 0bb0dbea5..7203dc22b 100644 --- a/app/lib/rules/helpers.ts +++ b/app/lib/rules/helpers.ts @@ -1,6 +1,6 @@ import {ConfDTO} from "../dto/ConfDTO" -const common = require('duniter-common'); +const common = require('../../../app/common'); const constants = common.constants export function maxAcceleration (conf:ConfDTO) { diff --git a/app/lib/rules/index.ts b/app/lib/rules/index.ts index e3cc2d4c6..950fc226a 100644 --- a/app/lib/rules/index.ts +++ b/app/lib/rules/index.ts @@ -4,7 +4,7 @@ import {ConfDTO} from "../dto/ConfDTO" import {IndexEntry} from "../indexer" import {LOCAL_RULES_FUNCTIONS} from "./local_rules" -const common = require('duniter-common'); +const common = require('../../../app/common'); const Block = common.document.Block export const ALIAS = { diff --git a/app/lib/rules/local_rules.ts b/app/lib/rules/local_rules.ts index 1bf8139c8..b67f44b6f 100644 --- a/app/lib/rules/local_rules.ts +++ b/app/lib/rules/local_rules.ts @@ -4,10 +4,10 @@ import {ConfDTO} from "../dto/ConfDTO" import {CindexEntry, IndexEntry, Indexer, MindexEntry, SindexEntry} from "../indexer" import {BaseDTO, TransactionDTO} from "../dto/TransactionDTO" import {DBBlock} from "../db/DBBlock" -import {verify} from "../common/crypto/keyring" +import {verify} from "../common-libs/crypto/keyring" const _ = require('underscore'); -const common = require('duniter-common'); +const common = require('../../../app/common'); const constants = common.constants const hashf = common.hashf diff --git a/app/modules/bma/lib/controllers/blockchain.ts b/app/modules/bma/lib/controllers/blockchain.ts index 4195fabb4..39ae637cc 100644 --- a/app/modules/bma/lib/controllers/blockchain.ts +++ b/app/modules/bma/lib/controllers/blockchain.ts @@ -6,7 +6,7 @@ import {BMAConstants} from "../constants" const co = require('co'); const _ = require('underscore'); -const common = require('duniter-common'); +const common = require('../../../../../app/common'); const http2raw = require('../http2raw'); const toJson = require('../tojson'); diff --git a/app/modules/bma/lib/controllers/transactions.ts b/app/modules/bma/lib/controllers/transactions.ts index 7b889682f..7d7a2de29 100644 --- a/app/modules/bma/lib/controllers/transactions.ts +++ b/app/modules/bma/lib/controllers/transactions.ts @@ -4,7 +4,7 @@ import {Source} from "../entity/source" import {BMAConstants} from "../constants"; const _ = require('underscore'); -const common = require('duniter-common'); +const common = require('../../../../../app/common'); const http2raw = require('../http2raw'); const Transaction = common.document.Transaction diff --git a/app/modules/bma/lib/controllers/wot.ts b/app/modules/bma/lib/controllers/wot.ts index 7f21cfe98..1fb49b5ab 100644 --- a/app/modules/bma/lib/controllers/wot.ts +++ b/app/modules/bma/lib/controllers/wot.ts @@ -2,7 +2,7 @@ import {AbstractController} from "./AbstractController" import {BMAConstants} from "../constants" const _ = require('underscore'); -const common = require('duniter-common'); +const common = require('../../../../../app/common'); const http2raw = require('../http2raw'); const Identity = common.document.Identity diff --git a/app/modules/crawler/index.ts b/app/modules/crawler/index.ts index aa0a078fd..3a777527c 100644 --- a/app/modules/crawler/index.ts +++ b/app/modules/crawler/index.ts @@ -6,7 +6,7 @@ import {Synchroniser} from "./lib/sync" import {req2fwd} from "./lib/req2fwd" import {CrawlerConstants} from "./lib/constants" -const common = require('duniter-common'); +const common = require('../../../app/common'); const Peer = common.document.Peer export const CrawlerDependency = { diff --git a/app/modules/crawler/lib/constants.ts b/app/modules/crawler/lib/constants.ts index 32c976546..a3ce7dd4d 100644 --- a/app/modules/crawler/lib/constants.ts +++ b/app/modules/crawler/lib/constants.ts @@ -1,4 +1,4 @@ -const common = require('duniter-common') +const common = require('../../../../app/common') export const CrawlerConstants = { diff --git a/app/modules/crawler/lib/crawler.ts b/app/modules/crawler/lib/crawler.ts index 4af7079cb..c739ea047 100644 --- a/app/modules/crawler/lib/crawler.ts +++ b/app/modules/crawler/lib/crawler.ts @@ -15,7 +15,7 @@ import { cleanLongDownPeers } from "./garbager"; const _ = require('underscore'); const async = require('async'); const querablep = require('querablep'); -const Peer = require('duniter-common').document.Peer; +const Peer = require('../../../../app/common').document.Peer; /** * Service which triggers the server's peering generation (actualization of the Peer document). diff --git a/app/modules/crawler/lib/req2fwd.ts b/app/modules/crawler/lib/req2fwd.ts index 8592e56f2..bccb576bf 100644 --- a/app/modules/crawler/lib/req2fwd.ts +++ b/app/modules/crawler/lib/req2fwd.ts @@ -1,6 +1,6 @@ import {Contacter} from "./contacter" -const common = require('duniter-common') +const common = require('../../../../app/common') export const req2fwd = async (requirements:any, toHost:string, toPort:number, logger:any) => { const mss:any = {}; diff --git a/app/modules/crawler/lib/sandbox.ts b/app/modules/crawler/lib/sandbox.ts index 430edc921..af03705e1 100644 --- a/app/modules/crawler/lib/sandbox.ts +++ b/app/modules/crawler/lib/sandbox.ts @@ -2,8 +2,8 @@ import {Contacter} from "./contacter" import {Server} from "../../../../server" -const rawer = require('duniter-common').rawer; -const parsers = require('duniter-common').parsers; +const rawer = require('../../../../app/common').rawer; +const parsers = require('../../../../app/common').parsers; export const pullSandbox = async (currency:string, fromHost:string, fromPort:number, toHost:string, toPort:number, logger:any) => { const from = new Contacter(fromHost, fromPort); diff --git a/app/modules/crawler/lib/sync.ts b/app/modules/crawler/lib/sync.ts index 1c5b21a33..95981ede9 100644 --- a/app/modules/crawler/lib/sync.ts +++ b/app/modules/crawler/lib/sync.ts @@ -17,7 +17,7 @@ const _ = require('underscore'); const moment = require('moment'); const multimeter = require('multimeter'); const makeQuerablePromise = require('querablep'); -const common = require('duniter-common'); +const common = require('../../../../app/common'); const Peer = common.document.Peer; const dos2unix = common.dos2unix; const hashf = common.hashf; diff --git a/app/modules/keypair/index.ts b/app/modules/keypair/index.ts index 39e5d42a6..7bfcceae0 100644 --- a/app/modules/keypair/index.ts +++ b/app/modules/keypair/index.ts @@ -1,4 +1,4 @@ -import {randomKey} from "../../lib/common/crypto/keyring" +import {randomKey} from "../../lib/common-libs/crypto/keyring" import {ConfDTO, KeypairConfDTO} from "../../lib/dto/ConfDTO" import {Scrypt} from "./lib/scrypt" diff --git a/app/modules/keypair/lib/scrypt.ts b/app/modules/keypair/lib/scrypt.ts index 2e292eaad..7092a400a 100644 --- a/app/modules/keypair/lib/scrypt.ts +++ b/app/modules/keypair/lib/scrypt.ts @@ -1,6 +1,6 @@ "use strict"; -import {Base58encode} from "../../../lib/common/crypto/base58" -import {decodeBase64} from "../../../lib/common/crypto/nacl-util" +import {Base58encode} from "../../../lib/common-libs/crypto/base58" +import {decodeBase64} from "../../../lib/common-libs/crypto/nacl-util" const nacl = require('tweetnacl'); const scrypt = require('scryptb'); diff --git a/app/modules/prover/index.ts b/app/modules/prover/index.ts index 9d343fb1f..e4e12e548 100644 --- a/app/modules/prover/index.ts +++ b/app/modules/prover/index.ts @@ -6,7 +6,7 @@ import {Prover} from "./lib/prover" import {Contacter} from "../crawler/lib/contacter"; const async = require('async'); -const common = require('duniter-common'); +const common = require('../../../app/common'); const Peer = common.document.Peer diff --git a/app/modules/prover/lib/blockGenerator.ts b/app/modules/prover/lib/blockGenerator.ts index d7a3b4032..2854cb6f3 100644 --- a/app/modules/prover/lib/blockGenerator.ts +++ b/app/modules/prover/lib/blockGenerator.ts @@ -7,12 +7,12 @@ import {LOCAL_RULES_HELPERS} from "../../../lib/rules/local_rules" import {Indexer} from "../../../lib/indexer" import {FileDAL} from "../../../lib/dal/fileDAL" import {DBBlock} from "../../../lib/db/DBBlock" -import {verify} from "../../../lib/common/crypto/keyring" +import {verify} from "../../../lib/common-libs/crypto/keyring" const _ = require('underscore'); const moment = require('moment'); const inquirer = require('inquirer'); -const common = require('duniter-common'); +const common = require('../../../../app/common'); const hashf = common.hashf; const rawer = common.rawer; diff --git a/app/modules/prover/lib/blockProver.ts b/app/modules/prover/lib/blockProver.ts index e092b93f7..bb54b6b9d 100644 --- a/app/modules/prover/lib/blockProver.ts +++ b/app/modules/prover/lib/blockProver.ts @@ -4,7 +4,7 @@ import {PowEngine} from "./engine" import {DBBlock} from "../../../lib/db/DBBlock" const querablep = require('querablep'); -const common = require('duniter-common'); +const common = require('../../../../app/common'); const Block = common.document.Block diff --git a/app/modules/prover/lib/permanentProver.ts b/app/modules/prover/lib/permanentProver.ts index 142449125..42ac4442c 100644 --- a/app/modules/prover/lib/permanentProver.ts +++ b/app/modules/prover/lib/permanentProver.ts @@ -5,7 +5,7 @@ import {Constants} from "./constants" import {DBBlock} from "../../../lib/db/DBBlock" const querablep = require('querablep'); -const common = require('duniter-common'); +const common = require('../../../../app/common'); const dos2unix = common.dos2unix; const parsers = common.parsers; diff --git a/app/modules/prover/lib/proof.ts b/app/modules/prover/lib/proof.ts index ccb196ca4..96c597842 100644 --- a/app/modules/prover/lib/proof.ts +++ b/app/modules/prover/lib/proof.ts @@ -3,12 +3,12 @@ import {hashf} from "../../../lib/common" import {DBBlock} from "../../../lib/db/DBBlock" import {ConfDTO} from "../../../lib/dto/ConfDTO" import {Constants} from "./constants" -import {KeyGen} from "../../../lib/common/crypto/keyring" +import {KeyGen} from "../../../lib/common-libs/crypto/keyring" const moment = require('moment'); -const dos2unix = require('duniter-common').dos2unix; +const dos2unix = require('../../../../app/common').dos2unix; const querablep = require('querablep'); -const rawer = require('duniter-common').rawer; +const rawer = require('../../../../app/common').rawer; const PAUSES_PER_TURN = 5; diff --git a/app/service/BlockchainService.ts b/app/service/BlockchainService.ts index b2195897c..760c37de5 100644 --- a/app/service/BlockchainService.ts +++ b/app/service/BlockchainService.ts @@ -11,7 +11,7 @@ import {GLOBAL_RULES_HELPERS} from "../lib/rules/global_rules" const _ = require('underscore'); const co = require('co'); -const parsers = require('duniter-common').parsers; +const parsers = require('../../app/common').parsers; const constants = require('../lib/constants'); const CHECK_ALL_RULES = true; diff --git a/app/service/IdentityService.ts b/app/service/IdentityService.ts index 1bf368b01..b942c2677 100644 --- a/app/service/IdentityService.ts +++ b/app/service/IdentityService.ts @@ -8,7 +8,7 @@ import {RevocationDTO} from "../lib/dto/RevocationDTO" import {BasicIdentity, IdentityDTO} from "../lib/dto/IdentityDTO" import {CertificationDTO} from "../lib/dto/CertificationDTO" import {DBCert} from "../lib/dal/sqliteDAL/CertDAL" -import {verify} from "../lib/common/crypto/keyring" +import {verify} from "../lib/common-libs/crypto/keyring" "use strict"; const constants = require('../lib/constants'); diff --git a/app/service/MembershipService.ts b/app/service/MembershipService.ts index 2693fc818..6c6d83a4f 100644 --- a/app/service/MembershipService.ts +++ b/app/service/MembershipService.ts @@ -6,7 +6,7 @@ import {LOCAL_RULES_HELPERS} from "../lib/rules/local_rules" import {GLOBAL_RULES_HELPERS} from "../lib/rules/global_rules" import {MembershipDTO} from "../lib/dto/MembershipDTO" -const hashf = require('duniter-common').hashf; +const hashf = require('../../app/common').hashf; const constants = require('../lib/constants'); export class MembershipService { diff --git a/app/service/PeeringService.ts b/app/service/PeeringService.ts index 52ed25c99..70d515191 100644 --- a/app/service/PeeringService.ts +++ b/app/service/PeeringService.ts @@ -5,16 +5,16 @@ import {DBPeer} from "../lib/dal/sqliteDAL/PeerDAL" import {DBBlock} from "../lib/db/DBBlock" import {Multicaster} from "../lib/streams/multicaster" import {PeerDTO} from "../lib/dto/PeerDTO" -import {verify} from "../lib/common/crypto/keyring" +import {verify} from "../lib/common-libs/crypto/keyring" const util = require('util'); const _ = require('underscore'); const events = require('events'); const rp = require('request-promise'); const logger = require('../lib/logger').NewLogger('peering'); -const dos2unix = require('duniter-common').dos2unix; -const hashf = require('duniter-common').hashf; -const rawer = require('duniter-common').rawer; +const dos2unix = require('../../app/common').dos2unix; +const hashf = require('../../app/common').hashf; +const rawer = require('../../app/common').rawer; const constants = require('../lib/constants'); export interface Keyring { diff --git a/package.json b/package.json index ad6fec5e3..c286809cf 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,6 @@ "commander": "2.9.0", "daemonize2": "0.4.2", "ddos": "0.1.16", - "duniter-common": "1.4.x", "event-stream": "3.3.4", "errorhandler": "1.5.0", "express": "4.15.2", @@ -65,6 +64,7 @@ "express-fileupload": "0.0.5", "heapdump": "^0.3.9", "inquirer": "3.0.6", + "jison": "0.4.17", "merkle": "0.5.1", "moment": "2.18.1", "morgan": "1.8.1", diff --git a/server.ts b/server.ts index ada29a16b..5756973ac 100644 --- a/server.ts +++ b/server.ts @@ -8,7 +8,7 @@ import {FileDAL} from "./app/lib/dal/fileDAL" import {DuniterBlockchain} from "./app/lib/blockchain/DuniterBlockchain" import {SQLBlockchain} from "./app/lib/blockchain/SqlBlockchain" import * as stream from "stream" -import {KeyGen, randomKey} from "./app/lib/common/crypto/keyring" +import {KeyGen, randomKey} from "./app/lib/common-libs/crypto/keyring" interface HookableServer { getMainEndpoint: (...args:any[]) => Promise<any> @@ -26,11 +26,11 @@ const archiver = require('archiver'); const unzip = require('unzip2'); const fs = require('fs'); const daemonize = require("daemonize2") -const parsers = require('duniter-common').parsers; +const parsers = require('./app/common').parsers; const constants = require('./app/lib/constants'); const jsonpckg = require('./package.json'); const directory = require('./app/lib/system/directory'); -const rawer = require('duniter-common').rawer; +const rawer = require('./app/common').rawer; const logger = require('./app/lib/logger').NewLogger('server'); export class Server extends stream.Duplex implements HookableServer { diff --git a/test/fast/block_format.js b/test/fast/block_format.js index 9f57aa505..ee0ca79b3 100644 --- a/test/fast/block_format.js +++ b/test/fast/block_format.js @@ -1,6 +1,6 @@ "use strict"; const should = require('should'); -const parsers = require('duniter-common').parsers; +const parsers = require('../../app/common').parsers; const raw = "Version: 10\n" + "Type: Block\n" + diff --git a/test/fast/block_local.js b/test/fast/block_local.js index c8f286f5e..2bcb15fae 100644 --- a/test/fast/block_local.js +++ b/test/fast/block_local.js @@ -1,13 +1,13 @@ "use strict"; const co = require('co'); const should = require('should'); -const parsers = require('duniter-common').parsers; +const parsers = require('../../app/common').parsers; const indexer = require('../../app/lib/indexer').Indexer const LOCAL_RULES = require('../../app/lib/rules/local_rules').LOCAL_RULES_FUNCTIONS const ALIAS = require('../../app/lib/rules').ALIAS const blocks = require('../data/blocks.js'); const parser = parsers.parseBlock; -const Block = require('duniter-common').document.Block +const Block = require('../../app/common').document.Block const BlockDTO = require('../../app/lib/dto/BlockDTO').BlockDTO const conf = { diff --git a/test/fast/common/crypto.js b/test/fast/common/crypto.js index 477a46f42..bdd3f903b 100644 --- a/test/fast/common/crypto.js +++ b/test/fast/common/crypto.js @@ -2,9 +2,9 @@ const should = require('should'); const co = require('co'); const nacl = require('tweetnacl'); -const base58 = require('../../../app/lib/common/crypto/base58') -const naclUtil = require('../../../app/lib/common/crypto/nacl-util') -const keyring = require('../../../app/lib/common/crypto/keyring') +const base58 = require('../../../app/lib/common-libs/crypto/base58') +const naclUtil = require('../../../app/lib/common-libs/crypto/nacl-util') +const keyring = require('../../../app/lib/common-libs/crypto/keyring') const Base58decode = base58.Base58decode const Base58encode = base58.Base58encode diff --git a/test/fast/common/randomKey.js b/test/fast/common/randomKey.js index 5c924fe65..404993d20 100644 --- a/test/fast/common/randomKey.js +++ b/test/fast/common/randomKey.js @@ -1,8 +1,8 @@ "use strict"; const co = require('co') const should = require('should'); -const keyring = require('../../../app/lib/common/crypto/keyring') -const naclUtil = require('../../../app/lib/common/crypto/nacl-util') +const keyring = require('../../../app/lib/common-libs/crypto/keyring') +const naclUtil = require('../../../app/lib/common-libs/crypto/nacl-util') const enc = naclUtil.encodeBase64 const dec = naclUtil.decodeBase64 diff --git a/test/fast/modules/common/crypto.js b/test/fast/modules/common/crypto.js new file mode 100644 index 000000000..ce72bc5c5 --- /dev/null +++ b/test/fast/modules/common/crypto.js @@ -0,0 +1,50 @@ +"use strict"; +const should = require('should'); +const co = require('co'); +const nacl = require('tweetnacl'); +const base58 = require('../../../../app/common/lib/crypto/base58'); +const keyring = require('../../../../app/common/lib/crypto/keyring'); + +const enc = nacl.util.encodeBase64, + dec = nacl.util.decodeBase64; + +let pub, sec, rawPub, rawSec; + +describe('ed25519 tests:', function(){ + + before(() => co(function*() { + // Generate the keypair + const keyPair = keyring.Key('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'); + pub = base58.decode(keyPair.publicKey); + sec = base58.decode(keyPair.secretKey); + rawPub = base58.encode(pub); + rawSec = base58.encode(sec); + })); + + //it('good signature from existing secret key should be verified', function(done){ + // const keys = nacl.sign.scryptKeyPair.fromSecretKey(dec("TM0Imyj/ltqdtsNG7BFOD1uKMZ81q6Yk2oz27U+4pvs9QBfD6EOJWpK3CqdNG368nJgszy7ElozAzVXxKvRmDA==")); + // const msg = "cg=="; + // const goodSig = dec("52Hh9omo9rxklulAE7gvVeYvAq0GgXYoZE2NB/gzehpCYIT04bMcGIs5bhYLaH93oib34jsVMWs9Udadr1B+AQ=="); + // const sig = crypto.signSync(msg, keys.secretKey); + // sig.should.equal(enc(goodSig)); + // crypto.verify(msg, sig, enc(keys.publicKey)).should.be.true; + // done(); + //}); + + it('good signature from generated key should be verified', function(done){ + const msg = "Some message to be signed"; + const sig = keyring.Key(rawPub, rawSec).signSync(msg); + const verified = keyring.verify(msg, sig, rawPub); + verified.should.equal(true); + done(); + }); + + it('wrong signature from generated key should NOT be verified', function(done){ + const msg = "Some message to be signed"; + const cor = dec(enc(msg) + 'delta'); + const sig = keyring.Key(rawPub, rawSec).signSync(msg); + const verified = keyring.verify(cor, sig, rawPub); + verified.should.equal(false); + done(); + }); +}); diff --git a/test/fast/modules/common/grammar-test.js b/test/fast/modules/common/grammar-test.js new file mode 100644 index 000000000..3e27dcc86 --- /dev/null +++ b/test/fast/modules/common/grammar-test.js @@ -0,0 +1,70 @@ +"use strict"; + +const unlock = require('../../../../app/common/lib/txunlock'); +const should = require('should'); + +describe('Grammar', () => { + + let k1 = "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd"; + let k2 = "GgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd"; + let Ha = "CA978112CA1BBDCAFAC231B39A23DC4DA786EFF8147C4E72B9807785AFEE48BB"; + let Hz = "594E519AE499312B29433B7DD8A97FF068DEFCBA9755B6D5D00E84C524D67B06"; + + it('SIG should work', () => { + + unlock('SIG(' + k1 + ')', [{ pubkey: k1, sigOK: true }]).should.equal(true); + unlock('SIG(' + k1 + ')', [{ pubkey: k1, sigOK: false }]).should.equal(false); + unlock('SIG(' + k1 + ')', [{ pubkey: k2, sigOK: false }]).should.equal(false); + unlock('SIG(' + k1 + ')', [{ pubkey: k2, sigOK: true }]).should.equal(false); + }); + + it('XHX should work', () => { + + unlock('XHX(' + Ha + ')', ['a']).should.equal(true); + unlock('XHX(' + Hz + ')', ['z']).should.equal(true); + unlock('XHX(' + Hz + ')', ['a']).should.equal(false); + unlock('XHX(' + Ha + ')', ['z']).should.equal(false); + }); + + it('&& keywork should work', () => { + + unlock('SIG(' + k1 + ') && SIG(' + k2 + ')', [{ pubkey: k1, sigOK: true }, { pubkey: k1, sigOK: true }]).should.equal(false); + unlock('SIG(' + k1 + ') && SIG(' + k2 + ')', [{ pubkey: k1, sigOK: true }, { pubkey: k2, sigOK: false }]).should.equal(false); + unlock('SIG(' + k1 + ') && SIG(' + k2 + ')', [{ pubkey: k1, sigOK: true }, { pubkey: k2, sigOK: true }]).should.equal(true); + }); + + it('|| keywork should work', () => { + + unlock('SIG(' + k1 + ') || SIG(' + k2 + ')', [{ pubkey: k1, sigOK: true }, { pubkey: k2, sigOK: true }]).should.equal(true); + unlock('SIG(' + k1 + ') || SIG(' + k2 + ')', [{ pubkey: k1, sigOK: false }, { pubkey: k2, sigOK: true }]).should.equal(true); + unlock('SIG(' + k1 + ') || SIG(' + k2 + ')', [{ pubkey: k1, sigOK: true }, { pubkey: k2, sigOK: false }]).should.equal(true); + unlock('SIG(' + k1 + ') || SIG(' + k2 + ')', [{ pubkey: k1, sigOK: false }, { pubkey: k2, sigOK: false }]).should.equal(false); + }); + + it('|| && keyworks combined should work', () => { + + unlock('SIG(' + k1 + ') || (SIG(' + k1 + ') && SIG(' + k2 + '))', [{ pubkey: k1, sigOK: true },{ pubkey: k1, sigOK: true },{ pubkey: k2, sigOK: true }]).should.equal(true); + unlock('SIG(' + k2 + ') || (SIG(' + k1 + ') && SIG(' + k2 + '))', [{ pubkey: k2, sigOK: false },{ pubkey: k1, sigOK: true },{ pubkey: k2, sigOK: false }]).should.equal(false); + }); + + it('SIG XHX functions combined should work', () => { + + unlock('SIG(' + k1 + ') && XHX(' + Ha + ')', [{ pubkey: k1, sigOK: true },'a']).should.equal(true); + unlock('SIG(' + k1 + ') && XHX(' + Ha + ')', [{ pubkey: k1, sigOK: true },'z']).should.equal(false); + unlock('SIG(' + k1 + ') || XHX(' + Ha + ')', [{ pubkey: k1, sigOK: true },'a']).should.equal(true); + unlock('SIG(' + k1 + ') || XHX(' + Ha + ')', [{ pubkey: k1, sigOK: true },'z']).should.equal(true); + unlock('SIG(' + k1 + ') || XHX(' + Ha + ')', [{ pubkey: k1, sigOK: false },'z']).should.equal(false); + unlock('SIG(' + k1 + ') || XHX(' + Ha + ')', [{ pubkey: k1, sigOK: false },'a']).should.equal(true); + }); + + it('SIG, XHX, &&, || words combined should work', () => { + + unlock('SIG(' + k1 + ') && XHX(' + Ha + ') || XHX(' + Hz + ')', [{ pubkey: k1, sigOK: true },'a','z']).should.equal(true); + unlock('SIG(' + k1 + ') && XHX(' + Ha + ') || XHX(' + Hz + ')', [{ pubkey: k1, sigOK: true },'a','a']).should.equal(true); + unlock('SIG(' + k1 + ') && XHX(' + Ha + ') || XHX(' + Hz + ')', [{ pubkey: k1, sigOK: true },'z','a']).should.equal(false); + unlock('SIG(' + k1 + ') && XHX(' + Ha + ') || XHX(' + Hz + ')', [{ pubkey: k1, sigOK: false },'a','a']).should.equal(false); + unlock('SIG(' + k1 + ') && XHX(' + Ha + ') || XHX(' + Hz + ')', [{ pubkey: k1, sigOK: false },'a','z']).should.equal(true); + unlock('SIG(' + k1 + ') && XHX(' + Ha + ') || XHX(' + Hz + ')', [{ pubkey: k1, sigOK: false },'z','z']).should.equal(true); + unlock('(SIG(EA7Dsw39ShZg4SpURsrgMaMqrweJPUFPYHwZA8e92e3D) || XHX(03AC674216F3E15C761EE1A5E255F067953623C8B388B4459E13F978D7C846F4))', [{ pubkey: k1, sigOK: false },'1234']).should.equal(true); + }); +}); diff --git a/test/fast/modules/common/peering.js b/test/fast/modules/common/peering.js new file mode 100644 index 000000000..864b29001 --- /dev/null +++ b/test/fast/modules/common/peering.js @@ -0,0 +1,63 @@ +"use strict"; +const should = require('should'); +const assert = require('assert'); +const parsers = require('../../../../app/common/lib/parsers'); + +const rawPeer = "" + + "Version: 10\n" + + "Type: Peer\n" + + "Currency: beta_brousouf\n" + + "PublicKey: 3Z7w5g4gC9oxwEbATnmK2UFgGWhLZPmZQb5dRxvNrXDu\n" + + "Block: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855\n" + + "Endpoints:\n" + + "BASIC_MERKLED_API duniter.twiced.fr 88.163.127.43 9101\n" + + "OTHER_PROTOCOL 88.163.127.43 9102\n" + + "bvuKzc6+cGWMGC8FIkZHN8kdQhaRL/MK60KYyw5vJqkKEgxXbygQHAzfoojeSY4gPKIu4FggBkR1HndSEm2FAQ==\n"; + +const Peer = require('../../../../app/common/lib/document/peer'); + +describe('Peer', function(){ + + describe('of some key', function(){ + + var pr; + + before(function(done) { + pr = Peer.fromJSON(parsers.parsePeer.syncWrite(rawPeer)); + done(); + }); + + it('should be version 10', function(){ + assert.equal(pr.version, 10); + }); + + it('should have beta_brousoufs currency name', function(){ + assert.equal(pr.currency, 'beta_brousouf'); + }); + + it('should have public key', function(){ + assert.equal(pr.pubkey, '3Z7w5g4gC9oxwEbATnmK2UFgGWhLZPmZQb5dRxvNrXDu'); + }); + + it('should have 2 endpoints', function(){ + assert.equal(pr.endpoints.length, 2); + }); + + it('should have DNS', function(){ + assert.equal(pr.getDns(), 'duniter.twiced.fr'); + }); + + it('should have IPv4', function(){ + should.exist(pr.getIPv4()); + assert.equal(pr.getIPv4(), "88.163.127.43"); + }); + + it('should have no IPv6 address', function(){ + should.not.exist(pr.getIPv6()); + }); + + it('should have port 9101', function(){ + assert.equal(pr.getPort(), 9101); + }); + }); +}); diff --git a/test/fast/modules/common/randomKey.js b/test/fast/modules/common/randomKey.js new file mode 100644 index 000000000..7a21f9cf8 --- /dev/null +++ b/test/fast/modules/common/randomKey.js @@ -0,0 +1,35 @@ +"use strict"; +const should = require('should'); +const co = require('co'); +const nacl = require('tweetnacl'); +const keyring = require('../../../../app/common/lib/crypto/keyring'); + +const enc = nacl.util.encodeBase64, + dec = nacl.util.decodeBase64; + +let key; + +describe('Random keypair', function(){ + + before(() => co(function*() { + // Generate the keypair + key = keyring.randomKey() + })); + + it('good signature from generated key should be verified', function(done){ + const msg = "Some message to be signed"; + const sig = keyring.Key(key.publicKey, key.secretKey).signSync(msg); + const verified = keyring.verify(msg, sig, key.publicKey); + verified.should.equal(true); + done(); + }); + + it('wrong signature from generated key should NOT be verified', function(done){ + const msg = "Some message to be signed"; + const cor = dec(enc(msg) + 'delta'); + const sig = keyring.Key(key.publicKey, key.secretKey).signSync(msg); + const verified = keyring.verify(cor, sig, key.publicKey); + verified.should.equal(false); + done(); + }); +}); diff --git a/test/fast/modules/common/tx_format.js b/test/fast/modules/common/tx_format.js new file mode 100644 index 000000000..10fc83238 --- /dev/null +++ b/test/fast/modules/common/tx_format.js @@ -0,0 +1,28 @@ +"use strict"; +var should = require('should'); +var parsers = require('../../../../app/common/lib/parsers'); + +var raw = "Version: 10\n" + + "Type: Transaction\n" + + "Currency: test_net\n" + + "Blockstamp: 3-2A27BD040B16B7AF59DDD88890E616987F4DD28AA47B9ABDBBEE46257B88E945\n" + + "Locktime: 0\n" + + "Issuers:\n" + + "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk\n" + + "Inputs:\n" + + "100000:0: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/fast/v1.0-local-index.js b/test/fast/v1.0-local-index.js index 29592f579..1f27f3624 100644 --- a/test/fast/v1.0-local-index.js +++ b/test/fast/v1.0-local-index.js @@ -2,9 +2,9 @@ const _ = require('underscore'); const should = require('should'); -const parsers = require('duniter-common').parsers; +const parsers = require('../../app/common').parsers; const indexer = require('../../app/lib/indexer').Indexer -const constants = require('duniter-common').constants; +const constants = require('../../app/common').constants; const BlockDTO = require('../../app/lib/dto/BlockDTO').BlockDTO const raw = "Version: 10\n" + diff --git a/test/integration/cli.js b/test/integration/cli.js index f6572d0ed..a6bdcfe2b 100644 --- a/test/integration/cli.js +++ b/test/integration/cli.js @@ -8,7 +8,7 @@ const _ = require('underscore'); const toolbox = require('./tools/toolbox'); const duniter = require('../../index'); const merkleh = require('../../app/lib/helpers/merkle'); -const hashf = require('duniter-common').hashf; +const hashf = require('../../app/common').hashf; const constants = require('../../app/lib/constants'); const MerkleDTO = require('../../app/lib/dto/MerkleDTO').MerkleDTO diff --git a/test/integration/server-sandbox.js b/test/integration/server-sandbox.js index e76ef7a1b..be25bc56f 100644 --- a/test/integration/server-sandbox.js +++ b/test/integration/server-sandbox.js @@ -3,7 +3,7 @@ const co = require('co'); const should = require('should'); const bma = require('../../app/modules/bma').BmaDependency.duniter.methods.bma; -const common = require('duniter-common'); +const common = require('../../app/common'); const user = require('./tools/user'); const commit = require('./tools/commit'); const toolbox = require('./tools/toolbox'); diff --git a/test/integration/tools/user.js b/test/integration/tools/user.js index d2912b6e6..c25105a7d 100644 --- a/test/integration/tools/user.js +++ b/test/integration/tools/user.js @@ -4,9 +4,9 @@ const _ = require('underscore'); const async = require('async'); const request = require('request'); const contacter = require('../../../app/modules/crawler').CrawlerDependency.duniter.methods.contacter; -const common = require('duniter-common'); +const common = require('../../../app/common'); const ucp = common.buid; -const parsers = require('duniter-common').parsers; +const parsers = require('../../../app/common').parsers; const keyring = common.keyring; const rawer = common.rawer; const constants = require('../../../app/lib/constants'); diff --git a/test/integration/transactions-chaining.js b/test/integration/transactions-chaining.js index 0ef947b48..c372b05b8 100644 --- a/test/integration/transactions-chaining.js +++ b/test/integration/transactions-chaining.js @@ -6,7 +6,7 @@ const should = require('should'); const assert = require('assert'); const constants = require('../../app/lib/constants'); const bma = require('../../app/modules/bma').BmaDependency.duniter.methods.bma; -const common = require('duniter-common'); +const common = require('../../app/common'); const toolbox = require('./tools/toolbox'); const node = require('./tools/node'); const user = require('./tools/user'); diff --git a/test/integration/transactions-pruning.js b/test/integration/transactions-pruning.js index d17dd4666..7b3be185f 100644 --- a/test/integration/transactions-pruning.js +++ b/test/integration/transactions-pruning.js @@ -6,7 +6,7 @@ const user = require('./tools/user'); const commit = require('./tools/commit'); const toolbox = require('./tools/toolbox'); const constants = require('../../app/lib/constants'); -const common = require('duniter-common'); +const common = require('../../app/common'); const s1 = toolbox.server({ currency: 'currency_one', diff --git a/test/integration/v1.0-modules-api.js b/test/integration/v1.0-modules-api.js index d0e79e535..c5f9c7874 100644 --- a/test/integration/v1.0-modules-api.js +++ b/test/integration/v1.0-modules-api.js @@ -7,7 +7,7 @@ const util = require('util'); const path = require('path'); const stream = require('stream'); const duniter = require('../../index'); -const parsers = require('duniter-common').parsers; +const parsers = require('../../app/common').parsers; const querablep = require('querablep'); describe("v1.0 Module API", () => { -- GitLab