├── .github └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Readme.md ├── data ├── blocks │ ├── conf │ └── db └── wallets │ ├── conf │ └── db └── src ├── block.rs ├── blockchain.rs ├── cli.rs ├── errors.rs ├── main.rs ├── server.rs ├── transaction.rs ├── txn.rs ├── utxoset.rs └── wallet.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | /src/data 4 | /data -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.19.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "1.0.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "anstream" 31 | version = "0.3.2" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" 34 | dependencies = [ 35 | "anstyle", 36 | "anstyle-parse", 37 | "anstyle-query", 38 | "anstyle-wincon", 39 | "colorchoice", 40 | "is-terminal", 41 | "utf8parse", 42 | ] 43 | 44 | [[package]] 45 | name = "anstyle" 46 | version = "1.0.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" 49 | 50 | [[package]] 51 | name = "anstyle-parse" 52 | version = "0.2.0" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" 55 | dependencies = [ 56 | "utf8parse", 57 | ] 58 | 59 | [[package]] 60 | name = "anstyle-query" 61 | version = "1.0.0" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" 64 | dependencies = [ 65 | "windows-sys", 66 | ] 67 | 68 | [[package]] 69 | name = "anstyle-wincon" 70 | version = "1.0.1" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" 73 | dependencies = [ 74 | "anstyle", 75 | "windows-sys", 76 | ] 77 | 78 | [[package]] 79 | name = "atty" 80 | version = "0.2.14" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 83 | dependencies = [ 84 | "hermit-abi 0.1.19", 85 | "libc", 86 | "winapi", 87 | ] 88 | 89 | [[package]] 90 | name = "autocfg" 91 | version = "1.1.0" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 94 | 95 | [[package]] 96 | name = "backtrace" 97 | version = "0.3.67" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" 100 | dependencies = [ 101 | "addr2line", 102 | "cc", 103 | "cfg-if", 104 | "libc", 105 | "miniz_oxide", 106 | "object", 107 | "rustc-demangle", 108 | ] 109 | 110 | [[package]] 111 | name = "bincode" 112 | version = "1.3.3" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 115 | dependencies = [ 116 | "serde", 117 | ] 118 | 119 | [[package]] 120 | name = "bitcoin_hashes" 121 | version = "0.7.6" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "b375d62f341cef9cd9e77793ec8f1db3fc9ce2e4d57e982c8fe697a2c16af3b6" 124 | 125 | [[package]] 126 | name = "bitcoincash-addr" 127 | version = "0.5.2" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "ad79afbfd27efc52fc928b198a365a7ee9da8d881a18c16d88764880b675e543" 130 | dependencies = [ 131 | "bitcoin_hashes", 132 | ] 133 | 134 | [[package]] 135 | name = "bitflags" 136 | version = "1.3.2" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 139 | 140 | [[package]] 141 | name = "block-buffer" 142 | version = "0.10.4" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 145 | dependencies = [ 146 | "generic-array", 147 | ] 148 | 149 | [[package]] 150 | name = "blockchain_rust" 151 | version = "0.1.0" 152 | dependencies = [ 153 | "bincode", 154 | "bitcoincash-addr", 155 | "clap", 156 | "colored", 157 | "env_logger", 158 | "failure", 159 | "log", 160 | "merkle-cbt", 161 | "rand 0.8.5", 162 | "rust-crypto", 163 | "serde", 164 | "serde_json", 165 | "sha2", 166 | "sled", 167 | ] 168 | 169 | [[package]] 170 | name = "byteorder" 171 | version = "1.4.3" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 174 | 175 | [[package]] 176 | name = "cc" 177 | version = "1.0.79" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" 180 | 181 | [[package]] 182 | name = "cfg-if" 183 | version = "1.0.0" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 186 | 187 | [[package]] 188 | name = "clap" 189 | version = "4.2.7" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938" 192 | dependencies = [ 193 | "clap_builder", 194 | ] 195 | 196 | [[package]] 197 | name = "clap_builder" 198 | version = "4.2.7" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd" 201 | dependencies = [ 202 | "anstream", 203 | "anstyle", 204 | "bitflags", 205 | "clap_lex", 206 | "strsim", 207 | ] 208 | 209 | [[package]] 210 | name = "clap_lex" 211 | version = "0.4.1" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" 214 | 215 | [[package]] 216 | name = "colorchoice" 217 | version = "1.0.0" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 220 | 221 | [[package]] 222 | name = "colored" 223 | version = "2.0.0" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" 226 | dependencies = [ 227 | "atty", 228 | "lazy_static", 229 | "winapi", 230 | ] 231 | 232 | [[package]] 233 | name = "cpufeatures" 234 | version = "0.2.7" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" 237 | dependencies = [ 238 | "libc", 239 | ] 240 | 241 | [[package]] 242 | name = "crc32fast" 243 | version = "1.3.2" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 246 | dependencies = [ 247 | "cfg-if", 248 | ] 249 | 250 | [[package]] 251 | name = "crossbeam-epoch" 252 | version = "0.9.14" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" 255 | dependencies = [ 256 | "autocfg", 257 | "cfg-if", 258 | "crossbeam-utils", 259 | "memoffset", 260 | "scopeguard", 261 | ] 262 | 263 | [[package]] 264 | name = "crossbeam-utils" 265 | version = "0.8.15" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" 268 | dependencies = [ 269 | "cfg-if", 270 | ] 271 | 272 | [[package]] 273 | name = "crypto-common" 274 | version = "0.1.6" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 277 | dependencies = [ 278 | "generic-array", 279 | "typenum", 280 | ] 281 | 282 | [[package]] 283 | name = "digest" 284 | version = "0.10.6" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" 287 | dependencies = [ 288 | "block-buffer", 289 | "crypto-common", 290 | ] 291 | 292 | [[package]] 293 | name = "env_logger" 294 | version = "0.10.0" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" 297 | dependencies = [ 298 | "humantime", 299 | "is-terminal", 300 | "log", 301 | "regex", 302 | "termcolor", 303 | ] 304 | 305 | [[package]] 306 | name = "errno" 307 | version = "0.3.1" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" 310 | dependencies = [ 311 | "errno-dragonfly", 312 | "libc", 313 | "windows-sys", 314 | ] 315 | 316 | [[package]] 317 | name = "errno-dragonfly" 318 | version = "0.1.2" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 321 | dependencies = [ 322 | "cc", 323 | "libc", 324 | ] 325 | 326 | [[package]] 327 | name = "failure" 328 | version = "0.1.8" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" 331 | dependencies = [ 332 | "backtrace", 333 | "failure_derive", 334 | ] 335 | 336 | [[package]] 337 | name = "failure_derive" 338 | version = "0.1.8" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" 341 | dependencies = [ 342 | "proc-macro2", 343 | "quote", 344 | "syn 1.0.109", 345 | "synstructure", 346 | ] 347 | 348 | [[package]] 349 | name = "fs2" 350 | version = "0.4.3" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" 353 | dependencies = [ 354 | "libc", 355 | "winapi", 356 | ] 357 | 358 | [[package]] 359 | name = "fuchsia-cprng" 360 | version = "0.1.1" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 363 | 364 | [[package]] 365 | name = "fxhash" 366 | version = "0.2.1" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" 369 | dependencies = [ 370 | "byteorder", 371 | ] 372 | 373 | [[package]] 374 | name = "gcc" 375 | version = "0.3.55" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" 378 | 379 | [[package]] 380 | name = "generic-array" 381 | version = "0.14.7" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 384 | dependencies = [ 385 | "typenum", 386 | "version_check", 387 | ] 388 | 389 | [[package]] 390 | name = "getrandom" 391 | version = "0.2.9" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" 394 | dependencies = [ 395 | "cfg-if", 396 | "libc", 397 | "wasi 0.11.0+wasi-snapshot-preview1", 398 | ] 399 | 400 | [[package]] 401 | name = "gimli" 402 | version = "0.27.2" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" 405 | 406 | [[package]] 407 | name = "hermit-abi" 408 | version = "0.1.19" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 411 | dependencies = [ 412 | "libc", 413 | ] 414 | 415 | [[package]] 416 | name = "hermit-abi" 417 | version = "0.3.1" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" 420 | 421 | [[package]] 422 | name = "humantime" 423 | version = "2.1.0" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 426 | 427 | [[package]] 428 | name = "instant" 429 | version = "0.1.12" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 432 | dependencies = [ 433 | "cfg-if", 434 | ] 435 | 436 | [[package]] 437 | name = "io-lifetimes" 438 | version = "1.0.10" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" 441 | dependencies = [ 442 | "hermit-abi 0.3.1", 443 | "libc", 444 | "windows-sys", 445 | ] 446 | 447 | [[package]] 448 | name = "is-terminal" 449 | version = "0.4.7" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" 452 | dependencies = [ 453 | "hermit-abi 0.3.1", 454 | "io-lifetimes", 455 | "rustix", 456 | "windows-sys", 457 | ] 458 | 459 | [[package]] 460 | name = "itoa" 461 | version = "1.0.6" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" 464 | 465 | [[package]] 466 | name = "lazy_static" 467 | version = "1.4.0" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 470 | 471 | [[package]] 472 | name = "libc" 473 | version = "0.2.144" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" 476 | 477 | [[package]] 478 | name = "linux-raw-sys" 479 | version = "0.3.7" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" 482 | 483 | [[package]] 484 | name = "lock_api" 485 | version = "0.4.9" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 488 | dependencies = [ 489 | "autocfg", 490 | "scopeguard", 491 | ] 492 | 493 | [[package]] 494 | name = "log" 495 | version = "0.4.17" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 498 | dependencies = [ 499 | "cfg-if", 500 | ] 501 | 502 | [[package]] 503 | name = "memchr" 504 | version = "2.5.0" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 507 | 508 | [[package]] 509 | name = "memoffset" 510 | version = "0.8.0" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" 513 | dependencies = [ 514 | "autocfg", 515 | ] 516 | 517 | [[package]] 518 | name = "merkle-cbt" 519 | version = "0.3.2" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "171d2f700835121c3b04ccf0880882987a050fd5c7ae88148abf537d33dd3a56" 522 | dependencies = [ 523 | "cfg-if", 524 | ] 525 | 526 | [[package]] 527 | name = "miniz_oxide" 528 | version = "0.6.2" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" 531 | dependencies = [ 532 | "adler", 533 | ] 534 | 535 | [[package]] 536 | name = "object" 537 | version = "0.30.3" 538 | source = "registry+https://github.com/rust-lang/crates.io-index" 539 | checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" 540 | dependencies = [ 541 | "memchr", 542 | ] 543 | 544 | [[package]] 545 | name = "parking_lot" 546 | version = "0.11.2" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" 549 | dependencies = [ 550 | "instant", 551 | "lock_api", 552 | "parking_lot_core", 553 | ] 554 | 555 | [[package]] 556 | name = "parking_lot_core" 557 | version = "0.8.6" 558 | source = "registry+https://github.com/rust-lang/crates.io-index" 559 | checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" 560 | dependencies = [ 561 | "cfg-if", 562 | "instant", 563 | "libc", 564 | "redox_syscall", 565 | "smallvec", 566 | "winapi", 567 | ] 568 | 569 | [[package]] 570 | name = "ppv-lite86" 571 | version = "0.2.17" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 574 | 575 | [[package]] 576 | name = "proc-macro2" 577 | version = "1.0.56" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" 580 | dependencies = [ 581 | "unicode-ident", 582 | ] 583 | 584 | [[package]] 585 | name = "quote" 586 | version = "1.0.27" 587 | source = "registry+https://github.com/rust-lang/crates.io-index" 588 | checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" 589 | dependencies = [ 590 | "proc-macro2", 591 | ] 592 | 593 | [[package]] 594 | name = "rand" 595 | version = "0.3.23" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" 598 | dependencies = [ 599 | "libc", 600 | "rand 0.4.6", 601 | ] 602 | 603 | [[package]] 604 | name = "rand" 605 | version = "0.4.6" 606 | source = "registry+https://github.com/rust-lang/crates.io-index" 607 | checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" 608 | dependencies = [ 609 | "fuchsia-cprng", 610 | "libc", 611 | "rand_core 0.3.1", 612 | "rdrand", 613 | "winapi", 614 | ] 615 | 616 | [[package]] 617 | name = "rand" 618 | version = "0.8.5" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 621 | dependencies = [ 622 | "libc", 623 | "rand_chacha", 624 | "rand_core 0.6.4", 625 | ] 626 | 627 | [[package]] 628 | name = "rand_chacha" 629 | version = "0.3.1" 630 | source = "registry+https://github.com/rust-lang/crates.io-index" 631 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 632 | dependencies = [ 633 | "ppv-lite86", 634 | "rand_core 0.6.4", 635 | ] 636 | 637 | [[package]] 638 | name = "rand_core" 639 | version = "0.3.1" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 642 | dependencies = [ 643 | "rand_core 0.4.2", 644 | ] 645 | 646 | [[package]] 647 | name = "rand_core" 648 | version = "0.4.2" 649 | source = "registry+https://github.com/rust-lang/crates.io-index" 650 | checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" 651 | 652 | [[package]] 653 | name = "rand_core" 654 | version = "0.6.4" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 657 | dependencies = [ 658 | "getrandom", 659 | ] 660 | 661 | [[package]] 662 | name = "rdrand" 663 | version = "0.4.0" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 666 | dependencies = [ 667 | "rand_core 0.3.1", 668 | ] 669 | 670 | [[package]] 671 | name = "redox_syscall" 672 | version = "0.2.16" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 675 | dependencies = [ 676 | "bitflags", 677 | ] 678 | 679 | [[package]] 680 | name = "regex" 681 | version = "1.8.1" 682 | source = "registry+https://github.com/rust-lang/crates.io-index" 683 | checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" 684 | dependencies = [ 685 | "aho-corasick", 686 | "memchr", 687 | "regex-syntax", 688 | ] 689 | 690 | [[package]] 691 | name = "regex-syntax" 692 | version = "0.7.1" 693 | source = "registry+https://github.com/rust-lang/crates.io-index" 694 | checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" 695 | 696 | [[package]] 697 | name = "rust-crypto" 698 | version = "0.2.36" 699 | source = "registry+https://github.com/rust-lang/crates.io-index" 700 | checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" 701 | dependencies = [ 702 | "gcc", 703 | "libc", 704 | "rand 0.3.23", 705 | "rustc-serialize", 706 | "time", 707 | ] 708 | 709 | [[package]] 710 | name = "rustc-demangle" 711 | version = "0.1.23" 712 | source = "registry+https://github.com/rust-lang/crates.io-index" 713 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 714 | 715 | [[package]] 716 | name = "rustc-serialize" 717 | version = "0.3.24" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" 720 | 721 | [[package]] 722 | name = "rustix" 723 | version = "0.37.19" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" 726 | dependencies = [ 727 | "bitflags", 728 | "errno", 729 | "io-lifetimes", 730 | "libc", 731 | "linux-raw-sys", 732 | "windows-sys", 733 | ] 734 | 735 | [[package]] 736 | name = "ryu" 737 | version = "1.0.13" 738 | source = "registry+https://github.com/rust-lang/crates.io-index" 739 | checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" 740 | 741 | [[package]] 742 | name = "scopeguard" 743 | version = "1.1.0" 744 | source = "registry+https://github.com/rust-lang/crates.io-index" 745 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 746 | 747 | [[package]] 748 | name = "serde" 749 | version = "1.0.163" 750 | source = "registry+https://github.com/rust-lang/crates.io-index" 751 | checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" 752 | dependencies = [ 753 | "serde_derive", 754 | ] 755 | 756 | [[package]] 757 | name = "serde_derive" 758 | version = "1.0.163" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" 761 | dependencies = [ 762 | "proc-macro2", 763 | "quote", 764 | "syn 2.0.16", 765 | ] 766 | 767 | [[package]] 768 | name = "serde_json" 769 | version = "1.0.96" 770 | source = "registry+https://github.com/rust-lang/crates.io-index" 771 | checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" 772 | dependencies = [ 773 | "itoa", 774 | "ryu", 775 | "serde", 776 | ] 777 | 778 | [[package]] 779 | name = "sha2" 780 | version = "0.10.6" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" 783 | dependencies = [ 784 | "cfg-if", 785 | "cpufeatures", 786 | "digest", 787 | ] 788 | 789 | [[package]] 790 | name = "sled" 791 | version = "0.34.7" 792 | source = "registry+https://github.com/rust-lang/crates.io-index" 793 | checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935" 794 | dependencies = [ 795 | "crc32fast", 796 | "crossbeam-epoch", 797 | "crossbeam-utils", 798 | "fs2", 799 | "fxhash", 800 | "libc", 801 | "log", 802 | "parking_lot", 803 | ] 804 | 805 | [[package]] 806 | name = "smallvec" 807 | version = "1.10.0" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 810 | 811 | [[package]] 812 | name = "strsim" 813 | version = "0.10.0" 814 | source = "registry+https://github.com/rust-lang/crates.io-index" 815 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 816 | 817 | [[package]] 818 | name = "syn" 819 | version = "1.0.109" 820 | source = "registry+https://github.com/rust-lang/crates.io-index" 821 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 822 | dependencies = [ 823 | "proc-macro2", 824 | "quote", 825 | "unicode-ident", 826 | ] 827 | 828 | [[package]] 829 | name = "syn" 830 | version = "2.0.16" 831 | source = "registry+https://github.com/rust-lang/crates.io-index" 832 | checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" 833 | dependencies = [ 834 | "proc-macro2", 835 | "quote", 836 | "unicode-ident", 837 | ] 838 | 839 | [[package]] 840 | name = "synstructure" 841 | version = "0.12.6" 842 | source = "registry+https://github.com/rust-lang/crates.io-index" 843 | checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" 844 | dependencies = [ 845 | "proc-macro2", 846 | "quote", 847 | "syn 1.0.109", 848 | "unicode-xid", 849 | ] 850 | 851 | [[package]] 852 | name = "termcolor" 853 | version = "1.2.0" 854 | source = "registry+https://github.com/rust-lang/crates.io-index" 855 | checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" 856 | dependencies = [ 857 | "winapi-util", 858 | ] 859 | 860 | [[package]] 861 | name = "time" 862 | version = "0.1.45" 863 | source = "registry+https://github.com/rust-lang/crates.io-index" 864 | checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" 865 | dependencies = [ 866 | "libc", 867 | "wasi 0.10.0+wasi-snapshot-preview1", 868 | "winapi", 869 | ] 870 | 871 | [[package]] 872 | name = "typenum" 873 | version = "1.16.0" 874 | source = "registry+https://github.com/rust-lang/crates.io-index" 875 | checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" 876 | 877 | [[package]] 878 | name = "unicode-ident" 879 | version = "1.0.8" 880 | source = "registry+https://github.com/rust-lang/crates.io-index" 881 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 882 | 883 | [[package]] 884 | name = "unicode-xid" 885 | version = "0.2.4" 886 | source = "registry+https://github.com/rust-lang/crates.io-index" 887 | checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" 888 | 889 | [[package]] 890 | name = "utf8parse" 891 | version = "0.2.1" 892 | source = "registry+https://github.com/rust-lang/crates.io-index" 893 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 894 | 895 | [[package]] 896 | name = "version_check" 897 | version = "0.9.4" 898 | source = "registry+https://github.com/rust-lang/crates.io-index" 899 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 900 | 901 | [[package]] 902 | name = "wasi" 903 | version = "0.10.0+wasi-snapshot-preview1" 904 | source = "registry+https://github.com/rust-lang/crates.io-index" 905 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 906 | 907 | [[package]] 908 | name = "wasi" 909 | version = "0.11.0+wasi-snapshot-preview1" 910 | source = "registry+https://github.com/rust-lang/crates.io-index" 911 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 912 | 913 | [[package]] 914 | name = "winapi" 915 | version = "0.3.9" 916 | source = "registry+https://github.com/rust-lang/crates.io-index" 917 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 918 | dependencies = [ 919 | "winapi-i686-pc-windows-gnu", 920 | "winapi-x86_64-pc-windows-gnu", 921 | ] 922 | 923 | [[package]] 924 | name = "winapi-i686-pc-windows-gnu" 925 | version = "0.4.0" 926 | source = "registry+https://github.com/rust-lang/crates.io-index" 927 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 928 | 929 | [[package]] 930 | name = "winapi-util" 931 | version = "0.1.5" 932 | source = "registry+https://github.com/rust-lang/crates.io-index" 933 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 934 | dependencies = [ 935 | "winapi", 936 | ] 937 | 938 | [[package]] 939 | name = "winapi-x86_64-pc-windows-gnu" 940 | version = "0.4.0" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 943 | 944 | [[package]] 945 | name = "windows-sys" 946 | version = "0.48.0" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 949 | dependencies = [ 950 | "windows-targets", 951 | ] 952 | 953 | [[package]] 954 | name = "windows-targets" 955 | version = "0.48.0" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" 958 | dependencies = [ 959 | "windows_aarch64_gnullvm", 960 | "windows_aarch64_msvc", 961 | "windows_i686_gnu", 962 | "windows_i686_msvc", 963 | "windows_x86_64_gnu", 964 | "windows_x86_64_gnullvm", 965 | "windows_x86_64_msvc", 966 | ] 967 | 968 | [[package]] 969 | name = "windows_aarch64_gnullvm" 970 | version = "0.48.0" 971 | source = "registry+https://github.com/rust-lang/crates.io-index" 972 | checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" 973 | 974 | [[package]] 975 | name = "windows_aarch64_msvc" 976 | version = "0.48.0" 977 | source = "registry+https://github.com/rust-lang/crates.io-index" 978 | checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" 979 | 980 | [[package]] 981 | name = "windows_i686_gnu" 982 | version = "0.48.0" 983 | source = "registry+https://github.com/rust-lang/crates.io-index" 984 | checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" 985 | 986 | [[package]] 987 | name = "windows_i686_msvc" 988 | version = "0.48.0" 989 | source = "registry+https://github.com/rust-lang/crates.io-index" 990 | checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" 991 | 992 | [[package]] 993 | name = "windows_x86_64_gnu" 994 | version = "0.48.0" 995 | source = "registry+https://github.com/rust-lang/crates.io-index" 996 | checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" 997 | 998 | [[package]] 999 | name = "windows_x86_64_gnullvm" 1000 | version = "0.48.0" 1001 | source = "registry+https://github.com/rust-lang/crates.io-index" 1002 | checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" 1003 | 1004 | [[package]] 1005 | name = "windows_x86_64_msvc" 1006 | version = "0.48.0" 1007 | source = "registry+https://github.com/rust-lang/crates.io-index" 1008 | checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" 1009 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "blockchain_rust" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | sha2 = "0.10.6" 10 | rust-crypto = "^0.2" 11 | bincode = "1.3" 12 | failure = "0.1" 13 | sled = "0.34" 14 | log = "0.4" 15 | env_logger = "0.10.0" 16 | clap = "4.0.29" 17 | bitcoincash-addr = "0.5.2" 18 | rand = "0.8.5" 19 | merkle-cbt = "0.3.2" 20 | serde = {version = "1.0", features = ["derive"] } 21 | serde_json = "1.0" 22 | colored = "2.0.0" 23 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Simple Blockchain in Rust 2 | 3 | This is a basic implementation of a blockchain in Rust. It showcases the basic principles of blockchain: creating blocks with data, adding them to the blockchain, and ensuring the integrity and validity of the chain through a simple proof-of-work mechanism. 4 | 5 | ## Installation 6 | 7 | 1. First, make sure you have Rust installed on your machine. If you don't have Rust installed, you can install it using `rustup`. Instructions for installing `rustup` can be found on the [official Rust website](https://www.rust-lang.org/tools/install). 8 | 9 | 2. Clone the repository to your local machine: 10 | 11 | ```bash 12 | git clone https://github.com/deepak-likes-code/rust-blockchain 13 | ``` 14 | 15 | 3. Navigate to the project directory and build the project: 16 | 17 | ```bash 18 | cd rust_blockchain 19 | cargo build 20 | ``` 21 | 22 | ## Usage 23 | 24 | The entry point for this application is the `cargo run` command. 25 | 26 | ## Commands 27 | 28 | The following commands are available in the CLI: 29 | 30 | 1. **printchain**: This command prints all the chain blocks. Usage: 31 | 32 | ``` 33 | cargo run printchain 34 | ``` 35 | 36 | 2. **getbalance**: This command retrieves the balance of a given address in the blockchain. Usage: 37 | 38 | ``` 39 | cargo run getbalance [ADDRESS] 40 | ``` 41 | 42 | Replace `[ADDRESS]` with the address you want to retrieve the balance for. 43 | 44 | 3. **create**: This command creates a new blockchain and sends the genesis block reward to the specified address. Usage: 45 | 46 | ``` 47 | cargo run create [ADDRESS] 48 | ``` 49 | 50 | Replace `[ADDRESS]` with the address where you want to send the genesis block reward. 51 | 52 | 4. **send**: This command sends an amount from one wallet to another in the blockchain. Usage: 53 | 54 | ``` 55 | cargo run send [FROM] [TO] [AMOUNT] 56 | ``` 57 | 58 | Replace `[FROM]` with the source wallet address, `[TO]` with the destination wallet address, and `[AMOUNT]` with the amount to send. 59 | 60 | 5. **createwallet**: This command creates a new wallet. Usage: 61 | 62 | ``` 63 | cargo run createwallet 64 | ``` 65 | 66 | 6. **listaddresses**: This command lists all wallet addresses. Usage: 67 | 68 | ``` 69 | cargo run listaddresses 70 | ``` 71 | 72 | 7. **reindex**: This command reindexes the UTXOSet. Usage: 73 | 74 | ``` 75 | cargo run reindex 76 | ``` 77 | 78 | ## Error Handling 79 | 80 | If an error occurs while executing any of the commands, the program will display a descriptive error message and exit with status 1. 81 | 82 | ## Understanding the Code 83 | 84 | Here is a brief overview of the important parts of the code: 85 | 86 | - `Block`: This struct represents a block in the blockchain. Each block contains data (in the form of a string), a timestamp, a nonce, a hash, the hash of the previous block, and its height in the blockchain. 87 | 88 | - `Blockchain`: This struct represents the blockchain itself, which is a list of blocks. 89 | 90 | - `Block::new_block`: This function is used to create a new block. It computes the block's hash using a simple proof-of-work mechanism. 91 | 92 | - `Block::proof_of_work`: This function attempts to find a nonce such that the hash of the block's data, the nonce, and some other information starts with a certain number of zeroes. 93 | 94 | - `Blockchain::new`: This function creates a new blockchain, starting with a genesis block. 95 | 96 | - `Blockchain::add_block`: This function adds a block with given data to the blockchain. 97 | 98 | The `tests` module contains a simple test that demonstrates creating a blockchain and adding blocks to it. 99 | 100 | ## Author 101 | 102 | Deepak Komma (deepakkomma@gmail.com) 103 | 104 | ## Version 105 | 106 | 0.1 107 | 108 | - 109 | 110 | This README is always evolving. If you think it's missing something crucial, please don't hesitate to suggest improvements. 111 | -------------------------------------------------------------------------------- /data/blocks/conf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepak-likes-code/rust-blockchain/4ca82a207efe23d745362f24fbd5503c79f7f51b/data/blocks/conf -------------------------------------------------------------------------------- /data/blocks/db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepak-likes-code/rust-blockchain/4ca82a207efe23d745362f24fbd5503c79f7f51b/data/blocks/db -------------------------------------------------------------------------------- /data/wallets/conf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepak-likes-code/rust-blockchain/4ca82a207efe23d745362f24fbd5503c79f7f51b/data/wallets/conf -------------------------------------------------------------------------------- /data/wallets/db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepak-likes-code/rust-blockchain/4ca82a207efe23d745362f24fbd5503c79f7f51b/data/wallets/db -------------------------------------------------------------------------------- /src/block.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::errors::Result; 3 | use crate::transaction::Transaction; 4 | use bincode::{deserialize, serialize}; 5 | use colored::*; 6 | use crypto::digest::Digest; 7 | use crypto::sha2::Sha256; 8 | use log::{debug, info}; 9 | use merkle_cbt::merkle_tree::{Merge, CBMT}; 10 | use serde::{Deserialize, Serialize}; 11 | use std::time::SystemTime; 12 | 13 | pub const TARGET_HEX: usize = 4; 14 | 15 | struct MergeTX {} 16 | 17 | impl Merge for MergeTX { 18 | type Item = Vec; 19 | 20 | fn merge(left: &Self::Item, right: &Self::Item) -> Self::Item { 21 | let mut hasher = Sha256::new(); 22 | let mut data = left.clone(); 23 | data.append(&mut right.clone()); 24 | hasher.input(&data); 25 | let mut re: [u8; 32] = [0; 32]; 26 | hasher.result(&mut re); 27 | re.to_vec() 28 | } 29 | } 30 | 31 | #[derive(Debug, Serialize, Deserialize, Clone)] 32 | pub struct Block { 33 | nonce: u32, 34 | height: i32, 35 | timestamp: u128, 36 | hash: String, 37 | prev_block_hash: String, 38 | transactions: Vec, 39 | } 40 | 41 | impl Block { 42 | pub fn new_block( 43 | data: Vec, 44 | prev_block_hash: String, 45 | height: i32, 46 | ) -> Result { 47 | let timestamp = SystemTime::now() 48 | .duration_since(SystemTime::UNIX_EPOCH)? 49 | .as_millis(); 50 | 51 | let mut block = Block { 52 | timestamp, 53 | nonce: 0, 54 | height, 55 | prev_block_hash, 56 | hash: String::new(), 57 | transactions: data, 58 | }; 59 | 60 | block.proof_of_work()?; 61 | Ok(block) 62 | } 63 | 64 | pub fn get_hash(&self) -> String { 65 | self.hash.clone() 66 | } 67 | 68 | pub fn get_height(&self) -> i32 { 69 | self.height 70 | } 71 | 72 | fn hash_transactions(&self) -> Result> { 73 | let mut transactions = Vec::new(); 74 | for tx in &self.transactions { 75 | transactions.push(tx.clone().hash()?.as_bytes().to_owned()); 76 | } 77 | let merkle_tree = CBMT::, MergeTX>::build_merkle_tree(&*transactions); 78 | Ok(merkle_tree.root()) 79 | } 80 | 81 | pub fn get_prev_hash(&self) -> String { 82 | self.prev_block_hash.clone() 83 | } 84 | 85 | fn proof_of_work(&mut self) -> Result<()> { 86 | info!("Mining the block"); 87 | while !self.validate()? { 88 | self.nonce += 1; 89 | } 90 | let data = self.prepare_hash_data()?; 91 | let mut hasher = Sha256::new(); 92 | hasher.input(&data[..]); 93 | self.hash = hasher.result_str(); 94 | Ok(()) 95 | } 96 | 97 | fn prepare_hash_data(&self) -> Result> { 98 | let content = ( 99 | self.prev_block_hash.clone(), 100 | self.hash_transactions()?, 101 | self.timestamp, 102 | TARGET_HEX, 103 | self.nonce, 104 | ); 105 | 106 | let bytes = bincode::serialize(&content)?; 107 | Ok(bytes) 108 | } 109 | 110 | pub fn new_genesis_block(coinbase: Transaction) -> Block { 111 | Block::new_block(vec![coinbase], String::new(), 0).unwrap() 112 | } 113 | 114 | pub fn get_transaction(&self) -> &Vec { 115 | &self.transactions 116 | } 117 | 118 | fn validate(&self) -> Result { 119 | let data = self.prepare_hash_data()?; 120 | let mut hasher = Sha256::new(); 121 | hasher.input(&data[..]); 122 | let mut vec1 = vec![]; 123 | vec1.resize(TARGET_HEX, '0' as u8); 124 | println!("{}", format!("{:#?}", vec1).green()); 125 | 126 | Ok(&hasher.result_str()[0..TARGET_HEX] == String::from_utf8(vec1)?) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/blockchain.rs: -------------------------------------------------------------------------------- 1 | //! Blockchain 2 | 3 | use super::*; 4 | use crate::block::*; 5 | use crate::transaction::*; 6 | use bincode::{deserialize, serialize}; 7 | use failure::format_err; 8 | use log::{debug, info}; 9 | use sled; 10 | use std::collections::HashMap; 11 | 12 | const GENESIS_COINBASE_DATA: &str = 13 | "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"; 14 | 15 | /// Blockchain implements interactions with a DB 16 | #[derive(Debug)] 17 | pub struct Blockchain { 18 | pub tip: String, 19 | pub db: sled::Db, 20 | } 21 | 22 | /// BlockchainIterator is used to iterate over blockchain blocks 23 | pub struct BlockchainIterator<'a> { 24 | current_hash: String, 25 | bc: &'a Blockchain, 26 | } 27 | 28 | impl Blockchain { 29 | /// NewBlockchain creates a new Blockchain db 30 | pub fn new() -> Result { 31 | info!("open blockchain"); 32 | 33 | let db = sled::open("data/blocks")?; 34 | let hash = match db.get("LAST")? { 35 | Some(l) => l.to_vec(), 36 | None => Vec::new(), 37 | }; 38 | info!("Found block database"); 39 | let lasthash = if hash.is_empty() { 40 | String::new() 41 | } else { 42 | String::from_utf8(hash.to_vec())? 43 | }; 44 | Ok(Blockchain { tip: lasthash, db }) 45 | } 46 | 47 | /// CreateBlockchain creates a new blockchain DB 48 | pub fn create_blockchain(address: String) -> Result { 49 | info!("Creating new blockchain"); 50 | 51 | std::fs::remove_dir_all("data/blocks").ok(); 52 | let db = sled::open("data/blocks")?; 53 | debug!("Creating new block database"); 54 | let cbtx = Transaction::new_coinbase(address, String::from(GENESIS_COINBASE_DATA))?; 55 | let genesis: Block = Block::new_genesis_block(cbtx); 56 | db.insert(genesis.get_hash(), serialize(&genesis)?)?; 57 | db.insert("LAST", genesis.get_hash().as_bytes())?; 58 | let bc = Blockchain { 59 | tip: genesis.get_hash(), 60 | db, 61 | }; 62 | bc.db.flush()?; 63 | Ok(bc) 64 | } 65 | 66 | /// MineBlock mines a new block with the provided transactions 67 | pub fn mine_block(&mut self, transactions: Vec) -> Result { 68 | info!("mine a new block"); 69 | 70 | for tx in &transactions { 71 | if !self.verify_transacton(tx)? { 72 | return Err(format_err!("ERROR: Invalid transaction")); 73 | } 74 | } 75 | 76 | let lasthash = self.db.get("LAST")?.unwrap(); 77 | 78 | let newblock = Block::new_block( 79 | transactions, 80 | String::from_utf8(lasthash.to_vec())?, 81 | self.get_best_height()? + 1, 82 | )?; 83 | self.db.insert(newblock.get_hash(), serialize(&newblock)?)?; 84 | self.db.insert("LAST", newblock.get_hash().as_bytes())?; 85 | self.db.flush()?; 86 | 87 | self.tip = newblock.get_hash(); 88 | Ok(newblock) 89 | } 90 | 91 | /// Iterator returns a BlockchainIterat 92 | pub fn iter(&self) -> BlockchainIterator { 93 | BlockchainIterator { 94 | current_hash: self.tip.clone(), 95 | bc: &self, 96 | } 97 | } 98 | 99 | /// FindUTXO finds and returns all unspent transaction outputs 100 | pub fn find_UTXO(&self) -> HashMap { 101 | let mut utxos: HashMap = HashMap::new(); 102 | let mut spend_txos: HashMap> = HashMap::new(); 103 | 104 | for block in self.iter() { 105 | for tx in block.get_transaction() { 106 | for index in 0..tx.vout.len() { 107 | if let Some(ids) = spend_txos.get(&tx.id) { 108 | if ids.contains(&(index as i32)) { 109 | continue; 110 | } 111 | } 112 | 113 | match utxos.get_mut(&tx.id) { 114 | Some(v) => { 115 | v.outputs.push(tx.vout[index].clone()); 116 | } 117 | None => { 118 | utxos.insert( 119 | tx.id.clone(), 120 | TXOutputs { 121 | outputs: vec![tx.vout[index].clone()], 122 | }, 123 | ); 124 | } 125 | } 126 | } 127 | 128 | if !tx.is_coinbase() { 129 | for i in &tx.vin { 130 | match spend_txos.get_mut(&i.txid) { 131 | Some(v) => { 132 | v.push(i.vout); 133 | } 134 | None => { 135 | spend_txos.insert(i.txid.clone(), vec![i.vout]); 136 | } 137 | } 138 | } 139 | } 140 | } 141 | } 142 | 143 | utxos 144 | } 145 | 146 | /// FindTransaction finds a transaction by its ID 147 | pub fn find_transacton(&self, id: &str) -> Result { 148 | for b in self.iter() { 149 | for tx in b.get_transaction() { 150 | if tx.id == id { 151 | return Ok(tx.clone()); 152 | } 153 | } 154 | } 155 | Err(format_err!("Transaction is not found")) 156 | } 157 | 158 | fn get_prev_TXs(&self, tx: &Transaction) -> Result> { 159 | let mut prev_TXs = HashMap::new(); 160 | for vin in &tx.vin { 161 | let prev_TX = self.find_transacton(&vin.txid)?; 162 | prev_TXs.insert(prev_TX.id.clone(), prev_TX); 163 | } 164 | Ok(prev_TXs) 165 | } 166 | 167 | /// SignTransaction signs inputs of a Transaction 168 | pub fn sign_transacton(&self, tx: &mut Transaction, private_key: &[u8]) -> Result<()> { 169 | let prev_TXs = self.get_prev_TXs(tx)?; 170 | tx.sign(private_key, prev_TXs)?; 171 | Ok(()) 172 | } 173 | 174 | /// VerifyTransaction verifies transaction input signatures 175 | pub fn verify_transacton(&self, tx: &Transaction) -> Result { 176 | if tx.is_coinbase() { 177 | return Ok(true); 178 | } 179 | let prev_TXs = self.get_prev_TXs(tx)?; 180 | tx.verify(prev_TXs) 181 | } 182 | 183 | /// AddBlock saves the block into the blockchain 184 | pub fn add_block(&mut self, block: Block) -> Result<()> { 185 | let data = serialize(&block)?; 186 | if let Some(_) = self.db.get(block.get_hash())? { 187 | return Ok(()); 188 | } 189 | self.db.insert(block.get_hash(), data)?; 190 | 191 | let lastheight = self.get_best_height()?; 192 | if block.get_height() > lastheight { 193 | self.db.insert("LAST", block.get_hash().as_bytes())?; 194 | self.tip = block.get_hash(); 195 | self.db.flush()?; 196 | } 197 | Ok(()) 198 | } 199 | 200 | // GetBlock finds a block by its hash and returns it 201 | pub fn get_block(&self, block_hash: &str) -> Result { 202 | let data = self.db.get(block_hash)?.unwrap(); 203 | let block = deserialize(&data.to_vec())?; 204 | Ok(block) 205 | } 206 | 207 | /// GetBestHeight returns the height of the latest block 208 | pub fn get_best_height(&self) -> Result { 209 | let lasthash = if let Some(h) = self.db.get("LAST")? { 210 | h 211 | } else { 212 | return Ok(-1); 213 | }; 214 | let last_data = self.db.get(lasthash)?.unwrap(); 215 | let last_block: Block = deserialize(&last_data.to_vec())?; 216 | Ok(last_block.get_height()) 217 | } 218 | 219 | /// GetBlockHashes returns a list of hashes of all the blocks in the chain 220 | pub fn get_block_hashs(&self) -> Vec { 221 | let mut list = Vec::new(); 222 | for b in self.iter() { 223 | list.push(b.get_hash()); 224 | } 225 | list 226 | } 227 | } 228 | 229 | impl<'a> Iterator for BlockchainIterator<'a> { 230 | type Item = Block; 231 | 232 | fn next(&mut self) -> Option { 233 | if let Ok(encoded_block) = self.bc.db.get(&self.current_hash) { 234 | return match encoded_block { 235 | Some(b) => { 236 | if let Ok(block) = deserialize::(&b) { 237 | self.current_hash = block.get_prev_hash(); 238 | Some(block) 239 | } else { 240 | None 241 | } 242 | } 243 | None => None, 244 | }; 245 | } 246 | None 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /src/cli.rs: -------------------------------------------------------------------------------- 1 | use crate::blockchain::Blockchain; 2 | use crate::errors::Result; 3 | use crate::server::Server; 4 | use crate::transaction::Transaction; 5 | use crate::utxoset::UTXOSet; 6 | use crate::wallet::{Wallet, Wallets}; 7 | use bitcoincash_addr::Address; 8 | use clap::{arg, Command}; 9 | use std::process::exit; 10 | 11 | pub struct Cli {} 12 | 13 | impl Cli { 14 | pub fn new() -> Result { 15 | Ok(Cli {}) 16 | } 17 | pub fn run(&mut self) -> Result<()> { 18 | let matches = Command::new("blockchain-rust-demo") 19 | .version("0.1") 20 | .author("behrouz.r.fa@gmail.com") 21 | .about("blockchain in rust: a simple blockchain for learning") 22 | .subcommand(Command::new("printchain").about("print all the chain blocks")) 23 | .subcommand(Command::new("createwallet").about("create a wallet")) 24 | .subcommand(Command::new("listaddresses").about("list all addresses")) 25 | .subcommand(Command::new("reindex").about("reindex UTXO")) 26 | .subcommand( 27 | Command::new("getbalance") 28 | .about("get balance in the blochain") 29 | .arg(arg!(
"'The Address it get balance for'")), 30 | ) 31 | .subcommand( 32 | Command::new("startnode") 33 | .about("start the node server") 34 | .arg(arg!("'the port server bind to locally'")), 35 | ) 36 | .subcommand( 37 | Command::new("create") 38 | .about("Create new blochain") 39 | .arg(arg!(
"'The address to send gensis block reqward to' ")), 40 | ) 41 | .subcommand( 42 | Command::new("send") 43 | .about("send in the blockchain") 44 | .arg(arg!(" 'Source wallet address'")) 45 | .arg(arg!(" 'Destination wallet address'")) 46 | .arg(arg!(" 'Destination wallet address'")) 47 | .arg(arg!(-m --mine " 'the from address mine immediately'")), 48 | ) 49 | .subcommand( 50 | Command::new("startminer") 51 | .about("start the minner server") 52 | .arg(arg!(" 'the port server bind to locally'")) 53 | .arg(arg!(
" 'wallet address'")), 54 | ) 55 | .get_matches(); 56 | 57 | if let Some(ref matches) = matches.subcommand_matches("startminer") { 58 | let port = if let Some(port) = matches.get_one::("PORT") { 59 | port 60 | } else { 61 | println!("PORT not supply!: usage"); 62 | exit(1) 63 | }; 64 | 65 | let address = if let Some(address) = matches.get_one::("ADDRESS") { 66 | address 67 | } else { 68 | println!("ADDRESS not supply!: usage"); 69 | exit(1) 70 | }; 71 | let bc = Blockchain::new()?; 72 | let utxo_set = UTXOSet { blockchain: bc }; 73 | let server = Server::new(port, address, utxo_set)?; 74 | server.start_server()?; 75 | } 76 | 77 | if let Some(ref matches) = matches.subcommand_matches("startnode") { 78 | if let Some(port) = matches.get_one::("PORT") { 79 | let bc = Blockchain::new()?; 80 | let utxo_set = UTXOSet { blockchain: bc }; 81 | let server = Server::new(port, "", utxo_set)?; 82 | server.start_server()?; 83 | } 84 | } 85 | 86 | if let Some(_) = matches.subcommand_matches("createwallet") { 87 | println!("address: {}", cmd_create_wallet()?); 88 | } 89 | if let Some(_) = matches.subcommand_matches("reindex") { 90 | let count = cmd_reindex()?; 91 | println!("Done! There are {} transactions in the UTXO set.", count); 92 | } 93 | 94 | if let Some(_) = matches.subcommand_matches("listaddresses") { 95 | cmd_list_address()?; 96 | } 97 | 98 | if let Some(ref matches) = matches.subcommand_matches("create") { 99 | if let Some(address) = matches.get_one::("ADDRESS") { 100 | cmd_create_blockchain(address)?; 101 | } 102 | } 103 | 104 | if let Some(ref matches) = matches.subcommand_matches("getbalance") { 105 | if let Some(address) = matches.get_one::("ADDRESS") { 106 | let balance = cmd_get_balance(address)?; 107 | println!("Balance: {}\n", balance); 108 | } 109 | } 110 | 111 | if let Some(ref matches) = matches.subcommand_matches("send") { 112 | let from = if let Some(address) = matches.get_one::("FROM") { 113 | address 114 | } else { 115 | println!("from not supply!: usage"); 116 | exit(1) 117 | }; 118 | 119 | let to = if let Some(address) = matches.get_one::("TO") { 120 | address 121 | } else { 122 | println!("from not supply!: usage"); 123 | exit(1) 124 | }; 125 | 126 | let amount: i32 = if let Some(amount) = matches.get_one::("AMOUNT") { 127 | amount.parse()? 128 | } else { 129 | println!("from not supply!: usage"); 130 | exit(1) 131 | }; 132 | 133 | if matches.contains_id("mine") { 134 | cmd_send(from, to, amount, true)?; 135 | } else { 136 | cmd_send(from, to, amount, false)?; 137 | } 138 | 139 | /*else { 140 | println!("Not printing testing lists..."); 141 | }*/ 142 | } 143 | 144 | if let Some(_) = matches.subcommand_matches("printchain") { 145 | cmd_print_chain()?; 146 | } 147 | 148 | Ok(()) 149 | } 150 | } 151 | 152 | fn cmd_send(from: &str, to: &str, amount: i32, mine_now: bool) -> Result<()> { 153 | let bc = Blockchain::new()?; 154 | let mut utxo_set = UTXOSet { blockchain: bc }; 155 | let wallets = Wallets::new()?; 156 | let wallet = wallets.get_wallet(from).unwrap(); 157 | let tx = Transaction::new_UTXO(wallet, to, amount, &utxo_set)?; 158 | if mine_now { 159 | let cbtx = Transaction::new_coinbase(from.to_string(), String::from("reward!"))?; 160 | let new_block = utxo_set.blockchain.mine_block(vec![cbtx, tx])?; 161 | 162 | utxo_set.update(&new_block)?; 163 | } else { 164 | Server::send_transaction(&tx, utxo_set)?; 165 | } 166 | 167 | println!("success!"); 168 | Ok(()) 169 | } 170 | 171 | fn cmd_create_wallet() -> Result { 172 | let mut ws = Wallets::new()?; 173 | let address = ws.create_wallet(); 174 | ws.save_all()?; 175 | Ok(address) 176 | } 177 | 178 | fn cmd_reindex() -> Result { 179 | let bc = Blockchain::new()?; 180 | let utxo_set = UTXOSet { blockchain: bc }; 181 | utxo_set.reindex()?; 182 | utxo_set.count_transactions() 183 | } 184 | 185 | fn cmd_create_blockchain(address: &str) -> Result<()> { 186 | let address = String::from(address); 187 | let bc = Blockchain::create_blockchain(address)?; 188 | 189 | let utxo_set = UTXOSet { blockchain: bc }; 190 | utxo_set.reindex()?; 191 | println!("create blockchain"); 192 | Ok(()) 193 | } 194 | 195 | fn cmd_get_balance(address: &str) -> Result { 196 | let pub_key_hash = Address::decode(address).unwrap().body; 197 | let bc = Blockchain::new()?; 198 | let utxo_set = UTXOSet { blockchain: bc }; 199 | let utxos = utxo_set.find_UTXO(&pub_key_hash)?; 200 | 201 | let mut balance = 0; 202 | for out in utxos.outputs { 203 | balance += out.value; 204 | } 205 | Ok(balance) 206 | } 207 | 208 | fn cmd_print_chain() -> Result<()> { 209 | let bc = Blockchain::new()?; 210 | for b in bc.iter() { 211 | println!("{:#?}", b); 212 | } 213 | Ok(()) 214 | } 215 | 216 | fn cmd_list_address() -> Result<()> { 217 | let ws = Wallets::new()?; 218 | let addresses = ws.get_all_addresses(); 219 | println!("addresses: "); 220 | for ad in addresses { 221 | println!("{}", ad); 222 | } 223 | Ok(()) 224 | } 225 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | pub type Result = std::result::Result; 2 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | pub mod block; 2 | pub mod blockchain; 3 | pub mod cli; 4 | pub mod errors; 5 | mod server; 6 | pub mod transaction; 7 | pub mod txn; 8 | pub mod utxoset; 9 | pub mod wallet; 10 | 11 | use errors::Result; 12 | 13 | fn main() -> Result<()> { 14 | let mut cli = cli::Cli::new()?; 15 | cli.run()?; 16 | Ok(()) 17 | } 18 | -------------------------------------------------------------------------------- /src/server.rs: -------------------------------------------------------------------------------- 1 | //! server of Blockchain 2 | 3 | use super::*; 4 | use crate::block::*; 5 | use crate::transaction::*; 6 | use crate::utxoset::*; 7 | use bincode::{deserialize, serialize}; 8 | use failure::format_err; 9 | use log::{debug, info}; 10 | use serde::{Deserialize, Serialize}; 11 | use std::collections::{HashMap, HashSet}; 12 | use std::io::prelude::*; 13 | use std::net::{TcpListener, TcpStream}; 14 | use std::sync::*; 15 | use std::thread; 16 | use std::time::Duration; 17 | 18 | #[derive(Serialize, Deserialize, Debug, Clone)] 19 | enum Message { 20 | Addr(Vec), 21 | Version(Versionmsg), 22 | Tx(Txmsg), 23 | GetData(GetDatamsg), 24 | GetBlock(GetBlocksmsg), 25 | Inv(Invmsg), 26 | Block(Blockmsg), 27 | } 28 | 29 | #[derive(Serialize, Deserialize, Debug, Clone)] 30 | struct Blockmsg { 31 | addr_from: String, 32 | block: Block, 33 | } 34 | 35 | #[derive(Serialize, Deserialize, Debug, Clone)] 36 | struct GetBlocksmsg { 37 | addr_from: String, 38 | } 39 | 40 | #[derive(Serialize, Deserialize, Debug, Clone)] 41 | struct GetDatamsg { 42 | addr_from: String, 43 | kind: String, 44 | id: String, 45 | } 46 | 47 | #[derive(Serialize, Deserialize, Debug, Clone)] 48 | struct Invmsg { 49 | addr_from: String, 50 | kind: String, 51 | items: Vec, 52 | } 53 | 54 | #[derive(Serialize, Deserialize, Debug, Clone)] 55 | struct Txmsg { 56 | addr_from: String, 57 | transaction: Transaction, 58 | } 59 | 60 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] 61 | struct Versionmsg { 62 | addr_from: String, 63 | version: i32, 64 | best_height: i32, 65 | } 66 | 67 | pub struct Server { 68 | node_address: String, 69 | mining_address: String, 70 | inner: Arc>, 71 | } 72 | 73 | struct ServerInner { 74 | known_nodes: HashSet, 75 | utxo: UTXOSet, 76 | blocks_in_transit: Vec, 77 | mempool: HashMap, 78 | } 79 | 80 | const KNOWN_NODE1: &str = "localhost:3000"; 81 | const CMD_LEN: usize = 12; 82 | const VERSION: i32 = 1; 83 | 84 | impl Server { 85 | pub fn new(port: &str, miner_address: &str, utxo: UTXOSet) -> Result { 86 | let mut node_set = HashSet::new(); 87 | node_set.insert(String::from(KNOWN_NODE1)); 88 | Ok(Server { 89 | node_address: String::from("localhost:") + port, 90 | mining_address: miner_address.to_string(), 91 | inner: Arc::new(Mutex::new(ServerInner { 92 | known_nodes: node_set, 93 | utxo, 94 | blocks_in_transit: Vec::new(), 95 | mempool: HashMap::new(), 96 | })), 97 | }) 98 | } 99 | 100 | pub fn start_server(&self) -> Result<()> { 101 | let server1 = Server { 102 | node_address: self.node_address.clone(), 103 | mining_address: self.mining_address.clone(), 104 | inner: Arc::clone(&self.inner), 105 | }; 106 | info!( 107 | "Start server at {}, minning address: {}", 108 | &self.node_address, &self.mining_address 109 | ); 110 | 111 | thread::spawn(move || { 112 | thread::sleep(Duration::from_millis(1000)); 113 | if server1.get_best_height()? == -1 { 114 | server1.request_blocks() 115 | } else { 116 | server1.send_version(KNOWN_NODE1) 117 | } 118 | }); 119 | 120 | let listener = TcpListener::bind(&self.node_address).unwrap(); 121 | info!("Server listen..."); 122 | 123 | for stream in listener.incoming() { 124 | let stream = stream?; 125 | let server1 = Server { 126 | node_address: self.node_address.clone(), 127 | mining_address: self.mining_address.clone(), 128 | inner: Arc::clone(&self.inner), 129 | }; 130 | thread::spawn(move || server1.handle_connection(stream)); 131 | } 132 | 133 | Ok(()) 134 | } 135 | 136 | pub fn send_transaction(tx: &Transaction, utxoset: UTXOSet) -> Result<()> { 137 | let server = Server::new("7000", "", utxoset)?; 138 | server.send_tx(KNOWN_NODE1, tx)?; 139 | Ok(()) 140 | } 141 | 142 | /* ------------------- inner halp functions ----------------------------------*/ 143 | 144 | fn remove_node(&self, addr: &str) { 145 | self.inner.lock().unwrap().known_nodes.remove(addr); 146 | } 147 | 148 | fn add_nodes(&self, addr: &str) { 149 | self.inner 150 | .lock() 151 | .unwrap() 152 | .known_nodes 153 | .insert(String::from(addr)); 154 | } 155 | 156 | fn get_known_nodes(&self) -> HashSet { 157 | self.inner.lock().unwrap().known_nodes.clone() 158 | } 159 | 160 | fn node_is_known(&self, addr: &str) -> bool { 161 | self.inner.lock().unwrap().known_nodes.get(addr).is_some() 162 | } 163 | 164 | fn replace_in_transit(&self, hashs: Vec) { 165 | let bit = &mut self.inner.lock().unwrap().blocks_in_transit; 166 | bit.clone_from(&hashs); 167 | } 168 | 169 | fn get_in_transit(&self) -> Vec { 170 | self.inner.lock().unwrap().blocks_in_transit.clone() 171 | } 172 | 173 | fn get_mempool_tx(&self, addr: &str) -> Option { 174 | match self.inner.lock().unwrap().mempool.get(addr) { 175 | Some(tx) => Some(tx.clone()), 176 | None => None, 177 | } 178 | } 179 | 180 | fn get_mempool(&self) -> HashMap { 181 | self.inner.lock().unwrap().mempool.clone() 182 | } 183 | 184 | fn insert_mempool(&self, tx: Transaction) { 185 | self.inner.lock().unwrap().mempool.insert(tx.id.clone(), tx); 186 | } 187 | 188 | fn clear_mempool(&self) { 189 | self.inner.lock().unwrap().mempool.clear() 190 | } 191 | 192 | fn get_best_height(&self) -> Result { 193 | self.inner.lock().unwrap().utxo.blockchain.get_best_height() 194 | } 195 | 196 | fn get_block_hashs(&self) -> Vec { 197 | self.inner.lock().unwrap().utxo.blockchain.get_block_hashs() 198 | } 199 | 200 | fn get_block(&self, block_hash: &str) -> Result { 201 | self.inner 202 | .lock() 203 | .unwrap() 204 | .utxo 205 | .blockchain 206 | .get_block(block_hash) 207 | } 208 | 209 | fn verify_tx(&self, tx: &Transaction) -> Result { 210 | self.inner 211 | .lock() 212 | .unwrap() 213 | .utxo 214 | .blockchain 215 | .verify_transacton(tx) 216 | } 217 | 218 | fn add_block(&self, block: Block) -> Result<()> { 219 | self.inner.lock().unwrap().utxo.blockchain.add_block(block) 220 | } 221 | 222 | fn mine_block(&self, txs: Vec) -> Result { 223 | self.inner.lock().unwrap().utxo.blockchain.mine_block(txs) 224 | } 225 | 226 | fn utxo_reindex(&self) -> Result<()> { 227 | self.inner.lock().unwrap().utxo.reindex() 228 | } 229 | 230 | /* -----------------------------------------------------*/ 231 | 232 | fn send_data(&self, addr: &str, data: &[u8]) -> Result<()> { 233 | if addr == &self.node_address { 234 | return Ok(()); 235 | } 236 | let mut stream = match TcpStream::connect(addr) { 237 | Ok(s) => s, 238 | Err(_) => { 239 | self.remove_node(addr); 240 | return Ok(()); 241 | } 242 | }; 243 | 244 | stream.write(data)?; 245 | 246 | info!("data send successfully"); 247 | Ok(()) 248 | } 249 | 250 | fn request_blocks(&self) -> Result<()> { 251 | for node in self.get_known_nodes() { 252 | self.send_get_blocks(&node)? 253 | } 254 | Ok(()) 255 | } 256 | 257 | fn send_block(&self, addr: &str, b: &Block) -> Result<()> { 258 | info!("send block data to: {} block hash: {}", addr, b.get_hash()); 259 | let data = Blockmsg { 260 | addr_from: self.node_address.clone(), 261 | block: b.clone(), 262 | }; 263 | let data = serialize(&(cmd_to_bytes("block"), data))?; 264 | self.send_data(addr, &data) 265 | } 266 | 267 | fn send_addr(&self, addr: &str) -> Result<()> { 268 | info!("send address info to: {}", addr); 269 | let nodes = self.get_known_nodes(); 270 | let data = serialize(&(cmd_to_bytes("addr"), nodes))?; 271 | self.send_data(addr, &data) 272 | } 273 | 274 | fn send_inv(&self, addr: &str, kind: &str, items: Vec) -> Result<()> { 275 | info!( 276 | "send inv message to: {} kind: {} data: {:?}", 277 | addr, kind, items 278 | ); 279 | let data = Invmsg { 280 | addr_from: self.node_address.clone(), 281 | kind: kind.to_string(), 282 | items, 283 | }; 284 | let data = serialize(&(cmd_to_bytes("inv"), data))?; 285 | self.send_data(addr, &data) 286 | } 287 | 288 | fn send_get_blocks(&self, addr: &str) -> Result<()> { 289 | info!("send get blocks message to: {}", addr); 290 | let data = GetBlocksmsg { 291 | addr_from: self.node_address.clone(), 292 | }; 293 | let data = serialize(&(cmd_to_bytes("getblocks"), data))?; 294 | self.send_data(addr, &data) 295 | } 296 | 297 | fn send_get_data(&self, addr: &str, kind: &str, id: &str) -> Result<()> { 298 | info!( 299 | "send get data message to: {} kind: {} id: {}", 300 | addr, kind, id 301 | ); 302 | let data = GetDatamsg { 303 | addr_from: self.node_address.clone(), 304 | kind: kind.to_string(), 305 | id: id.to_string(), 306 | }; 307 | let data = serialize(&(cmd_to_bytes("getdata"), data))?; 308 | self.send_data(addr, &data) 309 | } 310 | 311 | pub fn send_tx(&self, addr: &str, tx: &Transaction) -> Result<()> { 312 | info!("send tx to: {} txid: {}", addr, &tx.id); 313 | let data = Txmsg { 314 | addr_from: self.node_address.clone(), 315 | transaction: tx.clone(), 316 | }; 317 | let data = serialize(&(cmd_to_bytes("tx"), data))?; 318 | self.send_data(addr, &data) 319 | } 320 | 321 | fn send_version(&self, addr: &str) -> Result<()> { 322 | info!("send version info to: {}", addr); 323 | let data = Versionmsg { 324 | addr_from: self.node_address.clone(), 325 | best_height: self.get_best_height()?, 326 | version: VERSION, 327 | }; 328 | let data = serialize(&(cmd_to_bytes("version"), data))?; 329 | self.send_data(addr, &data) 330 | } 331 | 332 | fn handle_version(&self, msg: Versionmsg) -> Result<()> { 333 | info!("receive version msg: {:#?}", msg); 334 | let my_best_height = self.get_best_height()?; 335 | if my_best_height < msg.best_height { 336 | self.send_get_blocks(&msg.addr_from)?; 337 | } else if my_best_height > msg.best_height { 338 | self.send_version(&msg.addr_from)?; 339 | } 340 | 341 | self.send_addr(&msg.addr_from)?; 342 | 343 | if !self.node_is_known(&msg.addr_from) { 344 | self.add_nodes(&msg.addr_from); 345 | } 346 | Ok(()) 347 | } 348 | 349 | fn handle_addr(&self, msg: Vec) -> Result<()> { 350 | info!("receive address msg: {:#?}", msg); 351 | for node in msg { 352 | self.add_nodes(&node); 353 | } 354 | //self.request_blocks()?; 355 | Ok(()) 356 | } 357 | 358 | fn handle_block(&self, msg: Blockmsg) -> Result<()> { 359 | info!( 360 | "receive block msg: {}, {}", 361 | msg.addr_from, 362 | msg.block.get_hash() 363 | ); 364 | self.add_block(msg.block)?; 365 | 366 | let mut in_transit = self.get_in_transit(); 367 | if in_transit.len() > 0 { 368 | let block_hash = &in_transit[0]; 369 | self.send_get_data(&msg.addr_from, "block", block_hash)?; 370 | in_transit.remove(0); 371 | self.replace_in_transit(in_transit); 372 | } else { 373 | self.utxo_reindex()?; 374 | } 375 | 376 | Ok(()) 377 | } 378 | 379 | fn handle_inv(&self, msg: Invmsg) -> Result<()> { 380 | info!("receive inv msg: {:#?}", msg); 381 | if msg.kind == "block" { 382 | let block_hash = &msg.items[0]; 383 | self.send_get_data(&msg.addr_from, "block", block_hash)?; 384 | 385 | let mut new_in_transit = Vec::new(); 386 | for b in &msg.items { 387 | if b != block_hash { 388 | new_in_transit.push(b.clone()); 389 | } 390 | } 391 | self.replace_in_transit(new_in_transit); 392 | } else if msg.kind == "tx" { 393 | let txid = &msg.items[0]; 394 | match self.get_mempool_tx(txid) { 395 | Some(tx) => { 396 | if tx.id.is_empty() { 397 | self.send_get_data(&msg.addr_from, "tx", txid)? 398 | } 399 | } 400 | None => self.send_get_data(&msg.addr_from, "tx", txid)?, 401 | } 402 | } 403 | Ok(()) 404 | } 405 | 406 | fn handle_get_blocks(&self, msg: GetBlocksmsg) -> Result<()> { 407 | info!("receive get blocks msg: {:#?}", msg); 408 | let block_hashs = self.get_block_hashs(); 409 | self.send_inv(&msg.addr_from, "block", block_hashs)?; 410 | Ok(()) 411 | } 412 | 413 | fn handle_get_data(&self, msg: GetDatamsg) -> Result<()> { 414 | info!("receive get data msg: {:#?}", msg); 415 | if msg.kind == "block" { 416 | let block = self.get_block(&msg.id)?; 417 | self.send_block(&msg.addr_from, &block)?; 418 | } else if msg.kind == "tx" { 419 | let tx = self.get_mempool_tx(&msg.id).unwrap(); 420 | self.send_tx(&msg.addr_from, &tx)?; 421 | } 422 | Ok(()) 423 | } 424 | 425 | fn handle_tx(&self, msg: Txmsg) -> Result<()> { 426 | info!("receive tx msg: {} {}", msg.addr_from, &msg.transaction.id); 427 | self.insert_mempool(msg.transaction.clone()); 428 | 429 | let known_nodes = self.get_known_nodes(); 430 | if self.node_address == KNOWN_NODE1 { 431 | for node in known_nodes { 432 | if node != self.node_address && node != msg.addr_from { 433 | self.send_inv(&node, "tx", vec![msg.transaction.id.clone()])?; 434 | } 435 | } 436 | } else { 437 | let mut mempool = self.get_mempool(); 438 | debug!("Current mempool: {:#?}", &mempool); 439 | if mempool.len() >= 1 && !self.mining_address.is_empty() { 440 | loop { 441 | let mut txs = Vec::new(); 442 | 443 | for (_, tx) in &mempool { 444 | if self.verify_tx(tx)? { 445 | txs.push(tx.clone()); 446 | } 447 | } 448 | 449 | if txs.is_empty() { 450 | return Ok(()); 451 | } 452 | 453 | let cbtx = 454 | Transaction::new_coinbase(self.mining_address.clone(), String::new())?; 455 | txs.push(cbtx); 456 | 457 | for tx in &txs { 458 | mempool.remove(&tx.id); 459 | } 460 | 461 | let new_block = self.mine_block(txs)?; 462 | self.utxo_reindex()?; 463 | 464 | for node in self.get_known_nodes() { 465 | if node != self.node_address { 466 | self.send_inv(&node, "block", vec![new_block.get_hash()])?; 467 | } 468 | } 469 | 470 | if mempool.len() == 0 { 471 | break; 472 | } 473 | } 474 | self.clear_mempool(); 475 | } 476 | } 477 | 478 | Ok(()) 479 | } 480 | 481 | fn handle_connection(&self, mut stream: TcpStream) -> Result<()> { 482 | let mut buffer = Vec::new(); 483 | let count = stream.read_to_end(&mut buffer)?; 484 | info!("Accept request: length {}", count); 485 | 486 | let cmd = bytes_to_cmd(&buffer)?; 487 | 488 | match cmd { 489 | Message::Addr(data) => self.handle_addr(data)?, 490 | Message::Block(data) => self.handle_block(data)?, 491 | Message::Inv(data) => self.handle_inv(data)?, 492 | Message::GetBlock(data) => self.handle_get_blocks(data)?, 493 | Message::GetData(data) => self.handle_get_data(data)?, 494 | Message::Tx(data) => self.handle_tx(data)?, 495 | Message::Version(data) => self.handle_version(data)?, 496 | } 497 | 498 | Ok(()) 499 | } 500 | } 501 | 502 | fn cmd_to_bytes(cmd: &str) -> [u8; CMD_LEN] { 503 | let mut data = [0; CMD_LEN]; 504 | for (i, d) in cmd.as_bytes().iter().enumerate() { 505 | data[i] = *d; 506 | } 507 | data 508 | } 509 | 510 | fn bytes_to_cmd(bytes: &[u8]) -> Result { 511 | let mut cmd = Vec::new(); 512 | let cmd_bytes = &bytes[..CMD_LEN]; 513 | let data = &bytes[CMD_LEN..]; 514 | for b in cmd_bytes { 515 | if 0 as u8 != *b { 516 | cmd.push(*b); 517 | } 518 | } 519 | info!("cmd: {}", String::from_utf8(cmd.clone())?); 520 | 521 | if cmd == "addr".as_bytes() { 522 | let data: Vec = deserialize(data)?; 523 | Ok(Message::Addr(data)) 524 | } else if cmd == "block".as_bytes() { 525 | let data: Blockmsg = deserialize(data)?; 526 | Ok(Message::Block(data)) 527 | } else if cmd == "inv".as_bytes() { 528 | let data: Invmsg = deserialize(data)?; 529 | Ok(Message::Inv(data)) 530 | } else if cmd == "getblocks".as_bytes() { 531 | let data: GetBlocksmsg = deserialize(data)?; 532 | Ok(Message::GetBlock(data)) 533 | } else if cmd == "getdata".as_bytes() { 534 | let data: GetDatamsg = deserialize(data)?; 535 | Ok(Message::GetData(data)) 536 | } else if cmd == "tx".as_bytes() { 537 | let data: Txmsg = deserialize(data)?; 538 | Ok(Message::Tx(data)) 539 | } else if cmd == "version".as_bytes() { 540 | let data: Versionmsg = deserialize(data)?; 541 | Ok(Message::Version(data)) 542 | } else { 543 | Err(format_err!("Unknown command in the server")) 544 | } 545 | } 546 | 547 | #[cfg(test)] 548 | mod test { 549 | use super::*; 550 | use crate::blockchain::*; 551 | use crate::wallet::*; 552 | 553 | #[test] 554 | fn test_cmd() { 555 | let mut ws = Wallets::new().unwrap(); 556 | let wa1 = ws.create_wallet(); 557 | let bc = Blockchain::create_blockchain(wa1).unwrap(); 558 | let utxo_set = UTXOSet { blockchain: bc }; 559 | let server = Server::new("7878", "localhost:3001", utxo_set).unwrap(); 560 | 561 | let vmsg = Versionmsg { 562 | addr_from: server.node_address.clone(), 563 | best_height: server.get_best_height().unwrap(), 564 | version: VERSION, 565 | }; 566 | let data = serialize(&(cmd_to_bytes("version"), vmsg.clone())).unwrap(); 567 | if let Message::Version(v) = bytes_to_cmd(&data).unwrap() { 568 | assert_eq!(v, vmsg); 569 | } else { 570 | panic!("wrong!"); 571 | } 572 | } 573 | } 574 | -------------------------------------------------------------------------------- /src/transaction.rs: -------------------------------------------------------------------------------- 1 | //! transaction implement 2 | 3 | use super::*; 4 | use crate::utxoset::*; 5 | use crate::wallet::*; 6 | use bincode::serialize; 7 | use bitcoincash_addr::Address; 8 | use crypto::digest::Digest; 9 | use crypto::ed25519; 10 | use crypto::sha2::Sha256; 11 | use failure::format_err; 12 | use log::{debug, error, info}; 13 | use rand::rngs::OsRng; 14 | use rand::{Rng, RngCore}; 15 | use serde::{Deserialize, Serialize}; 16 | use std::collections::HashMap; 17 | 18 | const SUBSIDY: i32 = 10; 19 | 20 | /// TXInput represents a transaction input 21 | #[derive(Serialize, Deserialize, Debug, Clone)] 22 | pub struct TXInput { 23 | pub txid: String, 24 | pub vout: i32, 25 | pub signature: Vec, 26 | pub pub_key: Vec, 27 | } 28 | 29 | /// TXOutput represents a transaction output 30 | #[derive(Serialize, Deserialize, Debug, Clone)] 31 | pub struct TXOutput { 32 | pub value: i32, 33 | pub pub_key_hash: Vec, 34 | } 35 | 36 | // TXOutputs collects TXOutput 37 | #[derive(Serialize, Deserialize, Debug, Clone)] 38 | pub struct TXOutputs { 39 | pub outputs: Vec, 40 | } 41 | 42 | /// Transaction represents a Bitcoin transaction 43 | #[derive(Serialize, Deserialize, Debug, Clone)] 44 | pub struct Transaction { 45 | pub id: String, 46 | pub vin: Vec, 47 | pub vout: Vec, 48 | } 49 | 50 | impl Transaction { 51 | /// NewUTXOTransaction creates a new transaction 52 | pub fn new_UTXO(wallet: &Wallet, to: &str, amount: i32, utxo: &UTXOSet) -> Result { 53 | info!( 54 | "new UTXO Transaction from: {} to: {}", 55 | wallet.get_address(), 56 | to 57 | ); 58 | let mut vin = Vec::new(); 59 | 60 | let mut pub_key_hash = wallet.public_key.clone(); 61 | hash_pub_key(&mut pub_key_hash); 62 | 63 | let acc_v = utxo.find_spendable_outputs(&pub_key_hash, amount)?; 64 | 65 | if acc_v.0 < amount { 66 | error!("Not Enough balance"); 67 | return Err(format_err!( 68 | "Not Enough balance: current balance {}", 69 | acc_v.0 70 | )); 71 | } 72 | 73 | for tx in acc_v.1 { 74 | for out in tx.1 { 75 | let input = TXInput { 76 | txid: tx.0.clone(), 77 | vout: out, 78 | signature: Vec::new(), 79 | pub_key: wallet.public_key.clone(), 80 | }; 81 | vin.push(input); 82 | } 83 | } 84 | 85 | let mut vout = vec![TXOutput::new(amount, to.to_string())?]; 86 | if acc_v.0 > amount { 87 | vout.push(TXOutput::new(acc_v.0 - amount, wallet.get_address())?) 88 | } 89 | 90 | let mut tx = Transaction { 91 | id: String::new(), 92 | vin, 93 | vout, 94 | }; 95 | tx.id = tx.hash()?; 96 | utxo.blockchain 97 | .sign_transacton(&mut tx, &wallet.secret_key)?; 98 | Ok(tx) 99 | } 100 | 101 | /// NewCoinbaseTX creates a new coinbase transaction 102 | pub fn new_coinbase(to: String, mut data: String) -> Result { 103 | info!("new coinbase Transaction to: {}", to); 104 | let mut key: [u8; 32] = [0; 32]; 105 | if data.is_empty() { 106 | let mut rand = OsRng::default(); 107 | rand.fill_bytes(&mut key); 108 | data = format!("Reward to '{}'", to); 109 | } 110 | let mut pub_key = Vec::from(data.as_bytes()); 111 | pub_key.append(&mut Vec::from(key)); 112 | 113 | let mut tx = Transaction { 114 | id: String::new(), 115 | vin: vec![TXInput { 116 | txid: String::new(), 117 | vout: -1, 118 | signature: Vec::new(), 119 | pub_key, 120 | }], 121 | vout: vec![TXOutput::new(SUBSIDY, to)?], 122 | }; 123 | tx.id = tx.hash()?; 124 | Ok(tx) 125 | } 126 | 127 | /// IsCoinbase checks whether the transaction is coinbase 128 | pub fn is_coinbase(&self) -> bool { 129 | self.vin.len() == 1 && self.vin[0].txid.is_empty() && self.vin[0].vout == -1 130 | } 131 | 132 | /// Verify verifies signatures of Transaction inputs 133 | pub fn verify(&self, prev_TXs: HashMap) -> Result { 134 | if self.is_coinbase() { 135 | return Ok(true); 136 | } 137 | 138 | for vin in &self.vin { 139 | if prev_TXs.get(&vin.txid).unwrap().id.is_empty() { 140 | return Err(format_err!("ERROR: Previous transaction is not correct")); 141 | } 142 | } 143 | 144 | let mut tx_copy = self.trim_copy(); 145 | 146 | for in_id in 0..self.vin.len() { 147 | let prev_Tx = prev_TXs.get(&self.vin[in_id].txid).unwrap(); 148 | tx_copy.vin[in_id].signature.clear(); 149 | tx_copy.vin[in_id].pub_key = prev_Tx.vout[self.vin[in_id].vout as usize] 150 | .pub_key_hash 151 | .clone(); 152 | tx_copy.id = tx_copy.hash()?; 153 | tx_copy.vin[in_id].pub_key = Vec::new(); 154 | 155 | if !ed25519::verify( 156 | &tx_copy.id.as_bytes(), 157 | &self.vin[in_id].pub_key, 158 | &self.vin[in_id].signature, 159 | ) { 160 | return Ok(false); 161 | } 162 | } 163 | 164 | Ok(true) 165 | } 166 | 167 | /// Sign signs each input of a Transaction 168 | pub fn sign( 169 | &mut self, 170 | private_key: &[u8], 171 | prev_TXs: HashMap, 172 | ) -> Result<()> { 173 | if self.is_coinbase() { 174 | return Ok(()); 175 | } 176 | 177 | for vin in &self.vin { 178 | if prev_TXs.get(&vin.txid).unwrap().id.is_empty() { 179 | return Err(format_err!("ERROR: Previous transaction is not correct")); 180 | } 181 | } 182 | 183 | let mut tx_copy = self.trim_copy(); 184 | 185 | for in_id in 0..tx_copy.vin.len() { 186 | let prev_Tx = prev_TXs.get(&tx_copy.vin[in_id].txid).unwrap(); 187 | tx_copy.vin[in_id].signature.clear(); 188 | tx_copy.vin[in_id].pub_key = prev_Tx.vout[tx_copy.vin[in_id].vout as usize] 189 | .pub_key_hash 190 | .clone(); 191 | tx_copy.id = tx_copy.hash()?; 192 | tx_copy.vin[in_id].pub_key = Vec::new(); 193 | let signature = ed25519::signature(tx_copy.id.as_bytes(), private_key); 194 | self.vin[in_id].signature = signature.to_vec(); 195 | } 196 | 197 | Ok(()) 198 | } 199 | 200 | /// Hash returns the hash of the Transaction 201 | pub fn hash(&self) -> Result { 202 | let mut copy = self.clone(); 203 | copy.id = String::new(); 204 | let data = serialize(©)?; 205 | let mut hasher = Sha256::new(); 206 | hasher.input(&data[..]); 207 | Ok(hasher.result_str()) 208 | } 209 | 210 | /// TrimmedCopy creates a trimmed copy of Transaction to be used in signing 211 | fn trim_copy(&self) -> Transaction { 212 | let mut vin = Vec::new(); 213 | let mut vout = Vec::new(); 214 | 215 | for v in &self.vin { 216 | vin.push(TXInput { 217 | txid: v.txid.clone(), 218 | vout: v.vout.clone(), 219 | signature: Vec::new(), 220 | pub_key: Vec::new(), 221 | }) 222 | } 223 | 224 | for v in &self.vout { 225 | vout.push(TXOutput { 226 | value: v.value, 227 | pub_key_hash: v.pub_key_hash.clone(), 228 | }) 229 | } 230 | 231 | Transaction { 232 | id: self.id.clone(), 233 | vin, 234 | vout, 235 | } 236 | } 237 | } 238 | 239 | impl TXOutput { 240 | /// IsLockedWithKey checks if the output can be used by the owner of the pubkey 241 | pub fn is_locked_with_key(&self, pub_key_hash: &[u8]) -> bool { 242 | self.pub_key_hash == pub_key_hash 243 | } 244 | /// Lock signs the output 245 | fn lock(&mut self, address: &str) -> Result<()> { 246 | let pub_key_hash = Address::decode(address).unwrap().body; 247 | debug!("lock: {}", address); 248 | self.pub_key_hash = pub_key_hash; 249 | Ok(()) 250 | } 251 | 252 | pub fn new(value: i32, address: String) -> Result { 253 | let mut txo = TXOutput { 254 | value, 255 | pub_key_hash: Vec::new(), 256 | }; 257 | txo.lock(&address)?; 258 | Ok(txo) 259 | } 260 | } 261 | 262 | #[cfg(test)] 263 | mod test { 264 | use super::*; 265 | 266 | #[test] 267 | fn test_signature() { 268 | let mut ws = Wallets::new().unwrap(); 269 | let wa1 = ws.create_wallet(); 270 | let w = ws.get_wallet(&wa1).unwrap().clone(); 271 | ws.save_all().unwrap(); 272 | drop(ws); 273 | 274 | let data = String::from("test"); 275 | let tx = Transaction::new_coinbase(wa1, data).unwrap(); 276 | assert!(tx.is_coinbase()); 277 | 278 | let signature = ed25519::signature(tx.id.as_bytes(), &w.secret_key); 279 | assert!(ed25519::verify(tx.id.as_bytes(), &w.public_key, &signature)); 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /src/txn.rs: -------------------------------------------------------------------------------- 1 | use crate::errors::Result; 2 | use crate::transaction::Transaction; 3 | use crate::wallet::hash_pub_key; 4 | use bitcoincash_addr::Address; 5 | use failure::format_err; 6 | use log::debug; 7 | use serde::{Deserialize, Serialize}; 8 | use std::collections::HashMap; 9 | // TXOutputs collects TXOutput 10 | #[derive(Serialize, Deserialize, Debug, Clone)] 11 | pub struct TXOutputs { 12 | pub outputs: Vec, 13 | } 14 | 15 | /// TXInput represents a transaction input 16 | #[derive(Serialize, Deserialize, Debug, Clone)] 17 | pub struct TXInput { 18 | pub txid: String, 19 | pub vout: i32, 20 | pub signature: Vec, 21 | pub pub_key: Vec, 22 | } 23 | 24 | /// TXOutput represents a transaction output 25 | #[derive(Serialize, Deserialize, Debug, Clone)] 26 | pub struct TXOutput { 27 | pub value: i32, 28 | pub pub_key_hash: Vec, 29 | } 30 | impl TXInput { 31 | /// CanUnlockOutputWith checks whether the address initiated the transaction 32 | pub fn can_unlock_output_with(&self, unlocking_data: &[u8]) -> bool { 33 | let mut pubkeyhash = self.pub_key.clone(); 34 | hash_pub_key(&mut pubkeyhash); 35 | pubkeyhash == unlocking_data 36 | } 37 | } 38 | 39 | impl TXOutput { 40 | /// CanBeUnlockedWith checks if the output can be unlocked with the provided data 41 | pub fn can_be_unlock_with(&self, unlocking_data: &[u8]) -> bool { 42 | self.pub_key_hash == unlocking_data 43 | } 44 | 45 | /// Lock signs the output 46 | fn lock(&mut self, address: &str) -> Result<()> { 47 | let pub_key_hash = Address::decode(address).unwrap().body; 48 | debug!("lock: {}", address); 49 | self.pub_key_hash = pub_key_hash; 50 | Ok(()) 51 | } 52 | 53 | pub fn new(value: i32, address: String) -> Result { 54 | let mut txo = TXOutput { 55 | value, 56 | pub_key_hash: Vec::new(), 57 | }; 58 | txo.lock(&address)?; 59 | Ok(txo) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/utxoset.rs: -------------------------------------------------------------------------------- 1 | //! unspend transaction output set 2 | 3 | use super::*; 4 | use crate::block::*; 5 | use crate::blockchain::*; 6 | use crate::transaction::*; 7 | use bincode::{deserialize, serialize}; 8 | use sled; 9 | use std::collections::HashMap; 10 | 11 | /// UTXOSet represents UTXO set 12 | pub struct UTXOSet { 13 | pub blockchain: Blockchain, 14 | } 15 | 16 | impl UTXOSet { 17 | /// FindUnspentTransactions returns a list of transactions containing unspent outputs 18 | pub fn find_spendable_outputs( 19 | &self, 20 | pub_key_hash: &[u8], 21 | amount: i32, 22 | ) -> Result<(i32, HashMap>)> { 23 | let mut unspent_outputs: HashMap> = HashMap::new(); 24 | let mut accumulated = 0; 25 | 26 | let db = sled::open("data/utxos")?; 27 | for kv in db.iter() { 28 | let (k, v) = kv?; 29 | let txid = String::from_utf8(k.to_vec())?; 30 | let outs: TXOutputs = deserialize(&v.to_vec())?; 31 | 32 | for out_idx in 0..outs.outputs.len() { 33 | if outs.outputs[out_idx].is_locked_with_key(pub_key_hash) && accumulated < amount { 34 | accumulated += outs.outputs[out_idx].value; 35 | match unspent_outputs.get_mut(&txid) { 36 | Some(v) => v.push(out_idx as i32), 37 | None => { 38 | unspent_outputs.insert(txid.clone(), vec![out_idx as i32]); 39 | } 40 | } 41 | } 42 | } 43 | } 44 | 45 | Ok((accumulated, unspent_outputs)) 46 | } 47 | 48 | /// FindUTXO finds UTXO for a public key hash 49 | pub fn find_UTXO(&self, pub_key_hash: &[u8]) -> Result { 50 | let mut utxos = TXOutputs { 51 | outputs: Vec::new(), 52 | }; 53 | let db = sled::open("data/utxos")?; 54 | 55 | for kv in db.iter() { 56 | let (_, v) = kv?; 57 | let outs: TXOutputs = deserialize(&v.to_vec())?; 58 | 59 | for out in outs.outputs { 60 | if out.is_locked_with_key(pub_key_hash) { 61 | utxos.outputs.push(out.clone()) 62 | } 63 | } 64 | } 65 | 66 | Ok(utxos) 67 | } 68 | 69 | /// CountTransactions returns the number of transactions in the UTXO set 70 | pub fn count_transactions(&self) -> Result { 71 | let mut counter = 0; 72 | let db = sled::open("data/utxos")?; 73 | for kv in db.iter() { 74 | kv?; 75 | counter += 1; 76 | } 77 | Ok(counter) 78 | } 79 | 80 | /// Reindex rebuilds the UTXO set 81 | pub fn reindex(&self) -> Result<()> { 82 | std::fs::remove_dir_all("data/utxos").ok(); 83 | let db = sled::open("data/utxos")?; 84 | 85 | let utxos = self.blockchain.find_UTXO(); 86 | 87 | for (txid, outs) in utxos { 88 | db.insert(txid.as_bytes(), serialize(&outs)?)?; 89 | } 90 | 91 | Ok(()) 92 | } 93 | 94 | /// Update updates the UTXO set with transactions from the Block 95 | /// 96 | /// The Block is considered to be the tip of a blockchain 97 | pub fn update(&self, block: &Block) -> Result<()> { 98 | let db = sled::open("data/utxos")?; 99 | 100 | for tx in block.get_transaction() { 101 | if !tx.is_coinbase() { 102 | for vin in &tx.vin { 103 | let mut update_outputs = TXOutputs { 104 | outputs: Vec::new(), 105 | }; 106 | let outs: TXOutputs = deserialize(&db.get(&vin.txid)?.unwrap().to_vec())?; 107 | for out_idx in 0..outs.outputs.len() { 108 | if out_idx != vin.vout as usize { 109 | update_outputs.outputs.push(outs.outputs[out_idx].clone()); 110 | } 111 | } 112 | 113 | if update_outputs.outputs.is_empty() { 114 | db.remove(&vin.txid)?; 115 | } else { 116 | db.insert(vin.txid.as_bytes(), serialize(&update_outputs)?)?; 117 | } 118 | } 119 | } 120 | 121 | let mut new_outputs = TXOutputs { 122 | outputs: Vec::new(), 123 | }; 124 | for out in &tx.vout { 125 | new_outputs.outputs.push(out.clone()); 126 | } 127 | 128 | db.insert(tx.id.as_bytes(), serialize(&new_outputs)?)?; 129 | } 130 | Ok(()) 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/wallet.rs: -------------------------------------------------------------------------------- 1 | use crate::errors::Result; 2 | use bitcoincash_addr::{Address, HashType, Scheme}; 3 | use crypto::ed25519; 4 | use crypto::ripemd160::Ripemd160; 5 | use crypto::{digest::Digest, sha2::Sha256}; 6 | use log::info; 7 | use rand::{rngs::OsRng, RngCore}; 8 | use serde::{Deserialize, Serialize}; 9 | use std::collections::HashMap; 10 | 11 | #[derive(Deserialize, Serialize, Debug, Clone, PartialEq)] 12 | pub struct Wallet { 13 | pub secret_key: Vec, 14 | pub public_key: Vec, 15 | } 16 | 17 | impl Wallet { 18 | pub fn new() -> Self { 19 | let mut key: [u8; 32] = [0; 32]; 20 | OsRng.fill_bytes(&mut key); 21 | let (secret_key, public_key) = ed25519::keypair(&key); 22 | let secret_key = secret_key.to_vec(); 23 | let public_key = public_key.to_vec(); 24 | Wallet { 25 | secret_key, 26 | public_key, 27 | } 28 | } 29 | 30 | pub fn get_address(&self) -> String { 31 | let mut pub_hash = self.public_key.clone(); 32 | hash_pub_key(&mut pub_hash); 33 | let address = Address { 34 | body: pub_hash, 35 | scheme: Scheme::Base58, 36 | hash_type: HashType::Script, 37 | ..Default::default() 38 | }; 39 | address.encode().unwrap() 40 | } 41 | } 42 | 43 | pub fn hash_pub_key(pub_key: &mut Vec) { 44 | let mut hasher1 = Sha256::new(); 45 | hasher1.input(pub_key); 46 | hasher1.result(pub_key); 47 | let mut hasher2 = Ripemd160::new(); 48 | hasher2.input(pub_key); 49 | pub_key.resize(20, 0); 50 | hasher2.result(pub_key); 51 | } 52 | 53 | pub struct Wallets { 54 | wallets: HashMap, 55 | } 56 | 57 | impl Wallets { 58 | pub fn new() -> Result { 59 | let mut wlt = Wallets { 60 | wallets: HashMap::::new(), 61 | }; 62 | 63 | let db = sled::open("data/wallets")?; 64 | for item in db.into_iter() { 65 | let i = item?; 66 | let address = String::from_utf8(i.0.to_vec())?; 67 | let wallet = bincode::deserialize(&i.1.to_vec())?; 68 | wlt.wallets.insert(address, wallet); 69 | } 70 | drop(db); 71 | Ok(wlt) 72 | } 73 | 74 | pub fn create_wallet(&mut self) -> String { 75 | let wallet = Wallet::new(); 76 | let address = wallet.get_address(); 77 | self.wallets.insert(address.clone(), wallet); 78 | info!("Create wallet :{}", address); 79 | address 80 | } 81 | 82 | pub fn get_all_addresses(&self) -> Vec { 83 | let mut addresses = Vec::new(); 84 | for (address, _) in &self.wallets { 85 | addresses.push(address.clone()) 86 | } 87 | addresses 88 | } 89 | 90 | pub fn get_wallet(&self, address: &str) -> Option<&Wallet> { 91 | self.wallets.get(address) 92 | } 93 | 94 | pub fn save_all(&self) -> Result<()> { 95 | let db = sled::open("data/wallets")?; 96 | 97 | for (address, wallet) in &self.wallets { 98 | let data = bincode::serialize(wallet)?; 99 | db.insert(address, data)?; 100 | } 101 | db.flush()?; 102 | drop(db); 103 | Ok(()) 104 | } 105 | } 106 | --------------------------------------------------------------------------------