From c99b2cda2c89185685c48db6002905079c74f59b Mon Sep 17 00:00:00 2001 From: RajeshRk18 Date: Thu, 8 Feb 2024 23:53:49 +0530 Subject: [PATCH 1/4] add project --- blockchain/.gitignore | 1 + blockchain/Cargo.lock | 1245 +++++++++++++++++++++++++ blockchain/Cargo.toml | 32 + blockchain/README.md | 0 blockchain/src/lib/Cargo.toml | 24 + blockchain/src/lib/src/block.rs | 135 +++ blockchain/src/lib/src/chain.rs | 125 +++ blockchain/src/lib/src/lib.rs | 5 + blockchain/src/lib/src/node.rs | 223 +++++ blockchain/src/lib/src/query_utils.rs | 33 + blockchain/src/lib/src/transaction.rs | 86 ++ blockchain/src/query.rs | 136 +++ blockchain/src/query_client.rs | 62 ++ blockchain/src/server.rs | 41 + 14 files changed, 2148 insertions(+) create mode 100644 blockchain/.gitignore create mode 100644 blockchain/Cargo.lock create mode 100644 blockchain/Cargo.toml create mode 100644 blockchain/README.md create mode 100644 blockchain/src/lib/Cargo.toml create mode 100644 blockchain/src/lib/src/block.rs create mode 100644 blockchain/src/lib/src/chain.rs create mode 100644 blockchain/src/lib/src/lib.rs create mode 100644 blockchain/src/lib/src/node.rs create mode 100644 blockchain/src/lib/src/query_utils.rs create mode 100644 blockchain/src/lib/src/transaction.rs create mode 100644 blockchain/src/query.rs create mode 100644 blockchain/src/query_client.rs create mode 100644 blockchain/src/server.rs diff --git a/blockchain/.gitignore b/blockchain/.gitignore new file mode 100644 index 00000000..ea8c4bf7 --- /dev/null +++ b/blockchain/.gitignore @@ -0,0 +1 @@ +/target diff --git a/blockchain/Cargo.lock b/blockchain/Cargo.lock new file mode 100644 index 00000000..afad51ce --- /dev/null +++ b/blockchain/Cargo.lock @@ -0,0 +1,1245 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "jobserver" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "lib" +version = "0.1.0" +dependencies = [ + "anyhow", + "bincode", + "bytes", + "clap", + "futures", + "hex", + "log", + "rand", + "rocksdb", + "serde", + "serde_json", + "sha2", + "simple_logger", + "thiserror", + "tokio", + "tokio-util", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libloading" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "librocksdb-sys" +version = "0.11.0+8.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3386f101bcb4bd252d8e9d2fb41ec3b0862a15a62b478c355b2982efa469e3e" +dependencies = [ + "bindgen", + "bzip2-sys", + "cc", + "glob", + "libc", + "libz-sys", + "lz4-sys", + "zstd-sys", +] + +[[package]] +name = "libz-sys" +version = "1.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037731f5d3aaa87a5675e895b63ddff1a87624bc29f77004ea829809654e48f6" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "lz4-sys" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" + +[[package]] +name = "pow_blockchain" +version = "0.1.0" +dependencies = [ + "anyhow", + "bincode", + "clap", + "futures", + "lib", + "log", + "rocksdb", + "serde", + "simple_logger", + "tokio", + "tokio-util", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "prettyplease" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rocksdb" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6f170a4041d50a0ce04b0d2e14916d6ca863ea2e422689a5b694395d299ffe" +dependencies = [ + "libc", + "librocksdb-sys", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.113" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "simple_logger" +version = "4.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e7e46c8c90251d47d08b28b8a419ffb4aede0f87c2eea95e17d1d5bacbf3ef1" +dependencies = [ + "colored", + "log", + "time", + "windows-sys 0.48.0", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tokio" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "zstd-sys" +version = "2.0.9+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/blockchain/Cargo.toml b/blockchain/Cargo.toml new file mode 100644 index 00000000..2b4e9752 --- /dev/null +++ b/blockchain/Cargo.toml @@ -0,0 +1,32 @@ +workspace = { members = ["src/lib"] } +[package] +name = "pow_blockchain" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = {version = "4.3.4", features = ["derive"]} +bincode = "1.3.3" +log = "0.4.19" +anyhow = "1.0.71" +simple_logger = "4.1.0" +futures = "0.3.28" +tokio = {version="1.28.2", features = ["full"]} +tokio-util = {version="0.7.8", features=["codec"]} +serde = { version = "1.0", features = ["derive"] } +rocksdb = "0.21.0" +lib = { path = "./src/lib" } + +[[bin]] +name = "node" +path = "src/server.rs" + +[[bin]] +name = "query" +path = "src/query.rs" + +[[bin]] +name = "query_client" +path = "src/query_client.rs" \ No newline at end of file diff --git a/blockchain/README.md b/blockchain/README.md new file mode 100644 index 00000000..e69de29b diff --git a/blockchain/src/lib/Cargo.toml b/blockchain/src/lib/Cargo.toml new file mode 100644 index 00000000..d8a08982 --- /dev/null +++ b/blockchain/src/lib/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "lib" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bincode = "1.3.3" +bytes = "1.4.0" +clap = {version = "4.3.4", features = ["derive"]} +hex = "0.4.3" +log = "0.4.19" +rand = "0.8.5" +serde_json = "1.0.91" +sha2 = "0.10.6" +simple_logger = "4.1.0" +tokio = {version="1.28.2", features = ["full"]} +tokio-util = {version="0.7.8", features=["codec"]} +futures = "0.3.28" +serde = { version = "1.0", features = ["derive"] } +anyhow = "1.0.71" +thiserror = "1.0.40" +rocksdb = "0.21.0" \ No newline at end of file diff --git a/blockchain/src/lib/src/block.rs b/blockchain/src/lib/src/block.rs new file mode 100644 index 00000000..c7e98f00 --- /dev/null +++ b/blockchain/src/lib/src/block.rs @@ -0,0 +1,135 @@ +use crate::transaction::*; +use rand::{thread_rng, Rng}; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; + +pub const DIFFICULTY: u8 = 10; +pub static mut BLOCK_INDEX: u32 = 0; + +#[derive(Debug, Serialize, Deserialize, Clone, Hash)] +pub struct Body { + pub txn_data: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Hash)] +pub struct BlockHeader { + pub index: u32, + pub previous_hash: String, + pub timestamp: u64, + pub current_hash: String, + pub coinbase_txn: CoinbaseTxn, + pub merkle_root: String, + pub nonce: u32, + pub difficulty: u8, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Hash)] +pub struct Block { + pub block_header: BlockHeader, + pub body: Body, +} + +impl Block { + pub fn new(previous_hash: String, txn_data: Vec) -> Block { + let random = thread_rng().gen::(); + let block_header = BlockHeader { + timestamp: std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs(), + index: update_index(), + previous_hash, + current_hash: String::new(), + coinbase_txn: CoinbaseTxn::new(), + merkle_root: MerkleRoot::new(), + nonce: random, + difficulty: DIFFICULTY, + }; + + let body = Body { txn_data }; + + Block { block_header, body } + } +} + +#[derive(Debug)] +pub struct MerkleRoot; + +impl MerkleRoot { + pub fn new() -> String { + String::new() + } + + pub fn from(txns: Vec) -> String { + let mut txns = txns; + if txns.is_empty() { + let mut hasher = Sha256::new(); + hasher.update(String::default().as_bytes()); + let hash = hasher.finalize().as_slice().to_owned(); + return hex::encode(hash); + } + + if txns.len() % 2 != 0 { + txns.push(txns[txns.len() - 1].clone()); + } + + let hashed_txns = txns + .iter() + .map(|txn| { + let mut hasher = Sha256::new(); + hasher.update(&txn.sender.as_bytes()); + hasher.update(&txn.receiver.as_bytes()); + hasher.update(&txn.amount.to_string().as_bytes()); + let hash = hasher.finalize().as_slice().to_owned(); + hex::encode(hash) + }) + .collect::>(); + + Self::construct_root(hashed_txns) + } + + fn construct_root(hashed_leaves: Vec) -> String { + let mut merkle_root = String::new(); + + let mut hashes = hashed_leaves; + + while hashes.len() > 1 { + let mut nodes = hashes.clone(); + let mut parent_nodes = Vec::::new(); + for i in 0..nodes.len() / 2 { + let index = 2 * i; + let left = nodes[index].clone(); + let right = nodes[index + 1].clone(); + + let mut hasher = Sha256::new(); + hasher.update(&left.as_bytes()); + hasher.update(&right.as_bytes()); + let hash = hasher.finalize().as_slice().to_owned(); + let hash = hex::encode(hash); + parent_nodes.push(hash); + } + + nodes = parent_nodes; + if nodes.len() == 1 { + merkle_root = nodes[0].clone(); + break; + } + + if nodes.len() % 2 != 0 { + nodes.push(nodes[nodes.len() - 1].clone()); + } + + hashes = nodes; + } + + merkle_root + } +} + +fn update_index() -> u32 { + unsafe { + let pre_level = BLOCK_INDEX; + BLOCK_INDEX += 1; + pre_level + } +} diff --git a/blockchain/src/lib/src/chain.rs b/blockchain/src/lib/src/chain.rs new file mode 100644 index 00000000..54562c0f --- /dev/null +++ b/blockchain/src/lib/src/chain.rs @@ -0,0 +1,125 @@ +use crate::block::*; +use crate::transaction::*; + +use anyhow::Result; +use log::error; +use log::info; +use rocksdb::{Options, DB}; +use serde::Deserialize; +use serde::Serialize; +use sha2::{Digest as _, Sha256}; + +const DATABASE_PATH: &'static str = "/tmp/db"; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BlockChain { + pub blocks: Vec, +} + +impl BlockChain { + pub fn new() -> Self { + Self { blocks: vec![] } + } + + pub fn tip(&self) -> String { + self.blocks + .last() + .unwrap() + .block_header + .current_hash + .clone() + } + + pub fn all_blocks_in_longest_chain(&self) -> Vec { + self.blocks.clone() + } + + pub async fn write_to_db(block: Block) { + tokio::spawn(async move { + let mut opts = Options::default(); + opts.create_if_missing(true); + + let db = DB::open(&opts, DATABASE_PATH).expect("Failed to open database"); + + // Having block index as the key. + let block_key = bincode::serialize(&block.block_header.index).unwrap(); + let block_value = bincode::serialize(&block).unwrap(); + + db.put(block_key, block_value) + .expect("Failed to add block to database"); + }); + } + + pub async fn add_block(&mut self, new_block: Block) -> Result { + match self.blocks.last() { + Some(previous_block) => { + if new_block.block_header.previous_hash != previous_block.block_header.current_hash + { + error!("Block is an invalid extension of the previous blockchain state"); + } + self.blocks.push(new_block.clone()); + let index = new_block.block_header.index; + Self::write_to_db(new_block).await; + + info!("Block {} uploaded to database", index); + + Ok(self.clone()) + } + None => { + self.blocks.push(new_block.clone()); + Self::write_to_db(new_block).await; + + info!("Genesis block uploaded to database"); + + Ok(self.clone()) + } + } + } + + pub fn hash_block(block: Block) -> Vec { + let mut hasher = Sha256::new(); + hasher.update(&block.block_header.index.to_string().as_bytes()); + hasher.update(&block.block_header.previous_hash.as_bytes()); + hasher.update(&block.block_header.difficulty.to_string().as_bytes()); + hasher.update(&block.block_header.timestamp.to_string().as_bytes()); + hasher.update(&block.block_header.nonce.to_string().as_bytes()); + hasher.update(Self::hash_txn_batch(&block.body.txn_data).as_bytes()); + let hash = hasher.finalize().as_slice().to_owned(); + hash + } + + pub fn hash_txn_batch(txns: &Vec) -> String { + let mut hasher = Sha256::new(); + for txn in txns { + let txn_hash = Self::hash_txn(txn); + if txns.len() == 1 { + return txn_hash; + } + hasher.update(&txn_hash.as_bytes()); + } + + let hash = hasher.finalize().as_slice().to_owned(); + hex::encode(hash) + } + + pub fn hash_txn(txn: &Txn) -> String { + let mut hasher = Sha256::new(); + hasher.update(txn.id.as_bytes()); + hasher.update(txn.sender.as_bytes()); + hasher.update(txn.receiver.as_bytes()); + hasher.update(txn.amount.to_string().as_bytes()); + let hash = hasher.finalize().as_slice().to_owned(); + let hex_string = hex::encode(hash); + hex_string + } +} + +impl std::fmt::Display for BlockChain { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Current State:\nLatest Block: {:?}", + self.blocks.last().unwrap() + ) + } +} diff --git a/blockchain/src/lib/src/lib.rs b/blockchain/src/lib/src/lib.rs new file mode 100644 index 00000000..aee1e68b --- /dev/null +++ b/blockchain/src/lib/src/lib.rs @@ -0,0 +1,5 @@ +pub mod block; +pub mod chain; +pub mod node; +pub mod query_utils; +pub mod transaction; diff --git a/blockchain/src/lib/src/node.rs b/blockchain/src/lib/src/node.rs new file mode 100644 index 00000000..5057178a --- /dev/null +++ b/blockchain/src/lib/src/node.rs @@ -0,0 +1,223 @@ +use crate::block::*; +use crate::chain::BlockChain; +use crate::transaction::{CoinbaseTxn, Txn}; +use log::{debug, info, warn}; +use rand::{thread_rng, Rng as _}; +use sha2::{Digest as _, Sha256}; +use std::collections::HashSet; +use std::net::SocketAddr; +use tokio::sync::mpsc; +use tokio::task::JoinHandle; + +const REWARD: u8 = 50; + +pub struct Miner { + // Why task joinhandle required? + // Because we need to have a control over the miner task. + // For example, when we need to abort the mining task because another peer has already mined, + // we need to abort the mining task running in the node. + task: JoinHandle<()>, + + // Channel to send and receiver blocks. + // Having them as field so that it can be used anywhere and need not to pass it as a function argument. + block_sender: mpsc::UnboundedSender, + block_receiver: mpsc::UnboundedReceiver, +} + +impl Miner { + pub async fn mine(txns: Vec, previous_block: Block, node_address: SocketAddr) -> Block { + let merkle_root = MerkleRoot::from(txns.clone()); + let mut block = Block::new(previous_block.block_header.current_hash.clone(), txns); + block.block_header.merkle_root = merkle_root; + block.block_header.nonce = thread_rng().gen::(); + + let difficulty = block.block_header.difficulty as usize; + let target: String = vec!["0"; difficulty].join("").into(); + + debug!("Target: {}", &target); + + loop { + let block_hash = BlockChain::hash_block(block.clone()); + + let hash_to_bits = block_hash.iter().fold(String::new(), |acc, byte| { + let bits = format!("{byte:0>8b}"); + acc + bits.as_str() + }); + + if hash_to_bits.starts_with(target.as_str()) { + debug!("Bits: {}", hash_to_bits); + info!("{}", format!("Mined!⚡️")); + block.block_header.coinbase_txn.amount = REWARD; + block.block_header.coinbase_txn.validator = format!("{}", node_address); + + let mut hasher = Sha256::new(); + hasher.update(&serde_json::to_string(&block).unwrap().as_bytes()); + + let hash = hex::encode(hasher.finalize().as_slice().to_owned()); + + block.block_header.current_hash = hash; + + return block; + } + + block.block_header.nonce += 1; + } + } + + pub fn mine_genesis(node_address: SocketAddr) -> Block { + let nonce = thread_rng().gen::(); + let block_header = BlockHeader { + timestamp: std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs(), + index: 0, + previous_hash: "00000".to_string(), + current_hash: String::new(), + coinbase_txn: CoinbaseTxn::new(), + merkle_root: MerkleRoot::new(), + nonce, + difficulty: DIFFICULTY, + }; + let body = Body { txn_data: vec![] }; + + let mut block = Block { block_header, body }; + + let merkle_root = MerkleRoot::from(block.body.txn_data.clone()); + + block.block_header.merkle_root = merkle_root; + + let difficulty = block.block_header.difficulty as usize; + let target: String = vec!["0"; difficulty].join("").into(); + + loop { + let block_hash = BlockChain::hash_block(block.clone()); + + let hash_to_bits = block_hash.iter().fold(String::new(), |acc, byte| { + let bits = format!("{byte:0>8b}"); + acc + bits.as_str() + }); + + if hash_to_bits.starts_with(target.as_str()) { + info!("{}", format!("Mined genesis!👀🎉")); + block.block_header.coinbase_txn.amount = REWARD; + block.block_header.coinbase_txn.validator = format!("{}", node_address); + + let mut hasher = Sha256::new(); + hasher.update(&serde_json::to_string(&block).unwrap().as_bytes()); + + let hash = hex::encode(hasher.finalize().as_slice().to_owned()); + + block.block_header.current_hash = hash; + + return block; + } + + block.block_header.nonce += 1; + } + } +} + +pub struct Node { + address: SocketAddr, + mempool: HashSet, + state: BlockChain, + miner: Miner, +} + +impl Node { + pub async fn new(address: SocketAddr) -> Self { + let (block_sender, block_receiver) = mpsc::unbounded_channel::(); + + Self { + address, + mempool: HashSet::new(), + state: BlockChain::new(), + miner: Miner { + task: tokio::spawn(async {}), + block_sender, + block_receiver, + }, + } + } + + pub async fn run(&mut self) -> JoinHandle<()> { + self.run_miner(); + + loop { + tokio::select! { + // Receive block from miner thread + Some(block) = self.miner.block_receiver.recv() => { + info!("Block received from Miner task!"); + + info!("Block header: {:?}", block.block_header); + info!("Transactions added in the block: {:?}", block.body.txn_data); + + if let Ok(new_state) = self.state.add_block(block).await { + info!("Updating state"); + self.update_state(new_state).await; + } + } + } + } + } + + async fn update_state(&mut self, new_state: BlockChain) { + self.state = new_state; + + self.mempool.retain(|txn| { + !self + .state + .blocks + .last() + .unwrap() + .body + .txn_data + .contains(txn) + }); + + // generating random transactions + let txns = Txn::generate_txns(12); + + for txn in txns { + self.mempool.insert(txn); + } + + self.stop_and_restart().await; + } + + async fn stop_and_restart(&mut self) { + self.miner.task.abort(); + self.run_miner(); + } + + fn run_miner(&mut self) { + match self.state.blocks.last() { + Some(block) => { + info!("Restarting miner thread..."); + let block = block.clone(); + let txns = self.mempool.clone().into_iter().collect(); + let block_sender = self.miner.block_sender.clone(); + let node_address = self.address; + self.miner.task = tokio::spawn(async move { + let new_block = Miner::mine(txns, block, node_address).await; + if let Err(e) = block_sender.send(new_block) { + warn!("Can't send mined block to receiver: {}", e); + } + }); + } + + None => { + info!("Mining genesis block!"); + let block_sender = self.miner.block_sender.clone(); + let node_address = self.address; + self.miner.task = tokio::spawn(async move { + let new_block = Miner::mine_genesis(node_address); + if let Err(e) = block_sender.send(new_block) { + warn!("Can't send mined block to receiver: {}", e); + } + }); + } + } + } +} diff --git a/blockchain/src/lib/src/query_utils.rs b/blockchain/src/lib/src/query_utils.rs new file mode 100644 index 00000000..51a36f4e --- /dev/null +++ b/blockchain/src/lib/src/query_utils.rs @@ -0,0 +1,33 @@ +use crate::transaction::{CoinbaseTxn, Txn}; +use clap::Parser; +use serde::Deserialize; +use serde::Serialize; + +#[derive(Debug, Serialize, Deserialize)] +pub enum Response { + Txn { + sender: String, + receiver: String, + value: u32, + block_index: u32, + block_hash: String, + block_root: String, + validator: String, + }, + + Block { + index: u32, + timestamp: u64, + block_hash: String, + coinbase_txn: CoinbaseTxn, + merkle_root: String, + txns: Vec, + }, +} + +#[derive(Parser, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub enum Command { + Txn { id: String }, + + Block { index: u32 }, +} diff --git a/blockchain/src/lib/src/transaction.rs b/blockchain/src/lib/src/transaction.rs new file mode 100644 index 00000000..00c2c227 --- /dev/null +++ b/blockchain/src/lib/src/transaction.rs @@ -0,0 +1,86 @@ +use hex::encode; +use rand::{thread_rng, Rng}; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; + +#[derive(Debug, Clone, Default, Serialize, Deserialize, Hash, PartialEq, Eq)] +pub struct Txn { + pub id: String, + pub sender: String, + pub receiver: String, + pub amount: u32, +} + +#[allow(dead_code)] +impl Txn { + pub fn new(sender: String, receiver: String, amount: u32) -> Txn { + let id = Self::calculate_id(&sender, &receiver, &amount); + Txn { + id, + sender, + receiver, + amount, + } + } + + pub fn with_id(id: String, sender: String, receiver: String, amount: u32) -> Self { + Self { + id, + sender, + receiver, + amount, + } + } + + fn calculate_id(sender: &str, receiver: &str, amount: &u32) -> String { + let mut random = thread_rng(); + let noise = random.gen::(); + let mut hasher = Sha256::new(); + hasher.update(&sender.as_bytes()); + hasher.update(&receiver.as_bytes()); + hasher.update(&amount.to_string().as_bytes()); + hasher.update(&noise.to_string().as_bytes()); + let hash = hasher.finalize().as_slice().to_owned(); + let hash = hex::encode(hash); + hash + } + + pub fn generate_txns(num: usize) -> Vec { + let mut rng = thread_rng(); + + let txns = (0..num) + .map(|_| { + let mut sen = [0u8; 32]; + let mut rec = [0u8; 32]; + + rng.fill(&mut sen[..]); + rng.fill(&mut rec[..]); + + let value = rng.gen::(); + + Self::new( + format!("0x{}", encode(sen)), + format!("0x{}", encode(rec)), + value, + ) + }) + .collect::>(); + + txns + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)] +pub struct CoinbaseTxn { + pub amount: u8, + pub validator: String, +} + +impl CoinbaseTxn { + pub fn new() -> Self { + Self { + amount: 0, + validator: String::new(), + } + } +} diff --git a/blockchain/src/query.rs b/blockchain/src/query.rs new file mode 100644 index 00000000..d2bc321f --- /dev/null +++ b/blockchain/src/query.rs @@ -0,0 +1,136 @@ +const DB_PATH: &'static str = "/tmp/db"; + +use anyhow::Result; +use clap::Parser; +use lib::block::Block; +use lib::query_utils::{Command, Response}; +use log::info; +use rocksdb::DB; +use tokio_util::codec::LengthDelimitedCodec; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use tokio_util::codec::Framed; +use futures::stream::SplitSink; +use futures::SinkExt as _; +use futures::StreamExt as _; +use tokio::{ + io::AsyncWriteExt, + net::{TcpListener, TcpStream}, +}; + +#[derive(Parser)] +#[clap( + author = "Rajesh", + version = "0.1.0", + about = "CLI utility to query blockchain" +)] +struct Cli { + #[clap(short, long, value_parser, value_name = "NUM", default_value_t = 7291)] + port: u16, + + #[clap(short, long, value_parser, value_name="NUM", default_value_t=IpAddr::V4(Ipv4Addr::LOCALHOST))] + address: IpAddr, +} + +async fn handle(stream: TcpStream, remote_address: SocketAddr) -> Result<()> { + let (mut writer, mut reader) = Framed::new(stream, LengthDelimitedCodec::new()).split(); + + let buffer = reader.next().await.unwrap().unwrap().freeze(); + + let db = DB::open_default(DB_PATH).expect("Cannot open database"); + + info!("Opening database..."); + + match bincode::deserialize(&buffer).expect("Failed to deserialize the request") { + Command::Txn { id } => { + info!("Received txn query from client: {}", remote_address); + let mut blocks = Vec::new(); + let mut block_index: u32 = 0; + + while let Some(block) = db.get(bincode::serialize(&block_index).unwrap())? { + let block: Block = bincode::deserialize(&block)?; + blocks.push(block); + block_index += 1; + } + + let block = blocks + .into_iter() + .filter(|block| { + let mut contains = false; + + let _ = block.body.txn_data.iter().map(|txn| { + let has_txn = txn.id == id; + contains |= has_txn; + }); + + contains + }) + .collect::>(); + + let block = block[0].clone(); + + let response = Response::Block { + index: block.block_header.index, + block_hash: block.block_header.current_hash.clone(), + timestamp: block.block_header.timestamp, + coinbase_txn: block.block_header.coinbase_txn.clone(), + merkle_root: block.block_header.merkle_root.clone(), + txns: block.body.txn_data.clone(), + }; + + let msg = bincode::serialize(&response)?; + + writer.send(msg.into()).await?; + } + + Command::Block { index } => match db.get(bincode::serialize(&index).unwrap())? { + Some(bytes) => { + info!("Received block query from client: {}", remote_address); + let block: Block = bincode::deserialize(&bytes)?; + let response = Response::Block { + index: block.block_header.index, + block_hash: block.block_header.current_hash.clone(), + timestamp: block.block_header.timestamp, + coinbase_txn: block.block_header.coinbase_txn.clone(), + merkle_root: block.block_header.merkle_root.clone(), + txns: block.body.txn_data.clone(), + }; + + let msg = bincode::serialize(&response)?; + + writer.send(msg.into()).await?; + } + None => { + writer.send(vec![0u8].into()).await?; + } + }, + } + + info!("Responded to {}", remote_address); + + Ok(()) +} + +#[tokio::main] +async fn main() { + simple_logger::SimpleLogger::new().env().init().unwrap(); + + let cli = Cli::parse(); + + let server_address = SocketAddr::new(cli.address, cli.port); + + let listener = TcpListener::bind(server_address).await.unwrap(); + + loop { + match listener.accept().await { + Ok((stream, address)) => { + tokio::spawn(handle(stream, address)); + }, + + // Just ignore the connection + Err(_) => { + continue; + } + } + + } +} diff --git a/blockchain/src/query_client.rs b/blockchain/src/query_client.rs new file mode 100644 index 00000000..d0e52b83 --- /dev/null +++ b/blockchain/src/query_client.rs @@ -0,0 +1,62 @@ +use clap::Parser; + +use lib::query_utils::{Command, Response}; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use tokio::io::AsyncReadExt; +use tokio::io::AsyncWriteExt; +use tokio::net::TcpStream; +use log::info; +use anyhow::bail; + +#[derive(Parser)] +#[clap( + author = "Rajesh", + version = "0.1.0", + about = "CLI utility to query blockchain" +)] +struct Cli { + #[clap(subcommand)] + command: Command, + + #[clap(short, long, value_parser, value_name = "NUM", default_value_t = 7291)] + port: u16, + + #[clap(short, long, value_parser, value_name="NUM", default_value_t=IpAddr::V4(Ipv4Addr::LOCALHOST))] + address: IpAddr, +} + +#[tokio::main] +async fn main() { + simple_logger::SimpleLogger::new().env().init().unwrap(); + + let cli = Cli::parse(); + + let server = SocketAddr::new(cli.address, cli.port); + + let mut stream = TcpStream::connect(server).await.unwrap(); + + info!("connected to query server: {}", server); + + let message = bincode::serialize(&cli.command).expect("Failed to deserialize message"); + + + if let Err(err) = stream.write_all(&message).await { + eprintln!("Failed to send request message to node: {}", err); + } + + info!("Sent request"); + + let mut response = Vec::new(); + + if let Err(err) = stream.read_to_end(&mut response).await { + eprintln!("Failed to receive response: {}", err); + } + + info!("Received response"); + + let get_resp: Response = bincode::deserialize(&response).expect( + "Failed to deserialize the response. Maybe the data you requested is not available!", + ); + + println!("{:?}", get_resp); +} diff --git a/blockchain/src/server.rs b/blockchain/src/server.rs new file mode 100644 index 00000000..7454e142 --- /dev/null +++ b/blockchain/src/server.rs @@ -0,0 +1,41 @@ +use lib::node::Node; + +use clap::Parser; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use tokio::task::JoinHandle; + +#[derive(Parser)] +#[clap( + author = "Rajesh", + version = "0.1.0", + about = "CLI utility for nodes to respond to client requests" +)] +struct Cli { + #[clap(short, long, value_parser, value_name = "NUM", default_value_t = 1730)] + port: u16, + + #[clap(short, long, value_parser, value_name="NUM", default_value_t=IpAddr::V4(Ipv4Addr::LOCALHOST))] + address: IpAddr, +} + +#[tokio::main] +async fn main() { + simple_logger::SimpleLogger::new().env().init().unwrap(); + + let cli = Cli::parse(); + + let server_address = SocketAddr::new(cli.address, cli.port); + + let server = init_node(server_address).await; + + server.await.unwrap(); +} + +async fn init_node(server: SocketAddr) -> JoinHandle<()> { + let mut node = Node::new(server).await; + let node_handle = tokio::spawn(async move { + node.run().await; + }); + + node_handle +} From 25a84df0b0ff7fb40cde5f23a25caed3bced8405 Mon Sep 17 00:00:00 2001 From: Rajesh Kanna Date: Mon, 12 Feb 2024 18:24:17 +0530 Subject: [PATCH 2/4] add readme --- blockchain/README.md | 66 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/blockchain/README.md b/blockchain/README.md index e69de29b..16c1b7d5 100644 --- a/blockchain/README.md +++ b/blockchain/README.md @@ -0,0 +1,66 @@ +## Implementation of a blockchain based on pows consensus + +- This blockchain operates on a proof-of-work mechanism, employing dedicated threads for critical tasks such as mining and updating the database with the latest state (i.e., the most recent block). This design enables asynchronous computation and significantly improves performance of blockchain node. + +- Logging is configured to capture information at all levels (debug, info, warn) to monitor the blockchain node, query server and query client. + +- A transaction, or a block can be queried using the transaction id and the block index respectively using the query server which binds to a separate port listening for requests and assigns the separate thread for each query making the server highly scalable and performant. + +### How to run? + +#### Run a Node + +```rust +cargo run --bin node +``` + +This runs with the default address for node which is 127.0.0.1:1730. + +To run the node with port of your choice, + +```rust +cargo run --bin node -- -p +``` + +#### Run the query server + +```rust +cargo run --bin query +``` + +This runs with the default address for query server which is 127.0.0.1:7291 + +To run the server with port of your choice, + +```rust +cargo run --bin query -- -p +``` + +#### Run the client (with query) + +```rust +cargo run --bin query_client +``` + +This runs by connecting with the default query server port. So if you are running the query server with the port of your choice, + +```rust +cargo run --bin query_client -- -p +``` + +- To query a transaction, + + ```rust + cargo run --bin query_client -- txn + ``` +- To query a block, + + ```rust + cargo run --bin query_client -- block + ``` + +### Limitations + +- It randomly generates transactions and add to the block. As project submission is time constrained, I cannot add the functionality of accepting transactions from say, a client which is just another port. + +- Also, this blockchain can only be run by a single node. That is, peer-to-peer communication is not enabled for the same reason due to time constraint. \ No newline at end of file From 928a079280bb5a428685b53bbb1cc0ce34ce604f Mon Sep 17 00:00:00 2001 From: Rajesh Kanna Date: Tue, 20 Feb 2024 23:53:44 +0530 Subject: [PATCH 3/4] add comments --- blockchain/src/lib/src/block.rs | 4 ++ blockchain/src/lib/src/chain.rs | 19 +++++++++- blockchain/src/lib/src/node.rs | 15 ++++++-- blockchain/src/lib/src/query_utils.rs | 2 + blockchain/src/lib/src/transaction.rs | 5 +++ blockchain/src/query.rs | 54 ++++++++++++++++++--------- blockchain/src/query_client.rs | 25 ++++++++++--- blockchain/src/server.rs | 1 + 8 files changed, 98 insertions(+), 27 deletions(-) diff --git a/blockchain/src/lib/src/block.rs b/blockchain/src/lib/src/block.rs index c7e98f00..c3a04a92 100644 --- a/blockchain/src/lib/src/block.rs +++ b/blockchain/src/lib/src/block.rs @@ -23,6 +23,7 @@ pub struct BlockHeader { pub difficulty: u8, } +// Structure of a Block #[derive(Debug, Clone, Serialize, Deserialize, Hash)] pub struct Block { pub block_header: BlockHeader, @@ -30,6 +31,7 @@ pub struct Block { } impl Block { + // Construct a new block with given transactions. pub fn new(previous_hash: String, txn_data: Vec) -> Block { let random = thread_rng().gen::(); let block_header = BlockHeader { @@ -60,6 +62,7 @@ impl MerkleRoot { String::new() } + // Computes merkle root from given list of transactions pub fn from(txns: Vec) -> String { let mut txns = txns; if txns.is_empty() { @@ -126,6 +129,7 @@ impl MerkleRoot { } } +// Increments index everytime a new block is created. Wrapped by unsafe because mutating a static value is unsafe. fn update_index() -> u32 { unsafe { let pre_level = BLOCK_INDEX; diff --git a/blockchain/src/lib/src/chain.rs b/blockchain/src/lib/src/chain.rs index 54562c0f..21b7d758 100644 --- a/blockchain/src/lib/src/chain.rs +++ b/blockchain/src/lib/src/chain.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use crate::block::*; use crate::transaction::*; @@ -11,6 +13,7 @@ use sha2::{Digest as _, Sha256}; const DATABASE_PATH: &'static str = "/tmp/db"; +// Structure of a Blockchain #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BlockChain { pub blocks: Vec, @@ -21,6 +24,7 @@ impl BlockChain { Self { blocks: vec![] } } + // Gives the latest block hash of the chain pub fn tip(&self) -> String { self.blocks .last() @@ -34,12 +38,23 @@ impl BlockChain { self.blocks.clone() } + // Writes the latest mined block to the databse for persistent storage. pub async fn write_to_db(block: Block) { tokio::spawn(async move { let mut opts = Options::default(); opts.create_if_missing(true); - let db = DB::open(&opts, DATABASE_PATH).expect("Failed to open database"); + let db = loop { + match DB::open(&opts, DATABASE_PATH) { + Ok(db) => { + break db; + } + Err(e) => { + tokio::time::sleep(Duration::from_millis(100)).await; + continue; + } + } + }; // Having block index as the key. let block_key = bincode::serialize(&block.block_header.index).unwrap(); @@ -50,6 +65,7 @@ impl BlockChain { }); } + // Appends latest block to the chain and writes the new block to database. pub async fn add_block(&mut self, new_block: Block) -> Result { match self.blocks.last() { Some(previous_block) => { @@ -114,6 +130,7 @@ impl BlockChain { } } +// Displays the latest state of the blockchain. impl std::fmt::Display for BlockChain { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( diff --git a/blockchain/src/lib/src/node.rs b/blockchain/src/lib/src/node.rs index 5057178a..86f92f2a 100644 --- a/blockchain/src/lib/src/node.rs +++ b/blockchain/src/lib/src/node.rs @@ -25,6 +25,7 @@ pub struct Miner { } impl Miner { + // Attempts to find a nonce that is required to achieve the target. pub async fn mine(txns: Vec, previous_block: Block, node_address: SocketAddr) -> Block { let merkle_root = MerkleRoot::from(txns.clone()); let mut block = Block::new(previous_block.block_header.current_hash.clone(), txns); @@ -118,10 +119,11 @@ impl Miner { } } +// Structure of a Node pub struct Node { - address: SocketAddr, - mempool: HashSet, - state: BlockChain, + address: SocketAddr, // address of the node + mempool: HashSet, // when a transaction is created, it is added to the mempool. Miner task picks up transactions from the mempool. + state: BlockChain, // local state copy of the node miner: Miner, } @@ -144,6 +146,8 @@ impl Node { pub async fn run(&mut self) -> JoinHandle<()> { self.run_miner(); + // Why select macro is used? + // If we have many more tasks running, then select will look for any task that has been completed loop { tokio::select! { // Receive block from miner thread @@ -196,7 +200,11 @@ impl Node { Some(block) => { info!("Restarting miner thread..."); let block = block.clone(); + + // getting transactions from the mempool let txns = self.mempool.clone().into_iter().collect(); + + // cloning this to send to miner thread. If found valid nonce, it is used to send the mined block to main thread. let block_sender = self.miner.block_sender.clone(); let node_address = self.address; self.miner.task = tokio::spawn(async move { @@ -207,6 +215,7 @@ impl Node { }); } + // If None, then there is no block before. So, genesis block is mined. None => { info!("Mining genesis block!"); let block_sender = self.miner.block_sender.clone(); diff --git a/blockchain/src/lib/src/query_utils.rs b/blockchain/src/lib/src/query_utils.rs index 51a36f4e..e2cab4c7 100644 --- a/blockchain/src/lib/src/query_utils.rs +++ b/blockchain/src/lib/src/query_utils.rs @@ -3,6 +3,7 @@ use clap::Parser; use serde::Deserialize; use serde::Serialize; +// Response Variants #[derive(Debug, Serialize, Deserialize)] pub enum Response { Txn { @@ -25,6 +26,7 @@ pub enum Response { }, } +// Request Variants #[derive(Parser, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub enum Command { Txn { id: String }, diff --git a/blockchain/src/lib/src/transaction.rs b/blockchain/src/lib/src/transaction.rs index 00c2c227..784658c1 100644 --- a/blockchain/src/lib/src/transaction.rs +++ b/blockchain/src/lib/src/transaction.rs @@ -3,6 +3,7 @@ use rand::{thread_rng, Rng}; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; +// Structure of a transaction #[derive(Debug, Clone, Default, Serialize, Deserialize, Hash, PartialEq, Eq)] pub struct Txn { pub id: String, @@ -23,6 +24,7 @@ impl Txn { } } + // computes Transaction id pub fn with_id(id: String, sender: String, receiver: String, amount: u32) -> Self { Self { id, @@ -45,6 +47,8 @@ impl Txn { hash } + // As there is no functionality for a client to send transactions to a node (because of the limited time for submission), + // random transactions are generated pub fn generate_txns(num: usize) -> Vec { let mut rng = thread_rng(); @@ -70,6 +74,7 @@ impl Txn { } } +// Structure of a Coinbase transaction. It is usually the local node. #[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)] pub struct CoinbaseTxn { pub amount: u8, diff --git a/blockchain/src/query.rs b/blockchain/src/query.rs index d2bc321f..a3845320 100644 --- a/blockchain/src/query.rs +++ b/blockchain/src/query.rs @@ -8,6 +8,7 @@ use log::info; use rocksdb::DB; use tokio_util::codec::LengthDelimitedCodec; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::time::Duration; use tokio_util::codec::Framed; use futures::stream::SplitSink; use futures::SinkExt as _; @@ -31,16 +32,27 @@ struct Cli { address: IpAddr, } -async fn handle(stream: TcpStream, remote_address: SocketAddr) -> Result<()> { - let (mut writer, mut reader) = Framed::new(stream, LengthDelimitedCodec::new()).split(); +// Responsible for handling each client stream +async fn handle(mut stream: TcpStream, remote_address: SocketAddr) -> Result<()> { + let mut buffer = Vec::new(); - let buffer = reader.next().await.unwrap().unwrap().freeze(); + stream.read_to_end(&mut buffer).await?; - let db = DB::open_default(DB_PATH).expect("Cannot open database"); + let db = loop { + match DB::open_default(DB_PATH) { + Ok(db) => { + break db; + }, + Err(_) => { + tokio::time::sleep(Duration::from_millis(100)).await; + continue; + } + } + }; info!("Opening database..."); - match bincode::deserialize(&buffer).expect("Failed to deserialize the request") { + match bincode::deserialize(&buffer[..]).expect("Failed to deserialize the request") { Command::Txn { id } => { info!("Received txn query from client: {}", remote_address); let mut blocks = Vec::new(); @@ -52,6 +64,8 @@ async fn handle(stream: TcpStream, remote_address: SocketAddr) -> Result<()> { block_index += 1; } + let mut txn_data = Vec::new(); + let block = blocks .into_iter() .filter(|block| { @@ -60,6 +74,10 @@ async fn handle(stream: TcpStream, remote_address: SocketAddr) -> Result<()> { let _ = block.body.txn_data.iter().map(|txn| { let has_txn = txn.id == id; contains |= has_txn; + + if has_txn { + txn_data.push(txn.clone()); + } }); contains @@ -67,18 +85,13 @@ async fn handle(stream: TcpStream, remote_address: SocketAddr) -> Result<()> { .collect::>(); let block = block[0].clone(); - - let response = Response::Block { - index: block.block_header.index, - block_hash: block.block_header.current_hash.clone(), - timestamp: block.block_header.timestamp, - coinbase_txn: block.block_header.coinbase_txn.clone(), - merkle_root: block.block_header.merkle_root.clone(), - txns: block.body.txn_data.clone(), - }; - + let txn = txn_data[0].clone(); + let response = Response::Txn { sender: txn.sender.clone(), receiver: txn.receiver.clone(), value: txn.amount, block_index: block.block_header.index, block_hash: block.block_header.current_hash.clone(), block_root: block.block_header.merkle_root.clone(), validator: block.block_header.coinbase_txn.validator.clone() }; + + // serializes the response let msg = bincode::serialize(&response)?; - + + // writes response into the client stream. writer.send(msg.into()).await?; } @@ -97,14 +110,17 @@ async fn handle(stream: TcpStream, remote_address: SocketAddr) -> Result<()> { let msg = bincode::serialize(&response)?; - writer.send(msg.into()).await?; + stream.write_all(msg.into()).await?; } + + // If None, 0 is sent indicating the failure of query. None => { - writer.send(vec![0u8].into()).await?; + stream.write_all(vec![0u8].into()).await?; } }, } + // If reached here, either the response or zero is sent to the client. So, we log this. info!("Responded to {}", remote_address); Ok(()) @@ -120,6 +136,8 @@ async fn main() { let listener = TcpListener::bind(server_address).await.unwrap(); + // Listening for new connection. Will create a separate thread for each connection and handles the request accordingly. + // This is what makes the server more scalable and can handle massive connections smoothly. loop { match listener.accept().await { Ok((stream, address)) => { diff --git a/blockchain/src/query_client.rs b/blockchain/src/query_client.rs index d0e52b83..ad07d6dd 100644 --- a/blockchain/src/query_client.rs +++ b/blockchain/src/query_client.rs @@ -1,12 +1,12 @@ use clap::Parser; +use anyhow::bail; use lib::query_utils::{Command, Response}; +use log::info; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use tokio::io::AsyncReadExt; use tokio::io::AsyncWriteExt; use tokio::net::TcpStream; -use log::info; -use anyhow::bail; #[derive(Parser)] #[clap( @@ -31,29 +31,44 @@ async fn main() { let cli = Cli::parse(); + // creates address from the given port and host. let server = SocketAddr::new(cli.address, cli.port); - let mut stream = TcpStream::connect(server).await.unwrap(); + // Attempts to connect to the server. If connection cannot be made, it retries every 100ms. + let mut stream = loop { + match TcpStream::connect(server).await { + Ok(stream) => { + break stream; + } + + Err(_) => { + tokio::time::sleep(Duration::from_millis(100)).await; + continue; + } + } + }; info!("connected to query server: {}", server); let message = bincode::serialize(&cli.command).expect("Failed to deserialize message"); - + // Sends the request message to the query server. if let Err(err) = stream.write_all(&message).await { eprintln!("Failed to send request message to node: {}", err); } info!("Sent request"); - + let mut response = Vec::new(); + // Reads response from the server stream. if let Err(err) = stream.read_to_end(&mut response).await { eprintln!("Failed to receive response: {}", err); } info!("Received response"); + // Attempts to deserialize the response. let get_resp: Response = bincode::deserialize(&response).expect( "Failed to deserialize the response. Maybe the data you requested is not available!", ); diff --git a/blockchain/src/server.rs b/blockchain/src/server.rs index 7454e142..64ec3865 100644 --- a/blockchain/src/server.rs +++ b/blockchain/src/server.rs @@ -31,6 +31,7 @@ async fn main() { server.await.unwrap(); } +// Starting the node async fn init_node(server: SocketAddr) -> JoinHandle<()> { let mut node = Node::new(server).await; let node_handle = tokio::spawn(async move { From 747808f06916d1b151f120330129f57341728e09 Mon Sep 17 00:00:00 2001 From: RajeshRk18 Date: Wed, 21 Feb 2024 14:02:52 +0530 Subject: [PATCH 4/4] add comments --- blockchain/src/lib/src/chain.rs | 7 +++++-- blockchain/src/lib/src/node.rs | 4 ++-- blockchain/src/query.rs | 8 ++++---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/blockchain/src/lib/src/chain.rs b/blockchain/src/lib/src/chain.rs index 21b7d758..3f78f74a 100644 --- a/blockchain/src/lib/src/chain.rs +++ b/blockchain/src/lib/src/chain.rs @@ -34,6 +34,7 @@ impl BlockChain { .clone() } + // gives the local copy of the blockchain as there is only one node. pub fn all_blocks_in_longest_chain(&self) -> Vec { self.blocks.clone() } @@ -75,7 +76,7 @@ impl BlockChain { } self.blocks.push(new_block.clone()); let index = new_block.block_header.index; - Self::write_to_db(new_block).await; + Self::write_to_db(new_block).await; // writing to disk info!("Block {} uploaded to database", index); @@ -83,7 +84,7 @@ impl BlockChain { } None => { self.blocks.push(new_block.clone()); - Self::write_to_db(new_block).await; + Self::write_to_db(new_block).await; // writing to disk info!("Genesis block uploaded to database"); @@ -92,6 +93,7 @@ impl BlockChain { } } + // computes block hash: SHA256(index || previous hash || difficulty || timestamp || nonce || txns) pub fn hash_block(block: Block) -> Vec { let mut hasher = Sha256::new(); hasher.update(&block.block_header.index.to_string().as_bytes()); @@ -118,6 +120,7 @@ impl BlockChain { hex::encode(hash) } + //computes transaction hash: SHA256(id || sender || receiver || amount) pub fn hash_txn(txn: &Txn) -> String { let mut hasher = Sha256::new(); hasher.update(txn.id.as_bytes()); diff --git a/blockchain/src/lib/src/node.rs b/blockchain/src/lib/src/node.rs index 86f92f2a..d3316591 100644 --- a/blockchain/src/lib/src/node.rs +++ b/blockchain/src/lib/src/node.rs @@ -14,8 +14,7 @@ const REWARD: u8 = 50; pub struct Miner { // Why task joinhandle required? // Because we need to have a control over the miner task. - // For example, when we need to abort the mining task because another peer has already mined, - // we need to abort the mining task running in the node. + // Whenever a new block is mined, we use join handle to abort the task and restart to mine new block task: JoinHandle<()>, // Channel to send and receiver blocks. @@ -191,6 +190,7 @@ impl Node { } async fn stop_and_restart(&mut self) { + // aborting the miner task when a block is mined and then restarted. self.miner.task.abort(); self.run_miner(); } diff --git a/blockchain/src/query.rs b/blockchain/src/query.rs index a3845320..94a1b0cc 100644 --- a/blockchain/src/query.rs +++ b/blockchain/src/query.rs @@ -14,7 +14,7 @@ use futures::stream::SplitSink; use futures::SinkExt as _; use futures::StreamExt as _; use tokio::{ - io::AsyncWriteExt, + io::{AsyncWriteExt, AsyncReadExt}, net::{TcpListener, TcpStream}, }; @@ -92,7 +92,7 @@ async fn handle(mut stream: TcpStream, remote_address: SocketAddr) -> Result<()> let msg = bincode::serialize(&response)?; // writes response into the client stream. - writer.send(msg.into()).await?; + stream.write_all(&msg[..]).await?; } Command::Block { index } => match db.get(bincode::serialize(&index).unwrap())? { @@ -110,12 +110,12 @@ async fn handle(mut stream: TcpStream, remote_address: SocketAddr) -> Result<()> let msg = bincode::serialize(&response)?; - stream.write_all(msg.into()).await?; + stream.write_all(&msg[..]).await?; } // If None, 0 is sent indicating the failure of query. None => { - stream.write_all(vec![0u8].into()).await?; + stream.write_all(&[0u8]).await?; } }, }