diff --git a/Cargo.lock b/Cargo.lock index b0d819cb327262b5ec3953e37f0c4e64e2cf68d2..013c04009950466aea988230c5484adb0a4a70dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,14 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "addr2line" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "0.2.3" @@ -21,12 +30,32 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +[[package]] +name = "backtrace" +version = "0.3.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46254cf2fdcdf1badb5934448c1bcbe046a56537b3987d96c51a7afc5d03f293" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" +[[package]] +name = "beef" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "474a626a67200bd107d44179bb3d4fc61891172d11696609264589be6a0e6a43" + [[package]] name = "bincode" version = "1.2.1" @@ -37,6 +66,27 @@ dependencies = [ "serde", ] +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + [[package]] name = "bs58" version = "0.3.1" @@ -49,6 +99,12 @@ version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + [[package]] name = "byteorder" version = "1.3.4" @@ -123,30 +179,97 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "cryptoxide" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da24927b5b899890bcb29205436c957b7892ec3a3fbffce81d710b9611e77778" + [[package]] name = "cslice" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "697c714f50560202b1f4e2e09cd50a421881c83e9025db75d15f276616f04f40" +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array", +] + [[package]] name = "dubp-common" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b41d722bf752e5f87d07685aba6df25f69881f9aafa7060fae459e2948d8080" dependencies = [ - "dup-crypto", + "dup-crypto 0.18.0", "serde", "serde_json", "thiserror", ] +[[package]] +name = "dubp-common" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fbf84c220b013b9ebd30315fb89ce1ce74ad415ca24401fbc2bd264aaa5097" +dependencies = [ + "dup-crypto 0.25.0", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "dubp-documents" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db28e6dd002d47cb916bc72c0ef02e2daedeb019e2710716fa5c30851fabbbca" +dependencies = [ + "beef", + "dubp-wallet", + "log", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "dubp-documents-parser" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf34add60e4c075265ef74bebebd3fd665f73eaec36133e8bb2fe8aef968a9fc" +dependencies = [ + "dubp-documents", + "json-pest-parser", + "pest", + "pest_derive", + "serde_json", + "thiserror", +] + +[[package]] +name = "dubp-wallet" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42d477e61d1f94ce759f1936b9542a7e96acd4de68002289a5fc97c9dc5f36" +dependencies = [ + "dubp-common 0.25.0", + "serde", + "smallvec", + "thiserror", +] + [[package]] name = "dubp-wot" version = "0.11.0" dependencies = [ "bincode", - "dubp-common", + "dubp-common 0.2.0", "log", "rayon", "serde", @@ -154,15 +277,20 @@ dependencies = [ [[package]] name = "duniteroxyde" -version = "0.2.9" +version = "0.3.0" dependencies = [ "bincode", "bs58", - "dubp-common", + "dubp-common 0.25.0", + "dubp-documents", + "dubp-documents-parser", + "dubp-wallet", "dubp-wot", "flate2", "neon", "neon-build", + "neon-serde", + "unwrap", ] [[package]] @@ -181,12 +309,45 @@ dependencies = [ "zeroize", ] +[[package]] +name = "dup-crypto" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287b23c4281016278b47b80baf67186ce756dde738070cc7a41297da5e81dec1" +dependencies = [ + "base64", + "bs58", + "byteorder", + "cryptoxide", + "getrandom", + "ring", + "serde", + "thiserror", + "zeroize", +] + [[package]] name = "either" version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "backtrace", + "version_check", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + [[package]] name = "flate2" version = "1.0.17" @@ -199,6 +360,33 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", +] + +[[package]] +name = "getrandom" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" +dependencies = [ + "cfg-if", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" + [[package]] name = "hermit-abi" version = "0.1.12" @@ -223,6 +411,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "json-pest-parser" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bc5c84a2bceeda1ce3bd58497bde2d8cba61ca0b45873ef502401f0ff2ae8ed" +dependencies = [ + "pest", + "pest_derive", + "thiserror", + "unwrap", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -244,6 +444,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + [[package]] name = "maybe-uninit" version = "2.0.0" @@ -306,6 +512,19 @@ dependencies = [ "neon-sys", ] +[[package]] +name = "neon-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f134307714cdd478581fd3cc990202ead78744d676d8437bdff64260395eb8e" +dependencies = [ + "error-chain", + "neon", + "neon-runtime", + "num", + "serde", +] + [[package]] name = "neon-sys" version = "0.4.0" @@ -316,6 +535,70 @@ dependencies = [ "regex", ] +[[package]] +name = "num" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" +dependencies = [ + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e6b7c748f995c4c29c5f5ae0248536e04a5739927c74ec0fa564805094b9f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.13.0" @@ -326,6 +609,67 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5" + +[[package]] +name = "once_cell" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +dependencies = [ + "maplit", + "pest", + "sha-1", +] + [[package]] name = "proc-macro2" version = "1.0.19" @@ -388,19 +732,25 @@ checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" [[package]] name = "ring" -version = "0.16.12" +version = "0.16.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba5a8ec64ee89a76c98c549af81ff14813df09c3e6dc4766c3856da48597a0c" +checksum = "952cd6b98c85bbc30efa1ba5783b8abf12fec8b3287ffa52605b9432313e34e4" dependencies = [ "cc", - "lazy_static", "libc", + "once_cell", "spin", "untrusted", "web-sys", "winapi", ] +[[package]] +name = "rustc-demangle" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" + [[package]] name = "ryu" version = "1.0.5" @@ -459,6 +809,27 @@ dependencies = [ "serde", ] +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer", + "digest", + "fake-simd", + "opaque-debug", +] + +[[package]] +name = "smallvec" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" +dependencies = [ + "serde", +] + [[package]] name = "spin" version = "0.5.2" @@ -517,6 +888,18 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + [[package]] name = "unicode-xid" version = "0.2.0" @@ -525,9 +908,9 @@ checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] name = "untrusted" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60369ef7a31de49bcb3f6ca728d4ba7300d9a1658f94c727d4cab8c8d9f4aece" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "unwrap" @@ -535,6 +918,18 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e33648dd74328e622c7be51f3b40a303c63f93e6fa5f08778b6203a4c25c20f" +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasm-bindgen" version = "0.2.60" diff --git a/Cargo.toml b/Cargo.toml index 9b669f412cde609ea4d64a65665b07c7aaca5e17..56e722261f26245bae3bb5dcdf8452e9816e76d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,6 @@ members = [ [patch.crates-io] #dubp-common = { path = "../dubp-rs-libs/common" } +#dubp-documents = { path = "../dubp-rs-libs/documents" } +#dubp-documents-parser = { path = "../dubp-rs-libs/documents-parser" } +#dubp-wallet = { path = "../dubp-rs-libs/wallet" } diff --git a/app/lib/common-libs/constants.ts b/app/lib/common-libs/constants.ts index a0b6151cfe8e3c02064c8f402fb3c2908655aced..3c501ab4c7ee6eb182d9294e3fc7eda581b2422a 100755 --- a/app/lib/common-libs/constants.ts +++ b/app/lib/common-libs/constants.ts @@ -23,7 +23,7 @@ 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_VERSION = "(10|11|12)"; +const BLOCK_VERSION = "(1[0-3])"; const TX_VERSION = "(10)"; const DIVIDEND = "[1-9][0-9]{0,5}"; const ZERO_OR_POSITIVE_INT = "0|[1-9][0-9]{0,18}"; diff --git a/app/lib/dto/TransactionDTO.ts b/app/lib/dto/TransactionDTO.ts index 71dfa660f6176053edb6f5eba203ea2e7e34e46a..6cd8bf15a7fbc904f841f7356b28b94f0d974ef7 100644 --- a/app/lib/dto/TransactionDTO.ts +++ b/app/lib/dto/TransactionDTO.ts @@ -14,6 +14,7 @@ import { hashf } from "../common"; import { Cloneable } from "./Cloneable"; import { verify } from "../../../neon/lib"; +import { TransactionDTOV10 } from "../../../neon/native"; export interface BaseDTO { base: number; @@ -252,7 +253,7 @@ export class TransactionDTO implements Cloneable { }; } - getTransactionSigResult(dubp_version: number) { + getTransactionSigResult() { const sigResult = new TxSignatureResultImpl(this.issuers.slice()); let i = 0; const raw = this.getRawTxNoSig(); @@ -260,20 +261,15 @@ export class TransactionDTO implements Cloneable { while (matching && i < this.signatures.length) { const sig = this.signatures[i]; const pub = this.issuers[i]; - if (dubp_version >= 12) { - sigResult.sigs[i].ok = verify(raw, sig, pub); - } else { - // TODO ESZ list all invalid transactions - sigResult.sigs[i].ok = verify(raw, sig, pub); - } + sigResult.sigs[i].ok = verify(raw, sig, pub); matching = sigResult.sigs[i].ok; i++; } return sigResult; } - checkSignatures(dubp_version: number) { - return this.getTransactionSigResult(dubp_version).allMatching; + checkSignatures() { + return this.getTransactionSigResult().allMatching; } static fromJSONObject(obj: any, currency: string = "") { @@ -293,6 +289,23 @@ export class TransactionDTO implements Cloneable { ); } + static fromTransactionDTOV10(txV10: TransactionDTOV10) { + return new TransactionDTO( + 10, + txV10.currency || "", + txV10.locktime || 0, + txV10.hash || "", + txV10.blockstamp || "", + txV10.blockstampTime || 0, + txV10.issuers || [], + txV10.inputs || [], + txV10.outputs || [], + txV10.unlocks || [], + txV10.signatures || [], + txV10.comment || "" + ); + } + static toRAW(json: TransactionDTO, noSig = false) { let raw = ""; raw += "Version: " + json.version + "\n"; diff --git a/app/lib/indexer.ts b/app/lib/indexer.ts index c691672f4c65e06431fbe83894f2d28104eb0f9e..c87fe09f726928a8ab6a6b478eb8ae149894fc94 100644 --- a/app/lib/indexer.ts +++ b/app/lib/indexer.ts @@ -18,7 +18,7 @@ import { RevocationDTO } from "./dto/RevocationDTO"; import { CertificationDTO } from "./dto/CertificationDTO"; import { TransactionDTO } from "./dto/TransactionDTO"; import { DBHead } from "./db/DBHead"; -import { verify } from "../../neon/lib"; +import { sourceIsUnlockable, verify } from "../../neon/lib"; import { rawer, txunlock } from "./common-libs/index"; import { CommonConstants } from "./common-libs/constants"; import { MembershipDTO } from "./dto/MembershipDTO"; @@ -2634,22 +2634,38 @@ function txSourceUnlock( source: { conditions: string; written_time: number }, HEAD: DBHead ) { - const tx = ENTRY.txObj; - const unlockParams: string[] = TransactionDTO.unlock2params( - ENTRY.unlock || "" - ); - const unlocksMetadata: UnlockMetadata = {}; - const sigResult = TransactionDTO.fromJSONObject(tx).getTransactionSigResult( - HEAD.version - ); if (!source.conditions) { return false; // Unlock fail } - if (source.conditions.match(/CLTV/)) { - unlocksMetadata.currentTime = HEAD.medianTime; - } - if (source.conditions.match(/CSV/)) { - unlocksMetadata.elapsedTime = HEAD.medianTime - source.written_time; + const tx = ENTRY.txObj; + if (HEAD.version >= 13) { + // Vérifier si les proofs d'une source (en input d'une tx) sont valides ou non + return sourceIsUnlockable( + HEAD.medianTime, + tx.issuers, + ENTRY.unlock || "", + source.written_time, + source.conditions + ); + } else { + const unlockParams: string[] = TransactionDTO.unlock2params( + ENTRY.unlock || "" + ); + const unlocksMetadata: UnlockMetadata = {}; + const sigResult = TransactionDTO.fromJSONObject( + tx + ).getTransactionSigResult(); + if (source.conditions.match(/CLTV/)) { + unlocksMetadata.currentTime = HEAD.medianTime; + } + if (source.conditions.match(/CSV/)) { + unlocksMetadata.elapsedTime = HEAD.medianTime - source.written_time; + } + return txunlock( + source.conditions, + unlockParams, + sigResult, + unlocksMetadata + ); } - return txunlock(source.conditions, unlockParams, sigResult, unlocksMetadata); } diff --git a/app/lib/rules/global_rules.ts b/app/lib/rules/global_rules.ts index c168807bffda3c74c90285a777039281cee9cb0a..eeb03fb055a56f5bfacb39f17a0621c794e56f69 100644 --- a/app/lib/rules/global_rules.ts +++ b/app/lib/rules/global_rules.ts @@ -16,7 +16,7 @@ import { FileDAL } from "../dal/fileDAL"; import { DBBlock } from "../db/DBBlock"; import { TransactionDTO, TxSignatureResult } from "../dto/TransactionDTO"; import { BlockDTO } from "../dto/BlockDTO"; -import { verify } from "../../../neon/lib"; +import { txsInputsAreUnlockable, verify } from "../../../neon/lib"; import { rawer, txunlock } from "../common-libs/index"; import { CommonConstants } from "../common-libs/constants"; import { IdentityDTO } from "../dto/IdentityDTO"; @@ -129,29 +129,31 @@ export const GLOBAL_RULES_FUNCTIONS = { let index = parseInt(sp[0]); unlocks[index] = sp[1]; } - for (let k = 0, len2 = inputs.length; k < len2; k++) { - let src = inputs[k]; - let dbSrc: SimpleTxInput | null = await dal.getSource( - src.identifier, - src.pos, - src.type === "D" + let k = 0; // need for DUBP version <= 12 + let sources_conditions = []; // need for DUBP version >= 13 + let sources_written_on = []; // need for DUBP version >= 13 + for (const input of inputs) { + let source: SimpleTxInput | null = await dal.getSource( + input.identifier, + input.pos, + input.type === "D" ); logger.debug( "Source %s:%s:%s:%s = %s", - src.amount, - src.base, - src.identifier, - src.pos, - dbSrc && dbSrc.consumed + input.amount, + input.base, + input.identifier, + input.pos, + source && source.consumed ); - if (!dbSrc) { + if (!source) { // For chained transactions which are checked on sandbox submission, we accept them if there is already // a previous transaction of the chain already recorded in the pool - dbSrc = await (async () => { + source = await (async () => { let hypotheticSrc: any = null; - let targetTX = await findSourceTx(src.identifier); + let targetTX = await findSourceTx(input.identifier); if (targetTX) { - let outputStr = targetTX.outputs[src.pos]; + let outputStr = targetTX.outputs[input.pos]; if (outputStr) { hypotheticSrc = TransactionDTO.outputStr2Obj(outputStr); hypotheticSrc.consumed = false; @@ -161,55 +163,77 @@ export const GLOBAL_RULES_FUNCTIONS = { return hypotheticSrc; })(); } - if (!dbSrc || dbSrc.consumed) { + if (!source || source.consumed) { logger.warn( "Source " + - [src.type, src.identifier, src.pos].join(":") + + [input.type, input.identifier, input.pos].join(":") + " is not available" ); throw constants.ERRORS.SOURCE_ALREADY_CONSUMED; } - sumOfInputs += dbSrc.amount * Math.pow(10, dbSrc.base); - if (block.medianTime - dbSrc.written_time < tx.locktime) { + sumOfInputs += source.amount * Math.pow(10, source.base); + if (block.medianTime - source.written_time < tx.locktime) { throw constants.ERRORS.LOCKTIME_PREVENT; } - let unlockValues = unlocks[k]; - let unlocksForCondition: string[] = (unlockValues || "").split(" "); - let unlocksMetadata: any = {}; - if (dbSrc.conditions) { - if (dbSrc.conditions.match(/CLTV/)) { - unlocksMetadata.currentTime = block.medianTime; - } - - if (dbSrc.conditions.match(/CSV/)) { - unlocksMetadata.elapsedTime = block.medianTime - dbSrc.written_time; - } + if (block.version <= 12) { + const sigs = tx.getTransactionSigResult(); + let unlockValues = unlocks[k]; + k++; + let unlocksForCondition: string[] = (unlockValues || "").split(" "); + let unlocksMetadata: any = {}; + if (source.conditions) { + if (source.conditions.match(/CLTV/)) { + unlocksMetadata.currentTime = block.medianTime; + } - const sigs = tx.getTransactionSigResult(block.version); + if (source.conditions.match(/CSV/)) { + unlocksMetadata.elapsedTime = + block.medianTime - source.written_time; + } - try { - if ( - !txunlock( - dbSrc.conditions, - unlocksForCondition, - sigs, - unlocksMetadata - ) - ) { - throw Error("Locked"); + try { + if ( + !txunlock( + source.conditions, + unlocksForCondition, + sigs, + unlocksMetadata + ) + ) { + throw Error("Locked"); + } + } catch (e) { + logger.warn( + "Source " + + [ + input.amount, + input.base, + input.type, + input.identifier, + input.pos, + ].join(":") + + " unlock fail" + ); + throw constants.ERRORS.WRONG_UNLOCKER; } - } catch (e) { - logger.warn( - "Source " + - [src.amount, src.base, src.type, src.identifier, src.pos].join( - ":" - ) + - " unlock fail" - ); - throw constants.ERRORS.WRONG_UNLOCKER; + } else { + throw Error("Source with no conditions"); } - } else { - throw Error("Source with no conditions"); + } + sources_conditions.push(source.conditions); + sources_written_on.push(source.written_time); + } + if (block.version >= 13) { + // No need to check the validity of the signatures, this is already done by local verification. + if ( + !txsInputsAreUnlockable( + block.medianTime, + sources_conditions, + sources_written_on, + tx + ) + ) { + throw constants.ERRORS.WRONG_UNLOCKER; } } let sumOfOutputs = outputs.reduce(function (p, output) { diff --git a/app/lib/rules/local_rules.ts b/app/lib/rules/local_rules.ts index ea5f8a578e143bc9ad9dfd5c5eb39cb1a492846d..feaeefc3b1c63eb38d20fa17037ddb3d2bad5b60 100644 --- a/app/lib/rules/local_rules.ts +++ b/app/lib/rules/local_rules.ts @@ -522,7 +522,7 @@ export const LOCAL_RULES_FUNCTIONS = { const txs = block.transactions; // Check rule against each transaction for (const tx of txs) { - if (!tx.checkSignatures(block.version)) { + if (!tx.checkSignatures()) { throw Error("Signature from a transaction must match"); } } @@ -625,6 +625,7 @@ function checkBunchOfTransactions( medianTime, }; const index = Indexer.localIndex(block, conf); + return (async () => { let local_rule = LOCAL_RULES_FUNCTIONS; await local_rule.checkTxLen(block); @@ -650,9 +651,6 @@ export const LOCAL_RULES_HELPERS = { getMaxTransactionDepth, - checkSingleTransactionLocally: (tx: any, conf: ConfDTO) => - checkBunchOfTransactions([tx], conf, 0), - checkTxAmountsValidity: (tx: TransactionDTO) => { const inputs = tx.inputsAsObjects(); const outputs = tx.outputsAsObjects(); diff --git a/app/service/TransactionsService.ts b/app/service/TransactionsService.ts index ec67cb29b7755b620a33ccad13424f94177aa3dc..e7cbf8cc3e624de036117d2fa1aa1b64c1bc90df 100644 --- a/app/service/TransactionsService.ts +++ b/app/service/TransactionsService.ts @@ -38,8 +38,8 @@ export class TransactionService extends FIFOService { this.logger = require("../lib/logger").NewLogger(this.dal.profile); } - processTx(txObj: any) { - const tx = TransactionDTO.fromJSONObject(txObj, this.conf.currency); + // Called only when receiving a doc tx via BMA or WS2P + processVerifiedTx(tx: TransactionDTO) { const hash = tx.getHash(); return this.pushFIFO<TransactionDTO>(hash, async () => { try { @@ -62,7 +62,6 @@ export class TransactionService extends FIFOService { // Start checks... const fakeTimeVariation = current.medianTime + 1; const dto = TransactionDTO.fromJSONObject(tx); - await LOCAL_RULES_HELPERS.checkSingleTransactionLocally(dto, this.conf); await GLOBAL_RULES_HELPERS.checkTxBlockStamp(tx, this.dal); await GLOBAL_RULES_HELPERS.checkSingleTransaction( dto, diff --git a/neon/lib/index.ts b/neon/lib/index.ts index 48fdad95ce394d4af0b1875f0940595960e531aa..f24c20e157b571dd1f392f78d099ce81d87f62db 100644 --- a/neon/lib/index.ts +++ b/neon/lib/index.ts @@ -1,3 +1,14 @@ -export { Ed25519Signator, generateRandomSeed, seedToSecretKey, sha256, verify, Wot } from "../native"; +export { + Ed25519Signator, + generateRandomSeed, + rawTxParseAndVerify, + sha256, + seedToSecretKey, + sourceIsUnlockable, + txVerify, + txsInputsAreUnlockable, + verify, + Wot +} from "../native"; export { KeyPairBuilder } from "./crypto"; export { WotBuilder } from "./wot"; diff --git a/neon/native/Cargo.toml b/neon/native/Cargo.toml index 363e8b702496e8ba7338940c96a34242f236335d..487c7d8222800808c0570a79803766b66df9dcb1 100644 --- a/neon/native/Cargo.toml +++ b/neon/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "duniteroxyde" -version = "0.2.9" +version = "0.3.0" authors = ["librelois <elois@ifee.fr>"] license = "AGPL-3.0" build = "build.rs" @@ -17,7 +17,14 @@ neon-build = "0.4.0" [dependencies] bincode = "1.2.1" bs58 = "0.3.0" -dubp-common = { version = "0.2.0", features = ["rand", "scrypt"] } +dubp-common = { version = "0.25.0", features = ["crypto_scrypt"] } +dubp-documents = { version = "0.25.0" } +dubp-documents-parser = { version = "0.25.0" } +dubp-wallet = { version = "0.25.0" } dubp-wot = { path = "../../rust-libs/dubp-wot" } flate2 = "1.0.16" neon = "0.4.0" +neon-serde = "0.4.0" + +[dev-dependencies] +unwrap = "1.2.1" diff --git a/neon/native/index.d.ts b/neon/native/index.d.ts index e31c8cbdf33858186239385d59905349669bc4d6..abec083830de1861fac0071c57ba576254c20ad0 100644 --- a/neon/native/index.d.ts +++ b/neon/native/index.d.ts @@ -1,6 +1,7 @@ /* tslint:disable */ import * as _crypto from './crypto'; +import * as _transactions from './transaction'; import * as _wot from './wot'; export import Ed25519Signator = _crypto.Ed25519Signator; @@ -9,5 +10,11 @@ export import seedToSecretKey = _crypto.seedToSecretKey; export import sha256 = _crypto.sha256; export import verify = _crypto.verify; +export import TransactionDTOV10 = _transactions.TransactionDTOV10; +export import rawTxParseAndVerify = _transactions.rawTxParseAndVerify; +export import sourceIsUnlockable = _transactions.sourceIsUnlockable; +export import txVerify = _transactions.txVerify; +export import txsInputsAreUnlockable = _transactions.txsInputsAreUnlockable; + export import Wot = _wot.Wot; export import DetailedDistance = _wot.DetailedDistance; diff --git a/neon/native/src/crypto.rs b/neon/native/src/crypto.rs index bd42f741ea630813f381158a765e8abd63da0e82..c7399976cd39cf59506dd90751ea3332d9cb1464 100644 --- a/neon/native/src/crypto.rs +++ b/neon/native/src/crypto.rs @@ -194,13 +194,15 @@ fn sign_bytes<'c>( mod tests { use super::*; + use unwrap::unwrap; #[test] fn test_keypair_from_expanded_base58_secret_key() { let expanded_base58_secret_key = "51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP"; - let signator = keypair_from_expanded_base58_secret_key(expanded_base58_secret_key) - .expect("fail to generate keypair"); + let signator = unwrap!(keypair_from_expanded_base58_secret_key( + expanded_base58_secret_key + )); assert_eq!( "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd", diff --git a/neon/native/src/lib.rs b/neon/native/src/lib.rs index 94aa1964de9374642d5aff07763705765f6ce4d5..bc28e4cd396352312503b23608aa1cfe6f597188 100644 --- a/neon/native/src/lib.rs +++ b/neon/native/src/lib.rs @@ -13,7 +13,20 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. +#![deny( + clippy::expect_used, + clippy::unwrap_used, + missing_debug_implementations, + missing_copy_implementations, + trivial_casts, + trivial_numeric_casts, + unsafe_code, + unstable_features, + unused_import_braces +)] + mod crypto; +mod transaction; mod wot; use neon::{prelude::*, register_module}; @@ -37,6 +50,19 @@ register_module!(mut cx, { cx.export_function("sha256", crate::crypto::sha256)?; cx.export_function("verify", crate::crypto::verify)?; cx.export_class::<crate::crypto::JsKeyPair>("Ed25519Signator")?; + cx.export_function( + "rawTxParseAndVerify", + crate::transaction::raw_tx_parse_and_verify, + )?; + cx.export_function( + "sourceIsUnlockable", + crate::transaction::source_is_unlockable, + )?; + cx.export_function( + "txsInputsAreUnlockable", + crate::transaction::txs_inputs_are_unlockable, + )?; + cx.export_function("txVerify", crate::transaction::tx_verify)?; cx.export_class::<crate::wot::JsWoT>("Wot")?; Ok(()) }); diff --git a/neon/native/src/transaction.rs b/neon/native/src/transaction.rs new file mode 100644 index 0000000000000000000000000000000000000000..a7dcba0964bcc5c836aaa5bd45694c2483930dc2 --- /dev/null +++ b/neon/native/src/transaction.rs @@ -0,0 +1,160 @@ +// Copyright (C) 2020 Éloïs SANCHEZ. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +use crate::into_neon_res; +use dubp_common::crypto::{bases::BaseConversionError, keys::ed25519, keys::PublicKey}; +use dubp_documents::transaction::{ + TransactionDocumentTrait, TransactionDocumentV10, TransactionDocumentV10Stringified, + TransactionInputUnlocksV10, +}; +use dubp_documents::{prelude::*, smallvec::SmallVec}; +use dubp_documents_parser::prelude::*; +use dubp_wallet::prelude::*; +use neon::prelude::*; + +pub fn raw_tx_parse_and_verify(mut cx: FunctionContext) -> JsResult<JsValue> { + let raw_tx = cx.argument::<JsString>(0)?.value(); + let currency_opt = if let Some(arg1) = cx.argument_opt(1) { + Some(arg1.downcast_or_throw::<JsString, _>(&mut cx)?.value()) + } else { + None + }; + + match TransactionDocumentV10::parse_from_raw_text(&raw_tx) { + Ok(tx) => { + if let Err(e) = tx.verify(currency_opt) { + cx.throw_error(format!("{}", e)) + } else { + let tx_stringified = tx.to_string_object(); + Ok(neon_serde::to_value(&mut cx, &tx_stringified)?) + } + } + Err(e) => cx.throw_error(format!("{}", e)), + } +} + +pub fn tx_verify(mut cx: FunctionContext) -> JsResult<JsUndefined> { + let tx_obj = cx.argument::<JsValue>(0)?; + let currency_opt = if let Some(arg1) = cx.argument_opt(1) { + Some(arg1.downcast_or_throw::<JsString, _>(&mut cx)?.value()) + } else { + None + }; + + let tx_stringified: TransactionDocumentV10Stringified = + neon_serde::from_value(&mut cx, tx_obj)?; + match TransactionDocumentV10::from_string_object(&tx_stringified) { + Ok(tx) => { + if let Err(e) = tx.verify(currency_opt) { + cx.throw_error(format!("{}", e)) + } else { + Ok(cx.undefined()) + } + } + Err(e) => cx.throw_error(format!("{}", e)), + } +} + +pub fn txs_inputs_are_unlockable(mut cx: FunctionContext) -> JsResult<JsBoolean> { + let current_bc_time = cx.argument::<JsNumber>(0)?.value() as u64; + let inputs_scripts_js = cx.argument::<JsValue>(1)?; + let inputs_written_on_js = cx.argument::<JsValue>(2)?; + let tx_obj = cx.argument::<JsValue>(3)?; + + let inputs_scripts_str: Vec<String> = neon_serde::from_value(&mut cx, inputs_scripts_js)?; + let inputs_written_on: Vec<u64> = neon_serde::from_value(&mut cx, inputs_written_on_js)?; + let tx_stringified: TransactionDocumentV10Stringified = + neon_serde::from_value(&mut cx, tx_obj)?; + + match TransactionDocumentV10::from_string_object(&tx_stringified) { + Ok(tx) => { + let proofs = tx.get_inputs_unlocks(); + let tx_issuers = tx.issuers(); + for i in 0..proofs.len() { + if !source_is_unlockable_inner( + current_bc_time, + &proofs[i], + inputs_written_on[i], + &tx_issuers, + &inputs_scripts_str[i], + ) { + return Ok(cx.boolean(false)); + } + } + // All proofs are valid and **seem** useful for all inputs + Ok(cx.boolean(true)) + } + Err(e) => { + // Tx malformated + println!("{}", e); + Ok(cx.boolean(false)) + } + } +} + +pub fn source_is_unlockable(mut cx: FunctionContext) -> JsResult<JsBoolean> { + let current_bc_time = cx.argument::<JsNumber>(0)?.value() as u64; + let tx_issuers_js = cx.argument::<JsValue>(1)?; + let proofs = cx.argument::<JsString>(2)?.value(); + let source_written_on = cx.argument::<JsNumber>(3)?.value() as u64; + let utxo_script = cx.argument::<JsString>(4)?.value(); + + let tx_issuers_str: Vec<String> = neon_serde::from_value(&mut cx, tx_issuers_js)?; + let tx_issuers_res = tx_issuers_str + .iter() + .map(|s| ed25519::PublicKey::from_base58(s)) + .collect::<Result<SmallVec<[ed25519::PublicKey; 1]>, BaseConversionError>>(); + let tx_issuers = into_neon_res(&mut cx, tx_issuers_res.map_err(|e| format!("{}", e)))?; + + if let Ok(proofs) = dubp_documents_parser::tx_unlock_v10_from_str(&proofs) { + Ok(cx.boolean(source_is_unlockable_inner( + current_bc_time, + &proofs, + source_written_on, + &tx_issuers, + &utxo_script, + ))) + } else { + // Proofs malformated + Ok(cx.boolean(false)) + } +} + +fn source_is_unlockable_inner( + current_bc_time: u64, + proofs: &TransactionInputUnlocksV10, + source_written_on: u64, + tx_issuers: &[ed25519::PublicKey], + utxo_script: &str, +) -> bool { + if let Ok(utxo_script) = dubp_documents_parser::wallet_script_from_str(&utxo_script) { + if let Ok(unlockable_on) = SourceV10::unlockable_on( + &tx_issuers, + &proofs.unlocks, + source_written_on, + &utxo_script, + ) { + // All proofs are valid and **seem** useful + // (it is too costly to determine the minimum set of proof that is strictly necessary and sufficient). + unlockable_on <= current_bc_time + } else { + // Invalid or insufficient or too much proofs (to prevent spam with a lot of useless proofs). + false + } + } else { + // Invalid source, can never be consumed + false + } +} diff --git a/neon/native/transaction.d.ts b/neon/native/transaction.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..9c08fe470d36e4a0962897705434bfd64b3549de --- /dev/null +++ b/neon/native/transaction.d.ts @@ -0,0 +1,26 @@ +/* tslint:disable */ + +export class TransactionDTOV10 { + currency: string; + locktime: number; + hash: string; + blockstamp: string; + blockstampTime: number; + issuers: string[]; + inputs: string[]; + outputs: string[]; + unlocks: string[]; + signatures: string[]; + comment: string; +} + +export function rawTxParseAndVerify(raw: string, currency?: string): TransactionDTOV10; +export function sourceIsUnlockable( + currentBcTime: number, + txIssuers: string[], + proofs: string, + sourceWrittenOn: number, + utxoScript: string +): boolean; +export function txVerify(tx: TransactionDTOV10, currency?: string): void; +export function txsInputsAreUnlockable(currentBcTime: number, inputsConditions: string[], inputsWrittenOn: number[], tx: TransactionDTOV10): boolean; diff --git a/package-lock.json b/package-lock.json index 5d995d3aa0a8952ecf47b62b075d626128d2bcba..1b922eef5499e2fc7fbd62f9abb75a1834c270d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,11 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "10": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/10/-/10-0.0.1.tgz", + "integrity": "sha1-dpElm1QaWkKJ8+CfSjVrIr5M/yA=" + }, "@babel/code-frame": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", diff --git a/package.json b/package.json index e19f3970d3299c42440bc2cad1cc7fb3adf137f4..20e2cc5199ff30d8e87339205a21a0cdef0b9102 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "url": "https://git.duniter.org/nodes/typescript/duniter/issues" }, "dependencies": { + "10": "0.0.1", "archiver": "3.1.1", "async": "3.2.0", "bindings": "1.5.0", diff --git a/server.ts b/server.ts index 76c6004500c1d19e20ae05584587ccc50c81e3db..ee920422d4f933b3e1751ea3de88649581c7b8ee 100644 --- a/server.ts +++ b/server.ts @@ -45,6 +45,7 @@ import {LevelUp} from "levelup"; import {BMAConstants} from "./app/modules/bma/lib/constants" import {HttpMilestonePage} from "./app/modules/bma/lib/dtos" import * as toJson from "./app/modules/bma/lib/tojson" +import { rawTxParseAndVerify, txVerify } from "./neon/lib" export interface HookableServer { generatorGetJoinData: (...args:any[]) => Promise<any> @@ -309,12 +310,22 @@ export class Server extends stream.Duplex implements HookableServer { } async writeRawTransaction(raw:string): Promise<TransactionDTO> { - const obj = parsers.parseTransaction.syncWrite(raw) - return await this.writeTransaction(obj) + let tx = rawTxParseAndVerify(raw, this.conf.currency) + return await this.writeVerifiedTransaction(TransactionDTO.fromTransactionDTOV10(tx)) } - async writeTransaction(obj:any, notify = true) { - const res = await this.TransactionsService.processTx(obj) + async writeTransaction(txObj:any, notify = true) { + const tx = TransactionDTO.fromJSONObject(txObj, this.conf.currency); + try { + txVerify(tx); + return await this.writeVerifiedTransaction(tx) + } catch (e) { + throw e; + } + } + + async writeVerifiedTransaction(verifiedTx: TransactionDTO, notify = true) { + const res = await this.TransactionsService.processVerifiedTx(verifiedTx); if (notify) { this.emitDocument(res, DuniterDocument.ENTITY_TRANSACTION) } diff --git a/test/integration/documents-currency.ts b/test/integration/documents-currency.ts index 1aeb9edc33c3f6d3a86270a59074136bded3db1f..c0772ac744087c6d5cc9c2116aeb87af691caccf 100644 --- a/test/integration/documents-currency.ts +++ b/test/integration/documents-currency.ts @@ -14,6 +14,7 @@ import {NewTestingServer} from './tools/toolbox'; import {TestUser} from "./tools/TestUser" +import { assertEqual } from './tools/test-framework'; const should = require('should'); @@ -177,7 +178,7 @@ describe("Document pool currency", function() { } catch (e) { should.exist(e.error); e.should.be.an.Object(); - e.error.message.should.match(/Signature from a transaction must match/); + e.error.message.should.match(/Wrong currency/); } }) @@ -202,7 +203,12 @@ describe("Document pool currency", function() { } catch (e) { should.exist(e.error); e.should.be.an.Object(); - e.error.message.should.match(/Wrong output format/); + assertEqual(`TextDocumentParseError: Grammar error: --> 13:77 + | +13 | 1500:1:XHX(6B86B273FF34FCE19D6B804EFF5A3F5747ADA4EAA22F1D49C01E52DDB7875B4B))␊ + | ^--- + | + = expected output_cond_op_and or output_cond_op_or`, e.error.message); } }) diff --git a/test/integration/transactions/transaction-crosschain.ts b/test/integration/transactions/transaction-crosschain.ts index 2ea332e92d61197a58bfac30e4e7ca4badcb1b2e..d1fdf21ae0e28814ed04368b93571b0641690745 100644 --- a/test/integration/transactions/transaction-crosschain.ts +++ b/test/integration/transactions/transaction-crosschain.ts @@ -17,6 +17,7 @@ import {Underscore} from "../../../app/lib/common-libs/underscore" import {HttpSources} from "../../../app/modules/bma/lib/dtos" import {shouldFail, shouldNotFail} from "../../unit-tools" import {expectAnswer} from "../tools/http-expect" +import { log } from "console" const assert = require('assert'); const should = require('should'); diff --git a/test/integration/transactions/transaction-unlock-pubkey-with-leading-1.ts b/test/integration/transactions/transaction-unlock-pubkey-with-leading-1.ts new file mode 100644 index 0000000000000000000000000000000000000000..092a73df5989b085f76bf9c71d84722e88ba0ab9 --- /dev/null +++ b/test/integration/transactions/transaction-unlock-pubkey-with-leading-1.ts @@ -0,0 +1,76 @@ +// Source file from duniter: Crypto-currency software to manage libre currency such as Ğ1 +// Copyright (C) 2018 Cedric Moreau <cem.moreau@gmail.com> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +import {simpleNodeWith2Users, TestingServer} from "../tools/toolbox" +import {TestUser} from "../tools/TestUser" +import {shouldFail, shouldNotFail} from "../../unit-tools" +import { CommonConstants } from "../../../app/lib/common-libs/constants"; + +const should = require('should'); +const assert = require('assert'); + +const now = 1480000000; + +const conf = { + dt: 10, + ud0: 200, + udTime0: now - 1, // So we have a UD right on block#1 + medianTimeBlocks: 1 // Easy: medianTime(b) = time(b-1) +}; + +let s1:TestingServer, cat:TestUser, tac:TestUser + +describe("Transactions unlock with leading 1", () => { + + before(async () => { + const res = await simpleNodeWith2Users(conf); + s1 = res.s1; + cat = res.cat; + tac = res.tac; + CommonConstants.BLOCK_GENESIS_VERSION = 13; + await s1.commit({ time: now }); + await s1.commit({ time: now + 1 }); + }) + + after(() => { + //CommonConstants.DUBP_NEXT_VERSION = 10 + CommonConstants.BLOCK_GENESIS_VERSION = 10; + return Promise.all([ + s1.closeCluster() + ]) + }) + + it('it should exist block#1 with UD of 200', () => s1.expect('/blockchain/block/1', (block:any) => { + should.exists(block); + assert.equal(block.number, 1); + assert.equal(block.dividend, 200); + })); + + it('Send money to pubkey with leading 1 and consume without leading 1', async () => { + let tx1 = await cat.prepareITX(200, "1XoFs76G4yidvVY3FZBwYyLXTMjabryhFD8mNQPkQKHk"); + await shouldNotFail(cat.sendTX(tx1)); + await s1.commit({ time: now + 4 }); + let current = await s1.get('/blockchain/current'); + // Try to consume money without leading 1 + const pub43Keyring = { pub: 'XoFs76G4yidvVY3FZBwYyLXTMjabryhFD8mNQPkQKHk', sec: '2ZuLaTHpkMkUcUJCGrAYBLHffEFCtUkNbq5UYeBQUkX9suoQzt5zcxZ6Q4B21LcHnDRUVUsu1hyfa9EzKrHFmSqx'}; + const pub43 = new TestUser('pub43', pub43Keyring, { server: s1 }); + let tx2 = await pub43.prepareUTX(tx1, ['SIG(0)'], [{ qty: 200, base: 0, lock: 'SIG(' + cat.pub + ')' }], { + blockstamp: [current.number, current.hash].join('-') + }); + await shouldNotFail(pub43.sendTX(tx2)); + await s1.commit({ time: now + 5 }); + let tx3 = await cat.prepareITX(200, tac); + await shouldNotFail(cat.sendTX(tx3)); + await s1.commit({ time: now + 15 }); + }); +}) diff --git a/test/neon/test_transaction.ts b/test/neon/test_transaction.ts new file mode 100644 index 0000000000000000000000000000000000000000..ea6cd68d82b2bb3a17d571d5f66d11a16566fb2a --- /dev/null +++ b/test/neon/test_transaction.ts @@ -0,0 +1,77 @@ + +import { rawTxParseAndVerify, sourceIsUnlockable, txVerify, txsInputsAreUnlockable } from "../../neon/lib"; +import * as assert from "assert"; +import { TransactionDTOV10 } from "../../neon/native"; + +let rawTx: string; +//let txV10: TransactionDTOV10; + +describe('Transaction tests:', function(){ + + before(async () => { + rawTx = `Version: 10 +Type: Transaction +Currency: duniter_unit_test_currency +Blockstamp: 204-00003E2B8A35370BA5A7064598F628A62D4E9EC1936BE8651CE9A85F2E06981B +Locktime: 0 +Issuers: +DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV +4tNQ7d9pj2Da5wUVoW9mFn7JjuPoowF977au8DdhEjVR +FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa +Inputs: +40:2:T:6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:2 +70:2:T:3A09A20E9014110FD224889F13357BAB4EC78A72F95CA03394D8CCA2936A7435:8 +20:2:D:DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV:46 +70:2:T:A0D9B4CDC113ECE1145C5525873821398890AE842F4B318BD076095A23E70956:3 +20:2:T:67F2045B5318777CC52CD38B424F3E40DDA823FA0364625F124BABE0030E7B5B:5 +15:2:D:FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa:46 +Unlocks: +0:SIG(0) +1:XHX(7665798292) +2:SIG(0) +3:SIG(0) SIG(2) +4:SIG(0) SIG(1) SIG(2) +5:SIG(2) +Outputs: +120:2:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g) +146:2:SIG(DSz4rgncXCytsUMW2JU2yhLquZECD2XpEkpP9gG5HyAx) +49:2:(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85F2AC2FD3FA4FDC46A4FC01CA)) +Comment: -----@@@----- (why not this comment?) +kL59C1izKjcRN429AlKdshwhWbasvyL7sthI757zm1DfZTdTIctDWlKbYeG/tS7QyAgI3gcfrTHPhu1E1lKCBw== +e3LpgB2RZ/E/BCxPJsn+TDDyxGYzrIsMyDt//KhJCjIQD6pNUxr5M5jrq2OwQZgwmz91YcmoQ2XRQAUDpe4BAw== +w69bYgiQxDmCReB0Dugt9BstXlAKnwJkKCdWvCeZ9KnUCv0FJys6klzYk/O/b9t74tYhWZSX0bhETWHiwfpWBw==`; + }) + + it('rawTxParseAndVerify', function(done){ + assert.throws( + () => rawTxParseAndVerify(rawTx), + "Not same sum of inputs amount and outputs amount: (SourceAmount { amount: 235, base: 2 }, SourceAmount { amount: 315, base: 2 })" + ); + //const tx = rawTxParseAndVerify(rawTx); + //assert.equal(tx.hash, "F26C0D9D77952D62085DE5D46A760EC53026D82464D6C9F3F2DAB8953B801934") + done(); + }); + + it('rawTxParseAndVerify2', function(done){ + const tx_ = rawTxParseAndVerify(`Version: 10 +Type: Transaction +Currency: duniter_unit_test_currency +Blockstamp: 6-1903D9F03FC7E14494FFD12296382E5EB4798D214B3A2CDE5C1E0D420E040A5C +Locktime: 0 +Issuers: +HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd +Inputs: +100:0:D:HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd:3 +Unlocks: +0:SIG(0) +Outputs: +10:0:SIG(2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc) +90:0:SIG(HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd) +Comment: +sfGOC9anaIDGjbtHri+SvbD7AiIvhWOcOFu41yP+7R94Y7EvTxtR++Qa4SANihkWMtnpamEn5/KbTqu7tQrDDg== +`); + + assert.equal(tx_.signatures[0], "sfGOC9anaIDGjbtHri+SvbD7AiIvhWOcOFu41yP+7R94Y7EvTxtR++Qa4SANihkWMtnpamEn5/KbTqu7tQrDDg==") + done(); + }); +}); \ No newline at end of file diff --git a/test/unit-tools.ts b/test/unit-tools.ts index e7bbf1aba4dc7a2301f99da554d8465852055739..3d2f82c4773e2d62215d2a5751273d055c101e84 100644 --- a/test/unit-tools.ts +++ b/test/unit-tools.ts @@ -31,9 +31,9 @@ export async function shouldNotFail<T>(promise:Promise<T>) { await promise } catch(e) { let err = e; - if (typeof e === 'string') { + /*if (typeof e === 'string') { err = JSON.parse((e as any).message) - } + }*/ should.not.exist(err); } }