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..16c1b7d5 --- /dev/null +++ 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 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..c3a04a92 --- /dev/null +++ b/blockchain/src/lib/src/block.rs @@ -0,0 +1,139 @@ +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, +} + +// Structure of a Block +#[derive(Debug, Clone, Serialize, Deserialize, Hash)] +pub struct Block { + pub block_header: BlockHeader, + pub body: Body, +} + +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 { + 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() + } + + // Computes merkle root from given list of transactions + 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 + } +} + +// 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; + 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..3f78f74a --- /dev/null +++ b/blockchain/src/lib/src/chain.rs @@ -0,0 +1,145 @@ +use std::time::Duration; + +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"; + +// Structure of a Blockchain +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BlockChain { + pub blocks: Vec, +} + +impl BlockChain { + pub fn new() -> Self { + Self { blocks: vec![] } + } + + // Gives the latest block hash of the chain + pub fn tip(&self) -> String { + self.blocks + .last() + .unwrap() + .block_header + .current_hash + .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() + } + + // 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 = 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(); + let block_value = bincode::serialize(&block).unwrap(); + + db.put(block_key, block_value) + .expect("Failed to add block to database"); + }); + } + + // 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) => { + 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; // writing to disk + + info!("Block {} uploaded to database", index); + + Ok(self.clone()) + } + None => { + self.blocks.push(new_block.clone()); + Self::write_to_db(new_block).await; // writing to disk + + info!("Genesis block uploaded to database"); + + Ok(self.clone()) + } + } + } + + // 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()); + 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) + } + + //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()); + 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 + } +} + +// 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!( + 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..d3316591 --- /dev/null +++ b/blockchain/src/lib/src/node.rs @@ -0,0 +1,232 @@ +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. + // 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. + // 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 { + // 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); + 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; + } + } +} + +// Structure of a Node +pub struct Node { + 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, +} + +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(); + + // 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 + 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) { + // aborting the miner task when a block is mined and then restarted. + 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(); + + // 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 { + 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); + } + }); + } + + // 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(); + 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..e2cab4c7 --- /dev/null +++ b/blockchain/src/lib/src/query_utils.rs @@ -0,0 +1,35 @@ +use crate::transaction::{CoinbaseTxn, Txn}; +use clap::Parser; +use serde::Deserialize; +use serde::Serialize; + +// Response Variants +#[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, + }, +} + +// Request Variants +#[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..784658c1 --- /dev/null +++ b/blockchain/src/lib/src/transaction.rs @@ -0,0 +1,91 @@ +use hex::encode; +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, + 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, + } + } + + // computes Transaction id + 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 + } + + // 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(); + + 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 + } +} + +// 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, + 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..94a1b0cc --- /dev/null +++ b/blockchain/src/query.rs @@ -0,0 +1,154 @@ +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 std::time::Duration; +use tokio_util::codec::Framed; +use futures::stream::SplitSink; +use futures::SinkExt as _; +use futures::StreamExt as _; +use tokio::{ + io::{AsyncWriteExt, AsyncReadExt}, + 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, +} + +// Responsible for handling each client stream +async fn handle(mut stream: TcpStream, remote_address: SocketAddr) -> Result<()> { + let mut buffer = Vec::new(); + + stream.read_to_end(&mut buffer).await?; + + 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") { + 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 mut txn_data = Vec::new(); + + 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; + + if has_txn { + txn_data.push(txn.clone()); + } + }); + + contains + }) + .collect::>(); + + let block = block[0].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. + stream.write_all(&msg[..]).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)?; + + stream.write_all(&msg[..]).await?; + } + + // If None, 0 is sent indicating the failure of query. + None => { + stream.write_all(&[0u8]).await?; + } + }, + } + + // If reached here, either the response or zero is sent to the client. So, we log this. + 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(); + + // 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)) => { + 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..ad07d6dd --- /dev/null +++ b/blockchain/src/query_client.rs @@ -0,0 +1,77 @@ +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; + +#[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(); + + // creates address from the given port and host. + let server = SocketAddr::new(cli.address, cli.port); + + // 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!", + ); + + println!("{:?}", get_resp); +} diff --git a/blockchain/src/server.rs b/blockchain/src/server.rs new file mode 100644 index 00000000..64ec3865 --- /dev/null +++ b/blockchain/src/server.rs @@ -0,0 +1,42 @@ +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(); +} + +// Starting the node +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 +}