broken state
This commit is contained in:
parent
82ca86f03d
commit
d63ae5aa55
199
node/Cargo.lock
generated
199
node/Cargo.lock
generated
@ -126,6 +126,12 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.9.3"
|
version = "2.9.3"
|
||||||
@ -151,22 +157,19 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"crossterm 0.29.0",
|
"crossterm 0.29.0",
|
||||||
"hex",
|
"hex",
|
||||||
"jemalloc",
|
|
||||||
"jemallocator",
|
|
||||||
"memory-stats",
|
"memory-stats",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
|
"sled",
|
||||||
"textwrap",
|
"textwrap",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-tungstenite",
|
"tokio-tungstenite",
|
||||||
"uuid",
|
"uuid",
|
||||||
"vlogger",
|
"vlogger",
|
||||||
"wasm-bindgen",
|
|
||||||
"web-sys",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -175,6 +178,12 @@ version = "3.19.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.10.1"
|
version = "1.10.1"
|
||||||
@ -318,16 +327,40 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc32fast"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossterm"
|
name = "crossterm"
|
||||||
version = "0.28.1"
|
version = "0.28.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
|
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.9.3",
|
||||||
"crossterm_winapi",
|
"crossterm_winapi",
|
||||||
"mio",
|
"mio",
|
||||||
"parking_lot",
|
"parking_lot 0.12.4",
|
||||||
"rustix 0.38.44",
|
"rustix 0.38.44",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
"signal-hook-mio",
|
"signal-hook-mio",
|
||||||
@ -340,12 +373,12 @@ version = "0.29.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
|
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.9.3",
|
||||||
"crossterm_winapi",
|
"crossterm_winapi",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"document-features",
|
"document-features",
|
||||||
"mio",
|
"mio",
|
||||||
"parking_lot",
|
"parking_lot 0.12.4",
|
||||||
"rustix 1.0.8",
|
"rustix 1.0.8",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
"signal-hook-mio",
|
"signal-hook-mio",
|
||||||
@ -486,6 +519,16 @@ version = "0.1.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fs2"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-core"
|
name = "futures-core"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@ -518,6 +561,15 @@ dependencies = [
|
|||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fxhash"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.7"
|
version = "0.14.7"
|
||||||
@ -635,13 +687,22 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "instant"
|
||||||
|
version = "0.1.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-uring"
|
name = "io-uring"
|
||||||
version = "0.7.10"
|
version = "0.7.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b"
|
checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.9.3",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
@ -667,35 +728,6 @@ version = "1.0.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jemalloc"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dd5927dc2630919333682c1757ed2d1518f510336096d0f43f0426a0f776f8e6"
|
|
||||||
dependencies = [
|
|
||||||
"loca",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jemalloc-sys"
|
|
||||||
version = "0.5.4+5.3.0-patched"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ac6c1946e1cea1788cbfde01c993b52a10e2da07f4bac608228d1bed20bfebf2"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jemallocator"
|
|
||||||
version = "0.5.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a0de374a9f8e63150e6f5e8a60cc14c668226d7a347d8aee1a45766e3c4dd3bc"
|
|
||||||
dependencies = [
|
|
||||||
"jemalloc-sys",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.77"
|
version = "0.3.77"
|
||||||
@ -730,15 +762,6 @@ version = "0.4.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed"
|
checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "loca"
|
|
||||||
version = "0.7.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "669389264b730a98e0d11713d0260f0ab363ad7e50ffc6d36db9c75bb7a5e527"
|
|
||||||
dependencies = [
|
|
||||||
"ptr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.13"
|
version = "0.4.13"
|
||||||
@ -831,6 +854,17 @@ version = "1.70.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
|
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
|
||||||
|
dependencies = [
|
||||||
|
"instant",
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot_core 0.8.6",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.4"
|
version = "0.12.4"
|
||||||
@ -838,7 +872,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
|
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lock_api",
|
"lock_api",
|
||||||
"parking_lot_core",
|
"parking_lot_core 0.9.11",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.8.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"instant",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall 0.2.16",
|
||||||
|
"smallvec",
|
||||||
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -849,7 +897,7 @@ checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall",
|
"redox_syscall 0.5.17",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
@ -890,12 +938,6 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ptr"
|
|
||||||
version = "0.2.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "76208293e44a44a702aa22f5cf40e2e55f4a6cd19953ddadb0b3f68e337d87ce"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.40"
|
version = "1.0.40"
|
||||||
@ -946,7 +988,7 @@ version = "0.29.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b"
|
checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.9.3",
|
||||||
"cassowary",
|
"cassowary",
|
||||||
"compact_str",
|
"compact_str",
|
||||||
"crossterm 0.28.1",
|
"crossterm 0.28.1",
|
||||||
@ -961,13 +1003,22 @@ dependencies = [
|
|||||||
"unicode-width 0.2.0",
|
"unicode-width 0.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.17"
|
version = "0.5.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
|
checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.9.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -982,7 +1033,7 @@ version = "0.38.44"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.9.3",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys 0.4.15",
|
"linux-raw-sys 0.4.15",
|
||||||
@ -995,7 +1046,7 @@ version = "1.0.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
|
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.9.3",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys 0.9.4",
|
"linux-raw-sys 0.9.4",
|
||||||
@ -1116,6 +1167,22 @@ version = "0.4.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
|
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sled"
|
||||||
|
version = "0.34.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935"
|
||||||
|
dependencies = [
|
||||||
|
"crc32fast",
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"fs2",
|
||||||
|
"fxhash",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"parking_lot 0.11.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.15.1"
|
version = "1.15.1"
|
||||||
@ -1225,9 +1292,7 @@ dependencies = [
|
|||||||
"io-uring",
|
"io-uring",
|
||||||
"libc",
|
"libc",
|
||||||
"mio",
|
"mio",
|
||||||
"parking_lot",
|
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"signal-hook-registry",
|
|
||||||
"slab",
|
"slab",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
@ -1431,16 +1496,6 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "web-sys"
|
|
||||||
version = "0.3.77"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
|
|
||||||
dependencies = [
|
|
||||||
"js-sys",
|
|
||||||
"wasm-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
@ -1684,7 +1739,7 @@ version = "0.39.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.9.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@ -11,11 +11,9 @@ serde = { version = "1.0.219", features = ["derive"] }
|
|||||||
serde_json = "1.0.143"
|
serde_json = "1.0.143"
|
||||||
sha2 = "0.10.9"
|
sha2 = "0.10.9"
|
||||||
thiserror = "2.0.16"
|
thiserror = "2.0.16"
|
||||||
tokio = { version = "1.47.1", features = ["full"] }
|
tokio = { version = "1.47.1", features = ["rt-multi-thread", "net", "sync", "time", "macros"] }
|
||||||
tokio-tungstenite = "0.27.0"
|
tokio-tungstenite = "0.27.0"
|
||||||
uuid = { version = "1.18.0", features = ["v4", "serde"] }
|
uuid = { version = "1.18.0", features = ["v4", "serde"] }
|
||||||
wasm-bindgen = "0.2.100"
|
|
||||||
web-sys = { version = "0.3.77", features = ["WebSocket"] }
|
|
||||||
vlogger = { path = "./lib/logger-rs" }
|
vlogger = { path = "./lib/logger-rs" }
|
||||||
ratatui = "0.29.0"
|
ratatui = "0.29.0"
|
||||||
crossterm = "0.29.0"
|
crossterm = "0.29.0"
|
||||||
@ -23,6 +21,7 @@ once_cell = "1.21.3"
|
|||||||
async-trait = "0.1.89"
|
async-trait = "0.1.89"
|
||||||
anyhow = "1.0.99"
|
anyhow = "1.0.99"
|
||||||
memory-stats = "1.2.0"
|
memory-stats = "1.2.0"
|
||||||
jemalloc = "0.3.0"
|
# jemalloc = "0.3.0"
|
||||||
jemallocator = "0.5.4"
|
# jemallocator = "0.5.4"
|
||||||
textwrap = "0.16.2"
|
textwrap = "0.16.2"
|
||||||
|
sled = "0.34.7"
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"genesis_time": "2019-03-18T00:00:00.000000000Z",
|
|
||||||
"chain_id": "the-blockchain-bar-ledger",
|
|
||||||
"balances": {
|
|
||||||
"andrej": 1000000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"balances": {
|
|
||||||
"andrej": 998801
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"head": {
|
|
||||||
"previous_hash": "",
|
|
||||||
"timestamp": 1756502951,
|
|
||||||
"merkle_root": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
|
||||||
"block_hash": "04e5cdf592f6ab35dccdc41add80568c0de8be60609f5f9053cbe0d156fb8ea0",
|
|
||||||
"nonce": 6
|
|
||||||
},
|
|
||||||
"data": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"head": {
|
|
||||||
"previous_hash": "04e5cdf592f6ab35dccdc41add80568c0de8be60609f5f9053cbe0d156fb8ea0",
|
|
||||||
"timestamp": 1756503577,
|
|
||||||
"merkle_root": "3f99805580f3b4bbd1a903180d2057cfd7dace97d820d7170ffd83c95ce3852c",
|
|
||||||
"block_hash": "0655c8ab80aeda9ed1db6e72ec48cdf9d9c7799356b11519979f3d7dbce57677",
|
|
||||||
"nonce": 7
|
|
||||||
},
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"Transaction": {
|
|
||||||
"from": "victor",
|
|
||||||
"to": "pia",
|
|
||||||
"value": 500,
|
|
||||||
"data": "almosen"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
4
node/proc/09:09:13_6977
Normal file
4
node/proc/09:09:13_6977
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[INFO ] [watcher.rs :60 ] 09:09:43: Physical memory usage: 9 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:09:43: Virtual memory usage: 1066 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:10:13: Physical memory usage: 9 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:10:13: Virtual memory usage: 1066 MB
|
||||||
30
node/proc/09:12:45_7340
Normal file
30
node/proc/09:12:45_7340
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
[INFO ] [watcher.rs :60 ] 09:12:55: Physical memory usage: 5 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:12:55: Virtual memory usage: 1062 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:13:05: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:13:05: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:13:15: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:13:15: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:13:25: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:13:25: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:13:35: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:13:35: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:13:45: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:13:45: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:13:55: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:13:55: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:14:05: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:14:05: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:14:15: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:14:15: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:14:25: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:14:25: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:14:35: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:14:35: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:14:45: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:14:45: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:14:55: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:14:55: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:15:05: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:15:05: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:15:15: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:15:15: Virtual memory usage: 1063 MB
|
||||||
410
node/proc/09:15:49_17386
Normal file
410
node/proc/09:15:49_17386
Normal file
@ -0,0 +1,410 @@
|
|||||||
|
[INFO ] [watcher.rs :60 ] 09:15:59: Physical memory usage: 5 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:15:59: Virtual memory usage: 1062 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:16:09: Physical memory usage: 5 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:16:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:16:19: Physical memory usage: 5 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:16:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:16:29: Physical memory usage: 5 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:16:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:16:39: Physical memory usage: 5 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:16:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:16:49: Physical memory usage: 5 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:16:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:16:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:16:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:17:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:17:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:17:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:17:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:17:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:17:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:17:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:17:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:17:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:17:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:17:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:17:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:18:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:18:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:18:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:18:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:18:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:18:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:18:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:18:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:18:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:18:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:18:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:18:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:19:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:19:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:19:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:19:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:19:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:19:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:19:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:19:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:19:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:19:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:19:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:19:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:20:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:20:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:20:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:20:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:20:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:20:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:20:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:20:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:20:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:20:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:20:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:20:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:21:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:21:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:21:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:21:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:21:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:21:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:21:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:21:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:21:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:21:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:21:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:21:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:22:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:22:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:22:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:22:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:22:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:22:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:22:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:22:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:22:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:22:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:22:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:22:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:23:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:23:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:23:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:23:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:23:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:23:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:23:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:23:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:23:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:23:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:23:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:23:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:24:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:24:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:24:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:24:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:24:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:24:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:24:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:24:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:24:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:24:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:24:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:24:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:25:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:25:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:25:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:25:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:25:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:25:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:25:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:25:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:25:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:25:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:25:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:25:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:26:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:26:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:26:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:26:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:26:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:26:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:26:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:26:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:26:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:26:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:26:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:26:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:27:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:27:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:27:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:27:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:27:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:27:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:27:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:27:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:27:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:27:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:27:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:27:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:28:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:28:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:28:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:28:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:28:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:28:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:28:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:28:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:28:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:28:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:28:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:28:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:29:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:29:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:29:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:29:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:29:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:29:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:29:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:29:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:29:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:29:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:29:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:29:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:30:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:30:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:30:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:30:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:30:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:30:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:30:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:30:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:30:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:30:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:30:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:30:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:31:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:31:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:31:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:31:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:31:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:31:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:31:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:31:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:31:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:31:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:31:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:31:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:32:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:32:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:32:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:32:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:32:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:32:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:32:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:32:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:32:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:32:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:32:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:32:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:33:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:33:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:33:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:33:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:33:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:33:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:33:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:33:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:33:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:33:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:33:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:33:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:34:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:34:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:34:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:34:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:34:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:34:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:34:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:34:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:34:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:34:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:34:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:34:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:35:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:35:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:35:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:35:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:35:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:35:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:35:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:35:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:35:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:35:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:35:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:35:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:36:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:36:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:36:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:36:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:36:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:36:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:36:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:36:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:36:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:36:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:36:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:36:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:37:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:37:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:37:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:37:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:37:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:37:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:37:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:37:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:37:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:37:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:37:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:37:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:38:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:38:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:38:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:38:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:38:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:38:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:38:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:38:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:38:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:38:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:38:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:38:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:39:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:39:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:39:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:39:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:39:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:39:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:39:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:39:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:39:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:39:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:39:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:39:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:40:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:40:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:40:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:40:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:40:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:40:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:40:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:40:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:40:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:40:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:40:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:40:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:41:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:41:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:41:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:41:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:41:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:41:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:41:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:41:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:41:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:41:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:41:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:41:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:42:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:42:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:42:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:42:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:42:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:42:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:42:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:42:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:42:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:42:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:42:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:42:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:43:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:43:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:43:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:43:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:43:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:43:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:43:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:43:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:43:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:43:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:43:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:43:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:44:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:44:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:44:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:44:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:44:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:44:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:44:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:44:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:44:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:44:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:44:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:44:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:45:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:45:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:45:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:45:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:45:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:45:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:45:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:45:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:45:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:45:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:45:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:45:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:46:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:46:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:46:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:46:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:46:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:46:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:46:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:46:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:46:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:46:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:46:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:46:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:47:09: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:47:09: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:47:19: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:47:19: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:47:29: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:47:29: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:47:39: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:47:39: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:47:49: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:47:49: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:47:59: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:47:59: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:48:10: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:48:10: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:48:20: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:48:20: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:48:30: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:48:30: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:48:40: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:48:40: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:48:50: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:48:50: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:49:00: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:49:00: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:49:10: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:49:10: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:49:20: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:49:20: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:49:30: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:49:30: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:49:40: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:49:40: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:49:50: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:49:50: Virtual memory usage: 1063 MB
|
||||||
|
[INFO ] [watcher.rs :60 ] 09:50:00: Physical memory usage: 6 MB
|
||||||
|
[INFO ] [watcher.rs :61 ] 09:50:00: Virtual memory usage: 1063 MB
|
||||||
0
node/src/api/server.rs
Normal file
0
node/src/api/server.rs
Normal file
219
node/src/args.rs
219
node/src/args.rs
@ -1,180 +1,171 @@
|
|||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
|
||||||
use crate::core;
|
use crate::core;
|
||||||
use crate::watcher::{RenderLayoutKind, RenderPane};
|
use crate::watcher::{RenderLayoutKind, RenderPane};
|
||||||
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
use clap::*;
|
use clap::*;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
pub command: CliCommand
|
pub command: CliCommand,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum CliCommand{
|
pub enum CliCommand {
|
||||||
#[command(name = "ping")]
|
#[command(name = "ping")]
|
||||||
Ping{
|
Ping {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
ping_cmd: CliPingCommand
|
ping_cmd: CliPingCommand,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Peer related Cmd
|
/// Peer related Cmd
|
||||||
#[command(name = "peer")]
|
#[command(name = "peer")]
|
||||||
Peer {
|
Peer {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
peer_cmd: CliPeerCommand
|
peer_cmd: CliPeerCommand,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Block related Cmd
|
/// Block related Cmd
|
||||||
#[command(name = "block")]
|
#[command(name = "block")]
|
||||||
Block {
|
Block {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
block_cmd: CliBlockCommand
|
block_cmd: CliBlockCommand,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Make a Transaction
|
/// Make a Transaction
|
||||||
#[command(name = "tx")]
|
#[command(name = "tx")]
|
||||||
Transaction(core::Tx),
|
Transaction(core::Tx),
|
||||||
|
|
||||||
/// Start new TcpListner on Addr
|
/// Start new TcpListner on Addr
|
||||||
#[command(name = "listen")]
|
#[command(name = "listen")]
|
||||||
StartListner{addr: String},
|
StartListner { addr: String },
|
||||||
|
|
||||||
/// Display Node id
|
/// Display Node id
|
||||||
#[command(name = "id")]
|
#[command(name = "id")]
|
||||||
DebugShowId,
|
DebugShowId,
|
||||||
|
|
||||||
/// Connect to Seed Nodes
|
/// Connect to Seed Nodes
|
||||||
#[command(name = "seed")]
|
#[command(name = "seed")]
|
||||||
Seeds {
|
Seeds {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
seed_cmd: CliSeedCommand
|
seed_cmd: CliSeedCommand,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Clear Pane
|
/// Clear Pane
|
||||||
#[command(name = "clear", aliases = ["c"])]
|
#[command(name = "clear", aliases = ["c"])]
|
||||||
Clear{
|
Clear { pane: RenderPane },
|
||||||
pane: RenderPane
|
|
||||||
},
|
|
||||||
|
|
||||||
#[command(name = "layout", aliases = ["lay"])]
|
#[command(name = "layout", aliases = ["lay"])]
|
||||||
Layout {
|
Layout { mode: RenderLayoutKind },
|
||||||
mode: RenderLayoutKind
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum CliPeerCommand {
|
pub enum CliPeerCommand {
|
||||||
/// Connect To Peer With IpAddr
|
/// Connect To Peer With IpAddr
|
||||||
#[command(name = "connect", aliases = ["c", "con"])]
|
#[command(name = "connect", aliases = ["c", "con"])]
|
||||||
Connect{addr: String},
|
Connect { addr: String },
|
||||||
|
|
||||||
/// Remove Peer Connection
|
/// Remove Peer Connection
|
||||||
#[command(name = "remove", aliases = ["rm"])]
|
#[command(name = "remove", aliases = ["rm"])]
|
||||||
Remove{id: String},
|
Remove { id: String },
|
||||||
|
|
||||||
/// List Connected Peers
|
|
||||||
#[command(name = "list", aliases = ["ls", "l"])]
|
|
||||||
List,
|
|
||||||
|
|
||||||
|
/// List Connected Peers
|
||||||
|
#[command(name = "list", aliases = ["ls", "l"])]
|
||||||
|
List,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum CliSeedCommand {
|
pub enum CliSeedCommand {
|
||||||
/// Connect to Seed nodes
|
/// Connect to Seed nodes
|
||||||
#[command(name = "connect", aliases = ["c", "con"])]
|
#[command(name = "connect", aliases = ["c", "con"])]
|
||||||
Connect
|
Connect,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum CliBlockCommand {
|
pub enum CliBlockCommand {
|
||||||
/// List Blocks in Chain
|
/// List Blocks in Chain
|
||||||
#[command(name = "list", aliases = ["ls", "l"])]
|
#[command(name = "list", aliases = ["ls", "l"])]
|
||||||
List,
|
List,
|
||||||
|
|
||||||
/// Create and Broadcast new Block
|
/// Create and Broadcast new Block
|
||||||
#[command(name = "create", aliases = ["c", "new"])]
|
#[command(name = "create", aliases = ["c", "new"])]
|
||||||
Create,
|
Create,
|
||||||
|
|
||||||
/// Export Blocks to file
|
/// Export Blocks to file
|
||||||
#[command(name = "dump", aliases = ["export"])]
|
#[command(name = "dump", aliases = ["export"])]
|
||||||
Dump {
|
Dump {
|
||||||
/// Output file
|
/// Output file
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
output: String
|
output: String,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum CliPingCommand {
|
pub enum CliPingCommand {
|
||||||
|
/// Ping Peer by Id
|
||||||
|
#[command(name = "id", aliases = ["i"])]
|
||||||
|
Id {
|
||||||
|
#[arg(short, long)]
|
||||||
|
id: String,
|
||||||
|
},
|
||||||
|
|
||||||
/// Ping Peer by Id
|
/// Ping Peer by Address
|
||||||
#[command(name = "id", aliases = ["i"])]
|
#[command(name = "addr", aliases = ["a", "ad"])]
|
||||||
Id {
|
Addr {
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
id: String
|
addr: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Ping Peer by Address
|
|
||||||
#[command(name = "addr", aliases = ["a", "ad"])]
|
|
||||||
Addr {
|
|
||||||
#[arg(short, long)]
|
|
||||||
addr: String
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
#[command(name = "node")]
|
#[command(name = "node")]
|
||||||
#[command(about = "A blockchain node CLI tool")]
|
#[command(about = "A blockchain node CLI tool")]
|
||||||
#[command(version = "1.0")]
|
#[command(version = "1.0")]
|
||||||
#[command(long_about = "A comprehensive CLI tool for managing blockchain nodes, peers, and transactions")]
|
#[command(
|
||||||
pub enum CliNodeCommand {
|
long_about = "A comprehensive CLI tool for managing blockchain nodes, peers, and transactions"
|
||||||
}
|
)]
|
||||||
|
pub enum CliNodeCommand {}
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
pub struct CliArgs {
|
pub struct CliArgs {
|
||||||
/// Provide address on which node will listen
|
/// Provide address on which node will listen
|
||||||
#[arg(short = 'a', long)]
|
#[arg(short = 'a', long)]
|
||||||
pub addr: Option<SocketAddr>,
|
pub addr: Option<SocketAddr>,
|
||||||
|
|
||||||
/// Provide File with current chain
|
/// Provide File with current chain
|
||||||
#[arg(short = 'f', long)]
|
#[arg(short = 'd', long)]
|
||||||
pub seed_file: Option<String>,
|
pub database: Option<String>,
|
||||||
|
|
||||||
/// Enable bootstrap mode (alternative syntax)
|
/// Enable bootstrap mode (alternative syntax)
|
||||||
#[arg(short = 'b', long = "bootstrap", action = clap::ArgAction::SetTrue)]
|
#[arg(short = 'b', long = "bootstrap", action = clap::ArgAction::SetTrue)]
|
||||||
pub bootstrap: bool,
|
pub bootstrap: bool,
|
||||||
|
|
||||||
/// Enable debug mode (alternative syntax)
|
/// Enable debug mode (alternative syntax)
|
||||||
#[arg(short = 'd', long = "debug", action = clap::ArgAction::SetTrue)]
|
#[arg(long = "debug", action = clap::ArgAction::SetTrue)]
|
||||||
pub debug: bool,
|
pub debug: bool,
|
||||||
|
|
||||||
/// Enable rendering (alternative syntax)
|
/// Enable rendering (alternative syntax)
|
||||||
#[arg(short = 'r', long = "render", action = clap::ArgAction::Set, default_value = "true")]
|
#[arg(short = 'r', long = "render", action = clap::ArgAction::Set, default_value = "true")]
|
||||||
pub render: bool,
|
pub render: bool,
|
||||||
|
|
||||||
/// Enable debug mode (alternative syntax)
|
/// Enable debug mode (alternative syntax)
|
||||||
#[arg(short = 's', long = "seed", action = clap::ArgAction::SetTrue)]
|
#[arg(short = 's', long = "seed", action = clap::ArgAction::SetTrue)]
|
||||||
pub seed: bool,
|
pub seed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
pub enum Commands {
|
pub enum Commands {}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
pub enum TxCmd {
|
pub enum TxCmd {
|
||||||
/// Add a new transaction to the DB
|
/// Add a new transaction to the DB
|
||||||
#[command(short_flag = 'a')]
|
#[command(short_flag = 'a')]
|
||||||
Add(core::Tx)
|
Add(core::Tx),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_args() -> CliArgs {
|
pub fn get_args() -> CliArgs {
|
||||||
CliArgs::parse()
|
CliArgs::parse()
|
||||||
}
|
}
|
||||||
|
|||||||
29
node/src/bus/event_bus.rs
Normal file
29
node/src/bus/event_bus.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
|
pub struct EventBus<T>
|
||||||
|
where
|
||||||
|
T: Clone + std::fmt::Debug,
|
||||||
|
{
|
||||||
|
sender: broadcast::Sender<T>,
|
||||||
|
_receiver: broadcast::Receiver<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone + std::fmt::Debug> EventBus<T> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let (sender, receiver) = broadcast::channel(1000);
|
||||||
|
Self {
|
||||||
|
sender,
|
||||||
|
_receiver: receiver,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn publish(&self, event: T) {
|
||||||
|
if let Err(e) = self.sender.send(event) {
|
||||||
|
eprintln!("{e}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subscribe(&self) -> broadcast::Receiver<T> {
|
||||||
|
self.sender.subscribe()
|
||||||
|
}
|
||||||
|
}
|
||||||
17
node/src/bus/executor.rs
Normal file
17
node/src/bus/executor.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
|
use super::event_bus::EventBus;
|
||||||
|
use crate::watcher::ExecutorCommand;
|
||||||
|
|
||||||
|
static EXECUTOR_EVENT_BUS: Lazy<Arc<EventBus<ExecutorCommand>>> =
|
||||||
|
Lazy::new(|| Arc::new(EventBus::new()));
|
||||||
|
|
||||||
|
pub fn publish_executor_event(event: ExecutorCommand) {
|
||||||
|
EXECUTOR_EVENT_BUS.publish(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subscribe_executor_event() -> broadcast::Receiver<ExecutorCommand> {
|
||||||
|
EXECUTOR_EVENT_BUS.subscribe()
|
||||||
|
}
|
||||||
25
node/src/bus/network.rs
Normal file
25
node/src/bus/network.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
|
use super::event_bus::EventBus;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum NetworkEvent {
|
||||||
|
SeedConnected(String),
|
||||||
|
SeedDisconnected(String),
|
||||||
|
AllSeedsConnected,
|
||||||
|
BootstrapCompleted,
|
||||||
|
NodeReady,
|
||||||
|
}
|
||||||
|
|
||||||
|
static NETWORK_EVENT_BUS: Lazy<Arc<EventBus<NetworkEvent>>> =
|
||||||
|
Lazy::new(|| Arc::new(EventBus::new()));
|
||||||
|
|
||||||
|
pub fn publish_network_event(event: NetworkEvent) {
|
||||||
|
NETWORK_EVENT_BUS.publish(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subscribe_network_event() -> broadcast::Receiver<NetworkEvent> {
|
||||||
|
NETWORK_EVENT_BUS.subscribe()
|
||||||
|
}
|
||||||
17
node/src/bus/render.rs
Normal file
17
node/src/bus/render.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
|
use super::event_bus::EventBus;
|
||||||
|
use crate::watcher::renderer::RenderCommand;
|
||||||
|
|
||||||
|
static RENDER_CHANNEL: Lazy<Arc<EventBus<RenderCommand>>> = Lazy::new(|| Arc::new(EventBus::new()));
|
||||||
|
pub fn publish_render_event(event: RenderCommand) {
|
||||||
|
RENDER_CHANNEL.publish(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subscribe_render_event() -> broadcast::Receiver<RenderCommand> {
|
||||||
|
RENDER_CHANNEL.subscribe()
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
23
node/src/bus/system.rs
Normal file
23
node/src/bus/system.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
|
use super::event_bus::EventBus;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum SystemEvent {
|
||||||
|
ExecutorStarted,
|
||||||
|
RendererStarted,
|
||||||
|
NodeStarted,
|
||||||
|
Exit,
|
||||||
|
}
|
||||||
|
|
||||||
|
static SYSTEM_EVENT_BUS: Lazy<Arc<EventBus<SystemEvent>>> = Lazy::new(|| Arc::new(EventBus::new()));
|
||||||
|
|
||||||
|
pub fn publish_system_event(event: SystemEvent) {
|
||||||
|
SYSTEM_EVENT_BUS.publish(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subscribe_system_event() -> broadcast::Receiver<SystemEvent> {
|
||||||
|
SYSTEM_EVENT_BUS.subscribe()
|
||||||
|
}
|
||||||
@ -1,54 +1,61 @@
|
|||||||
use crate::args::*;
|
use crate::args::*;
|
||||||
use crate::core::NetworkData;
|
use crate::core::NetworkData;
|
||||||
use crate::event_bus::publish_render_event;
|
use crate::node::*;
|
||||||
use crate::native_node::node::*;
|
use crate::watcher::{RenderCommand, ExecutorCommand};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use crate::watcher::{ExecutorCommand, RenderCommand};
|
|
||||||
|
|
||||||
pub fn handle_peer_command(cmd: CliPeerCommand) -> NodeCommand {
|
pub fn handle_peer_command(cmd: CliPeerCommand) -> NodeCommand {
|
||||||
match cmd {
|
match cmd {
|
||||||
CliPeerCommand::List => NodeCommand::ListPeers,
|
CliPeerCommand::List => NodeCommand::ListPeers,
|
||||||
CliPeerCommand::Remove { id } => NodeCommand::RemovePeer{ peer_id: id.parse::<uuid::Uuid>().unwrap() },
|
CliPeerCommand::Remove { id } => NodeCommand::RemovePeer {
|
||||||
CliPeerCommand::Connect { addr } => NodeCommand::ConnectTcpPeer(addr),
|
peer_id: id.parse::<uuid::Uuid>().unwrap(),
|
||||||
}
|
},
|
||||||
|
CliPeerCommand::Connect { addr } => NodeCommand::ConnectTcpPeer(addr),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_block_command(cmd: CliBlockCommand) -> NodeCommand {
|
pub fn handle_block_command(cmd: CliBlockCommand) -> NodeCommand {
|
||||||
match cmd {
|
match cmd {
|
||||||
CliBlockCommand::List => NodeCommand::ListBlocks,
|
CliBlockCommand::List => NodeCommand::ListBlocks,
|
||||||
CliBlockCommand::Dump { output } => NodeCommand::DumpBlocks(output),
|
CliBlockCommand::Dump { output } => NodeCommand::DumpBlocks(output),
|
||||||
CliBlockCommand::Create => NodeCommand::CreateBlock,
|
CliBlockCommand::Create => NodeCommand::CreateBlock,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_seed_command(cmd: CliSeedCommand) -> NodeCommand {
|
fn handle_seed_command(cmd: CliSeedCommand) -> NodeCommand {
|
||||||
match cmd {
|
match cmd {
|
||||||
CliSeedCommand::Connect => NodeCommand::ConnectToSeeds
|
CliSeedCommand::Connect => NodeCommand::ConnectToSeeds,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_ping(cmd: CliPingCommand) -> NodeCommand {
|
fn handle_ping(cmd: CliPingCommand) -> NodeCommand {
|
||||||
match cmd {
|
match cmd {
|
||||||
CliPingCommand::Id {id} => NodeCommand::PingId(id),
|
CliPingCommand::Id { id } => NodeCommand::PingId(id),
|
||||||
CliPingCommand::Addr {addr} => NodeCommand::PingAddr(addr)
|
CliPingCommand::Addr { addr } => NodeCommand::PingAddr(addr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cli(input: &[&str]) -> ExecutorCommand {
|
pub fn cli(input: &[&str]) -> ExecutorCommand {
|
||||||
match Cli::try_parse_from(input) {
|
match Cli::try_parse_from(input) {
|
||||||
Ok(cmd) => match cmd.command {
|
Ok(cmd) => match cmd.command {
|
||||||
CliCommand::Layout { mode } => ExecutorCommand::Render(RenderCommand::ChangeLayout(mode)),
|
CliCommand::Layout { mode } => {
|
||||||
CliCommand::Clear { pane } => ExecutorCommand::Render(RenderCommand::ClearPane(pane)),
|
ExecutorCommand::Render(RenderCommand::ChangeLayout(mode))
|
||||||
CliCommand::Peer { peer_cmd } => ExecutorCommand::Node(handle_peer_command(peer_cmd)),
|
}
|
||||||
CliCommand::Block { block_cmd } => ExecutorCommand::Node(handle_block_command(block_cmd)),
|
CliCommand::Clear { pane } => ExecutorCommand::Render(RenderCommand::ClearPane(pane)),
|
||||||
CliCommand::Transaction(tx)=> ExecutorCommand::Node(NodeCommand::ProcessNetworkData(NetworkData::Transaction(tx))),
|
CliCommand::Peer { peer_cmd } => ExecutorCommand::Node(handle_peer_command(peer_cmd)),
|
||||||
CliCommand::DebugShowId => ExecutorCommand::Node(NodeCommand::ShowId),
|
CliCommand::Block { block_cmd } => {
|
||||||
CliCommand::StartListner { addr } => {
|
ExecutorCommand::Node(handle_block_command(block_cmd))
|
||||||
ExecutorCommand::Node(NodeCommand::StartListner(addr.parse().unwrap()))
|
}
|
||||||
},
|
CliCommand::Transaction(tx) => ExecutorCommand::Node(NodeCommand::ProcessNetworkData(
|
||||||
CliCommand::Seeds { seed_cmd } => ExecutorCommand::Node(handle_seed_command(seed_cmd)),
|
NetworkData::Transaction(tx),
|
||||||
CliCommand::Ping { ping_cmd } => ExecutorCommand::Node(handle_ping(ping_cmd))
|
)),
|
||||||
|
CliCommand::DebugShowId => ExecutorCommand::Node(NodeCommand::ShowId),
|
||||||
|
CliCommand::StartListner { addr } => {
|
||||||
|
ExecutorCommand::Node(NodeCommand::StartListner(addr.parse().unwrap()))
|
||||||
|
}
|
||||||
|
CliCommand::Seeds { seed_cmd } => ExecutorCommand::Node(handle_seed_command(seed_cmd)),
|
||||||
|
CliCommand::Ping { ping_cmd } => ExecutorCommand::Node(handle_ping(ping_cmd)),
|
||||||
|
},
|
||||||
|
Err(e) => ExecutorCommand::InvalidCommand(format!("{e}")),
|
||||||
}
|
}
|
||||||
Err(e) => ExecutorCommand::InvalidCommand(format!("{e}"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,13 +6,14 @@ pub struct BlockHeader {
|
|||||||
pub timestamp: u64,
|
pub timestamp: u64,
|
||||||
pub merkle_root: String,
|
pub merkle_root: String,
|
||||||
pub block_hash: String,
|
pub block_hash: String,
|
||||||
pub nonce: u32
|
pub nonce: u32,
|
||||||
|
pub height: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, Default)]
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, Default)]
|
||||||
pub struct Block {
|
pub struct Block {
|
||||||
pub head: BlockHeader,
|
pub head: BlockHeader,
|
||||||
pub data: Vec<NetworkData>
|
pub data: Vec<NetworkData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockHeader {
|
impl BlockHeader {
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
use sha2::Digest;
|
use sha2::Digest;
|
||||||
use sha2::Sha256;
|
use sha2::Sha256;
|
||||||
|
|
||||||
use vlogger::*;
|
|
||||||
use crate::core::NetworkData;
|
use crate::core::NetworkData;
|
||||||
use crate::error::print_error_chain;
|
use crate::error::print_error_chain;
|
||||||
use crate::log;
|
use crate::log;
|
||||||
|
use vlogger::*;
|
||||||
|
|
||||||
use crate::core;
|
use crate::core;
|
||||||
use crate::error::{ BlockchainError, TxError };
|
use crate::error::{BlockchainError, TxError};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::time::UNIX_EPOCH;
|
use std::time::UNIX_EPOCH;
|
||||||
@ -18,265 +18,273 @@ pub type Account = String;
|
|||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum ValidationError {
|
pub enum ValidationError {
|
||||||
#[error("Invalid Block Hash Detected")]
|
#[error("Invalid Block Hash Detected")]
|
||||||
InvalidBlockHash,
|
InvalidBlockHash,
|
||||||
#[error("Previous Block Hash doesn't match")]
|
#[error("Previous Block Hash doesn't match")]
|
||||||
InvalidPreviousBlockHash,
|
InvalidPreviousBlockHash,
|
||||||
#[error("Invalid Block JSON: {0}")]
|
#[error("Invalid Block JSON: {0}")]
|
||||||
InvalidBlockJson(#[from] serde_json::Error)
|
InvalidBlockJson(#[from] serde_json::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Blockchain {
|
pub struct Blockchain {
|
||||||
id: String,
|
id: String,
|
||||||
balances: std::collections::HashMap<Account, u32>,
|
balances: std::collections::HashMap<Account, u32>,
|
||||||
blocks: Vec<core::Block>,
|
mempool: Vec<NetworkData>,
|
||||||
mempool: Vec<NetworkData>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
impl Blockchain {
|
impl Blockchain {
|
||||||
pub fn add(&mut self, data: NetworkData) -> Result<(), BlockchainError> {
|
pub fn add(&mut self, data: NetworkData) -> Result<(), BlockchainError> {
|
||||||
self.apply(data.clone())?;
|
self.apply(data.clone())?;
|
||||||
self.mempool.push(data);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hash_network_data(data: &NetworkData) -> String {
|
|
||||||
match data {
|
|
||||||
NetworkData::Transaction(tx) => Self::hash_transaction(tx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hash_transaction(tx: &core::Tx) -> String {
|
|
||||||
let mut hasher = Sha256::new();
|
|
||||||
hasher.update(tx.to());
|
|
||||||
hasher.update(tx.from());
|
|
||||||
hasher.update(tx.value().to_be_bytes());
|
|
||||||
hasher.update(tx.data());
|
|
||||||
|
|
||||||
let res = hasher.finalize();
|
|
||||||
hex::encode(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn calculate_next_level(level: &[String]) -> Vec<String> {
|
|
||||||
let mut next_level = Vec::new();
|
|
||||||
|
|
||||||
for chunk in level.chunks(2) {
|
|
||||||
let combined_hash = if chunk.len() == 2 {
|
|
||||||
Self::hash_pair(&chunk[0], &chunk[1])
|
|
||||||
} else {
|
|
||||||
Self::hash_pair(&chunk[0], &chunk[0])
|
|
||||||
};
|
|
||||||
next_level.push(combined_hash);
|
|
||||||
}
|
|
||||||
next_level
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn calculate_merkle_root(tx: &[NetworkData]) -> String {
|
|
||||||
let tx_hashes: Vec<String> = tx
|
|
||||||
.iter()
|
|
||||||
.map(|tx| Blockchain::hash_network_data(tx))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if tx_hashes.is_empty() {
|
|
||||||
return Blockchain::hash_data("");
|
|
||||||
}
|
|
||||||
|
|
||||||
if tx_hashes.len() == 1 {
|
|
||||||
return tx_hashes[0].clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut current_level = tx_hashes.to_vec();
|
|
||||||
|
|
||||||
while current_level.len() > 1 {
|
|
||||||
current_level = Self::calculate_next_level(¤t_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
return current_level[0].clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hash_pair(left: &str, right: &str) -> String {
|
|
||||||
let combined = format!("{}{}", left, right);
|
|
||||||
Self::hash_data(&combined)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hash_data(data: &str) -> String {
|
|
||||||
let mut hasher = Sha256::new();
|
|
||||||
hasher.update(data.as_bytes());
|
|
||||||
hex::encode(hasher.finalize())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn acc_exists(&self, acc: &Account) -> bool {
|
|
||||||
self.balances.iter().find(|(k, _)| *k == acc).is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dump_blocks(&self, path: String) {
|
|
||||||
let block_json = serde_json::to_string_pretty(&self.blocks).unwrap();
|
|
||||||
if let Ok(mut db_file) = std::fs::OpenOptions::new().truncate(true).create(true).write(true).open(path) {
|
|
||||||
db_file.write_all(&block_json.as_bytes()).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_block(&mut self) -> core::Block {
|
|
||||||
let previous_hash = if self.blocks().len() > 0 {
|
|
||||||
self.blocks().last().unwrap().head().block_hash()
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
};
|
|
||||||
let merkle_root = Self::calculate_merkle_root(&self.mempool);
|
|
||||||
let timestamp = std::time::SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
|
|
||||||
let nonce = 0;
|
|
||||||
let mut new_head = core::BlockHeader {
|
|
||||||
previous_hash: previous_hash.to_string(),
|
|
||||||
merkle_root,
|
|
||||||
timestamp,
|
|
||||||
nonce,
|
|
||||||
block_hash: "".to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut block_hash = String::new();
|
|
||||||
while !block_hash.starts_with("0") {
|
|
||||||
new_head.nonce += 1;
|
|
||||||
block_hash = calculate_block_hash(&new_head)
|
|
||||||
}
|
|
||||||
|
|
||||||
new_head.block_hash = block_hash;
|
|
||||||
|
|
||||||
let new_block = core::Block::new(new_head, self.mempool.clone());
|
|
||||||
self.blocks.push(new_block);
|
|
||||||
self.blocks.last().unwrap().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_transaction(&mut self, tx: &core::Tx) -> Result<(), BlockchainError> {
|
|
||||||
tx.validate()?;
|
|
||||||
return Ok(());
|
|
||||||
if let Some(from_balance) = self.balances.get_mut(tx.from()) {
|
|
||||||
if *from_balance > tx.value() {
|
|
||||||
*from_balance -= tx.value();
|
|
||||||
} else {
|
|
||||||
return Err(BlockchainError::Tx(TxError::FromInsuffitientFonds))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(BlockchainError::Tx(TxError::UnknownAccount(tx.from().to_string())))
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(to_balance) = self.balances.get_mut(&tx.to().to_string()) {
|
|
||||||
*to_balance += tx.value()
|
|
||||||
} else {
|
|
||||||
if self.acc_exists(tx.to()) {
|
|
||||||
*self.balances.get_mut(tx.to()).unwrap() += tx.value();
|
|
||||||
} else {
|
|
||||||
self.balances.insert(tx.to().clone(), tx.value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn apply(&mut self, data: NetworkData) -> Result<(), BlockchainError> {
|
|
||||||
match &data {
|
|
||||||
NetworkData::Transaction(tx) => {
|
|
||||||
self.apply_transaction(tx)?;
|
|
||||||
self.mempool.push(data);
|
self.mempool.push(data);
|
||||||
}
|
Ok(())
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(balances: HashMap<Account, u32>, blocks: Vec<core::Block>, mempool: Vec<NetworkData>) -> Blockchain {
|
pub fn hash_network_data(data: &NetworkData) -> String {
|
||||||
return Self {
|
match data {
|
||||||
id: BLOCKCHAIN_ID.to_string(),
|
NetworkData::Transaction(tx) => Self::hash_transaction(tx),
|
||||||
balances,
|
}
|
||||||
blocks,
|
|
||||||
mempool
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
pub fn hash_transaction(tx: &core::Tx) -> String {
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(tx.to());
|
||||||
|
hasher.update(tx.from());
|
||||||
|
hasher.update(tx.value().to_be_bytes());
|
||||||
|
hasher.update(tx.data());
|
||||||
|
|
||||||
|
let res = hasher.finalize();
|
||||||
|
hex::encode(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_next_level(level: &[String]) -> Vec<String> {
|
||||||
|
let mut next_level = Vec::new();
|
||||||
|
|
||||||
|
for chunk in level.chunks(2) {
|
||||||
|
let combined_hash = if chunk.len() == 2 {
|
||||||
|
Self::hash_pair(&chunk[0], &chunk[1])
|
||||||
|
} else {
|
||||||
|
Self::hash_pair(&chunk[0], &chunk[0])
|
||||||
|
};
|
||||||
|
next_level.push(combined_hash);
|
||||||
|
}
|
||||||
|
next_level
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_merkle_root(tx: &[NetworkData]) -> String {
|
||||||
|
let tx_hashes: Vec<String> = tx
|
||||||
|
.iter()
|
||||||
|
.map(|tx| Blockchain::hash_network_data(tx))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if tx_hashes.is_empty() {
|
||||||
|
return Blockchain::hash_data("");
|
||||||
|
}
|
||||||
|
|
||||||
|
if tx_hashes.len() == 1 {
|
||||||
|
return tx_hashes[0].clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut current_level = tx_hashes.to_vec();
|
||||||
|
|
||||||
|
while current_level.len() > 1 {
|
||||||
|
current_level = Self::calculate_next_level(¤t_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
return current_level[0].clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_pair(left: &str, right: &str) -> String {
|
||||||
|
let combined = format!("{}{}", left, right);
|
||||||
|
Self::hash_data(&combined)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_data(data: &str) -> String {
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(data.as_bytes());
|
||||||
|
hex::encode(hasher.finalize())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn acc_exists(&self, acc: &Account) -> bool {
|
||||||
|
self.balances.iter().find(|(k, _)| *k == acc).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dump_blocks(&self, path: String) {
|
||||||
|
let block_json = serde_json::to_string_pretty(&self.blocks).unwrap();
|
||||||
|
if let Ok(mut db_file) = std::fs::OpenOptions::new()
|
||||||
|
.truncate(true)
|
||||||
|
.create(true)
|
||||||
|
.write(true)
|
||||||
|
.open(path)
|
||||||
|
{
|
||||||
|
db_file.write_all(&block_json.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_block(&mut self) -> core::Block {
|
||||||
|
let previous_hash = if self.blocks().len() > 0 {
|
||||||
|
self.blocks().last().unwrap().head().block_hash()
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
|
let merkle_root = Self::calculate_merkle_root(&self.mempool);
|
||||||
|
let timestamp = std::time::SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs();
|
||||||
|
let nonce = 0;
|
||||||
|
let mut new_head = core::BlockHeader {
|
||||||
|
previous_hash: previous_hash.to_string(),
|
||||||
|
merkle_root,
|
||||||
|
timestamp,
|
||||||
|
nonce,
|
||||||
|
height: self.blocks().len() as u64 + 1,
|
||||||
|
block_hash: "".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut block_hash = String::new();
|
||||||
|
while !block_hash.starts_with("0") {
|
||||||
|
new_head.nonce += 1;
|
||||||
|
block_hash = calculate_block_hash(&new_head)
|
||||||
|
}
|
||||||
|
|
||||||
|
new_head.block_hash = block_hash;
|
||||||
|
|
||||||
|
let new_block = core::Block::new(new_head, self.mempool.clone());
|
||||||
|
self.blocks.push(new_block);
|
||||||
|
self.blocks.last().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_transaction(&mut self, tx: &core::Tx) -> Result<(), BlockchainError> {
|
||||||
|
tx.validate()?;
|
||||||
|
return Ok(());
|
||||||
|
if let Some(from_balance) = self.balances.get_mut(tx.from()) {
|
||||||
|
if *from_balance > tx.value() {
|
||||||
|
*from_balance -= tx.value();
|
||||||
|
} else {
|
||||||
|
return Err(BlockchainError::Tx(TxError::FromInsuffitientFonds));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(BlockchainError::Tx(TxError::UnknownAccount(
|
||||||
|
tx.from().to_string(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(to_balance) = self.balances.get_mut(&tx.to().to_string()) {
|
||||||
|
*to_balance += tx.value()
|
||||||
|
} else {
|
||||||
|
if self.acc_exists(tx.to()) {
|
||||||
|
*self.balances.get_mut(tx.to()).unwrap() += tx.value();
|
||||||
|
} else {
|
||||||
|
self.balances.insert(tx.to().clone(), tx.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply(&mut self, data: NetworkData) -> Result<(), BlockchainError> {
|
||||||
|
match &data {
|
||||||
|
NetworkData::Transaction(tx) => {
|
||||||
|
self.apply_transaction(tx)?;
|
||||||
|
self.mempool.push(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(balances: HashMap<Account, u32>, mempool: Vec<NetworkData>) -> Blockchain {
|
||||||
|
return Self {
|
||||||
|
id: BLOCKCHAIN_ID.to_string(),
|
||||||
|
balances,
|
||||||
|
mempool,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_block_hash(head: &core::BlockHeader) -> String {
|
pub fn calculate_block_hash(head: &core::BlockHeader) -> String {
|
||||||
let mut hasher = sha2::Sha256::new();
|
let mut hasher = sha2::Sha256::new();
|
||||||
|
|
||||||
hasher.update(head.nonce().to_be_bytes());
|
hasher.update(head.nonce().to_be_bytes());
|
||||||
hasher.update(head.previous_hash());
|
hasher.update(head.previous_hash());
|
||||||
hasher.update(head.timestamp().to_be_bytes());
|
hasher.update(head.timestamp().to_be_bytes());
|
||||||
hasher.update(head.merkle_root());
|
hasher.update(head.merkle_root());
|
||||||
|
|
||||||
let res = hasher.finalize();
|
let res = hasher.finalize();
|
||||||
hex::encode(res)
|
hex::encode(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Blockchain {
|
impl Blockchain {
|
||||||
pub fn list_blocks(&self) -> String {
|
pub fn list_blocks(&self) -> String {
|
||||||
let mut ret = String::from("Blocks List\n-------------------\n");
|
let mut ret = String::from("Blocks List\n-------------------\n");
|
||||||
for (i, b) in self.blocks.iter().enumerate() {
|
for (i, b) in self.blocks.iter().enumerate() {
|
||||||
ret.push_str(format!("Block Hash #{i}: {}\n", b.head.block_hash()).as_str())
|
ret.push_str(format!("Block Hash #{i}: {}\n", b.head.block_hash()).as_str())
|
||||||
|
}
|
||||||
|
ret
|
||||||
}
|
}
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_balances(&self) -> &std::collections::HashMap<String, u32> {
|
pub fn get_balances(&self) -> &std::collections::HashMap<String, u32> {
|
||||||
&self.balances
|
&self.balances
|
||||||
}
|
|
||||||
|
|
||||||
pub fn blocks(&self) -> &[core::Block] {
|
|
||||||
&self.blocks
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_block(&mut self, block: core::Block) {
|
|
||||||
match self.validate_block(&block) {
|
|
||||||
Ok(()) => self.blocks.push(block),
|
|
||||||
Err(e) => match e {
|
|
||||||
ValidationError::InvalidBlockHash => log(msg!(ERROR, "Invalid Block Hash")),
|
|
||||||
ValidationError::InvalidPreviousBlockHash => log(msg!(ERROR, "Invalid Previos Block Hash")),
|
|
||||||
ValidationError::InvalidBlockJson(e) => print_error_chain(e.into()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn validate_block(&self, block: &core::Block) -> Result<(), ValidationError>{
|
pub fn blocks(&self) -> &[core::Block] {
|
||||||
let head = block.head();
|
&self.blocks
|
||||||
let hash = calculate_block_hash(block.head());
|
|
||||||
if hash != head.block_hash() {
|
|
||||||
return Err(ValidationError::InvalidBlockHash)
|
|
||||||
}
|
}
|
||||||
if let Some(prev_block) = self.blocks().last() {
|
|
||||||
if head.previous_hash() != prev_block.head().block_hash() {
|
|
||||||
return Err(ValidationError::InvalidPreviousBlockHash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate_chain(&self) -> Result<(), ValidationError>{
|
pub fn add_block(&mut self, block: core::Block) {
|
||||||
let blocks = self.blocks();
|
match self.validate_block(&block) {
|
||||||
for block in blocks {
|
Ok(()) => self.blocks.push(block),
|
||||||
let head = block.head();
|
Err(e) => match e {
|
||||||
let hash = calculate_block_hash(block.head());
|
ValidationError::InvalidBlockHash => log(msg!(ERROR, "Invalid Block Hash")),
|
||||||
|
ValidationError::InvalidPreviousBlockHash => {
|
||||||
if hash != head.block_hash() {
|
log(msg!(ERROR, "Invalid Previos Block Hash"))
|
||||||
return Err(ValidationError::InvalidBlockHash)
|
}
|
||||||
}
|
ValidationError::InvalidBlockJson(e) => print_error_chain(e.into()),
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build(blocks: &str) -> Result<Blockchain, ValidationError> {
|
fn validate_block(&self, block: &core::Block) -> Result<(), ValidationError> {
|
||||||
match serde_json::from_str::<Vec<core::Block>>(&blocks) {
|
let head = block.head();
|
||||||
Ok(blocks) => {
|
let hash = calculate_block_hash(block.head());
|
||||||
let chain = Blockchain {
|
if hash != head.block_hash() {
|
||||||
blocks,
|
return Err(ValidationError::InvalidBlockHash);
|
||||||
balances: HashMap::new(),
|
}
|
||||||
mempool: vec![],
|
if let Some(prev_block) = self.blocks().last() {
|
||||||
id: BLOCKCHAIN_ID.to_string(),
|
if head.previous_hash() != prev_block.head().block_hash() {
|
||||||
};
|
return Err(ValidationError::InvalidPreviousBlockHash);
|
||||||
chain.validate_chain()?;
|
}
|
||||||
Ok(chain)
|
}
|
||||||
}
|
Ok(())
|
||||||
Err(e) => {
|
}
|
||||||
Err(ValidationError::InvalidBlockJson(e))
|
|
||||||
}
|
fn validate_chain(&self) -> Result<(), ValidationError> {
|
||||||
|
let blocks = self.blocks();
|
||||||
|
for block in blocks {
|
||||||
|
let head = block.head();
|
||||||
|
let hash = calculate_block_hash(block.head());
|
||||||
|
|
||||||
|
if hash != head.block_hash() {
|
||||||
|
return Err(ValidationError::InvalidBlockHash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(blocks: &str) -> Result<Blockchain, ValidationError> {
|
||||||
|
match serde_json::from_str::<Vec<core::Block>>(&blocks) {
|
||||||
|
Ok(blocks) => {
|
||||||
|
let chain = Blockchain {
|
||||||
|
blocks,
|
||||||
|
balances: HashMap::new(),
|
||||||
|
mempool: vec![],
|
||||||
|
id: BLOCKCHAIN_ID.to_string(),
|
||||||
|
};
|
||||||
|
chain.validate_chain()?;
|
||||||
|
Ok(chain)
|
||||||
|
}
|
||||||
|
Err(e) => Err(ValidationError::InvalidBlockJson(e)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,5 +2,5 @@ use super::Tx;
|
|||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
|
#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
|
||||||
pub enum NetworkData {
|
pub enum NetworkData {
|
||||||
Transaction(Tx)
|
Transaction(Tx),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,24 +3,29 @@ use crate::error::TxError;
|
|||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize, Debug, clap::Args, Clone)]
|
#[derive(serde::Deserialize, serde::Serialize, Debug, clap::Args, Clone)]
|
||||||
pub struct Tx {
|
pub struct Tx {
|
||||||
from: Account,
|
from: Account,
|
||||||
to: Account,
|
to: Account,
|
||||||
value: u32,
|
value: u32,
|
||||||
data: String
|
data: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tx {
|
impl Tx {
|
||||||
pub fn new(from: Account, to: Account, value: u32, data: String) -> Self {
|
pub fn new(from: Account, to: Account, value: u32, data: String) -> Self {
|
||||||
Self { from, to, value, data }
|
Self {
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
value,
|
||||||
|
data,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate(&self) -> Result<(), TxError> {
|
pub fn validate(&self) -> Result<(), TxError> {
|
||||||
if self.from.is_empty() {
|
if self.from.is_empty() {
|
||||||
return Err(TxError::FromEmpty)
|
return Err(TxError::FromEmpty);
|
||||||
} else if self.to.is_empty() {
|
} else if self.to.is_empty() {
|
||||||
return Err(TxError::ToEmpty)
|
return Err(TxError::ToEmpty);
|
||||||
} else if self.value == 0 {
|
} else if self.value == 0 {
|
||||||
return Err(TxError::ValueEmpty)
|
return Err(TxError::ValueEmpty);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
20
node/src/db/database.rs
Normal file
20
node/src/db/database.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
use sled;
|
||||||
|
use crate::log;
|
||||||
|
use vlogger::*;
|
||||||
|
|
||||||
|
const DB_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/database");
|
||||||
|
|
||||||
|
fn db_init() {
|
||||||
|
match sled::open(DB_PATH) {
|
||||||
|
Ok(db) => {
|
||||||
|
if db.was_recovered() {
|
||||||
|
log(msg!(INFO, "Loaded Database from Previous state at: {}", DB_PATH));
|
||||||
|
} else {
|
||||||
|
log(msg!(INFO, "Created Database at {}", DB_PATH));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
use thiserror::Error;
|
|
||||||
use crate::log;
|
use crate::log;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
@ -7,7 +7,7 @@ pub enum BlockchainError {
|
|||||||
#[error("invalid account creation")]
|
#[error("invalid account creation")]
|
||||||
InvalidAccountCreation,
|
InvalidAccountCreation,
|
||||||
#[error("Transactional error")]
|
#[error("Transactional error")]
|
||||||
Tx(#[from] TxError)
|
Tx(#[from] TxError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -22,27 +22,27 @@ pub enum TxError {
|
|||||||
#[error("0 value transaction")]
|
#[error("0 value transaction")]
|
||||||
ValueEmpty,
|
ValueEmpty,
|
||||||
#[error("account {0} not found in database")]
|
#[error("account {0} not found in database")]
|
||||||
UnknownAccount(String)
|
UnknownAccount(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_error_chain(err: anyhow::Error) {
|
pub fn print_error_chain(err: anyhow::Error) {
|
||||||
let mut err_string = String::from(format!("Error: {}\n", err));
|
let mut err_string = String::from(format!("Error: {}\n", err));
|
||||||
|
|
||||||
let mut source = err.source();
|
let mut source = err.source();
|
||||||
let mut level = 1;
|
let mut level = 1;
|
||||||
|
|
||||||
while let Some(err) = source {
|
while let Some(err) = source {
|
||||||
err_string.push_str(format!(" {}: {}\n", level, err).as_str());
|
err_string.push_str(format!(" {}: {}\n", level, err).as_str());
|
||||||
source = err.source();
|
source = err.source();
|
||||||
level += 1;
|
level += 1;
|
||||||
}
|
}
|
||||||
log(err_string)
|
log(err_string)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum SystemError<T>
|
pub enum SystemError<T>
|
||||||
where
|
where
|
||||||
T: std::fmt::Debug
|
T: std::fmt::Debug,
|
||||||
{
|
{
|
||||||
#[error("TODO")]
|
#[error("TODO")]
|
||||||
TODO,
|
TODO,
|
||||||
@ -54,5 +54,5 @@ where
|
|||||||
ChannelClosed { message: T },
|
ChannelClosed { message: T },
|
||||||
|
|
||||||
#[error("Event Bus closed")]
|
#[error("Event Bus closed")]
|
||||||
EventBusClosed(#[from] tokio::sync::broadcast::error::RecvError)
|
EventBusClosed(#[from] tokio::sync::broadcast::error::RecvError),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,78 +0,0 @@
|
|||||||
use once_cell::sync::Lazy;
|
|
||||||
use tokio::sync::broadcast;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use crate::watcher::renderer::RenderCommand;
|
|
||||||
|
|
||||||
static NETWORK_EVENT_BUS: Lazy<Arc<EventBus<NetworkEvent>>> = Lazy::new(|| Arc::new(EventBus::new()));
|
|
||||||
static SYSTEM_EVENT_BUS: Lazy<Arc<EventBus<SystemEvent>>> = Lazy::new(|| Arc::new(EventBus::new()));
|
|
||||||
static RENDER_CHANNEL: Lazy<Arc<EventBus<RenderCommand>>> = Lazy::new(|| Arc::new(EventBus::new()));
|
|
||||||
|
|
||||||
pub fn publish_system_event(event: SystemEvent) {
|
|
||||||
SYSTEM_EVENT_BUS.publish(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subscribe_system_event() -> broadcast::Receiver<SystemEvent> {
|
|
||||||
SYSTEM_EVENT_BUS.subscribe()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn publish_network_event(event: NetworkEvent) {
|
|
||||||
NETWORK_EVENT_BUS.publish(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subscribe_network_event() -> broadcast::Receiver<NetworkEvent> {
|
|
||||||
NETWORK_EVENT_BUS.subscribe()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn publish_render_event(event: RenderCommand) {
|
|
||||||
RENDER_CHANNEL.publish(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subscribe_render_event() -> broadcast::Receiver<RenderCommand> {
|
|
||||||
RENDER_CHANNEL.subscribe()
|
|
||||||
}
|
|
||||||
|
|
||||||
struct EventBus<T>
|
|
||||||
where
|
|
||||||
T: Clone + std::fmt::Debug
|
|
||||||
{
|
|
||||||
sender: broadcast::Sender<T>,
|
|
||||||
_receiver: broadcast::Receiver<T>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone + std::fmt::Debug> EventBus<T> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let (sender, receiver) = broadcast::channel(1000);
|
|
||||||
Self {
|
|
||||||
sender,
|
|
||||||
_receiver: receiver
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn publish(&self, event: T) {
|
|
||||||
if let Err(e) = self.sender.send(event) {
|
|
||||||
eprintln!("{e}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subscribe(&self) -> broadcast::Receiver<T> {
|
|
||||||
self.sender.subscribe()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum SystemEvent {
|
|
||||||
ExecutorStarted,
|
|
||||||
RendererStarted,
|
|
||||||
NodeStarted,
|
|
||||||
Exit,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum NetworkEvent {
|
|
||||||
SeedConnected(String),
|
|
||||||
SeedDisconnected(String),
|
|
||||||
AllSeedsConnected,
|
|
||||||
BootstrapCompleted,
|
|
||||||
NodeReady,
|
|
||||||
}
|
|
||||||
@ -1,10 +1,9 @@
|
|||||||
pub mod native_node {
|
pub mod node {
|
||||||
pub mod node;
|
pub mod node;
|
||||||
pub use node::*;
|
pub use node::*;
|
||||||
|
|
||||||
pub mod error;
|
|
||||||
pub use error::*;
|
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
|
pub use error::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
@ -13,53 +12,67 @@ pub mod args;
|
|||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
||||||
pub mod event_bus;
|
pub mod db {
|
||||||
|
pub mod database;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod bus {
|
||||||
|
pub mod executor;
|
||||||
|
pub mod network;
|
||||||
|
pub mod render;
|
||||||
|
pub mod system;
|
||||||
|
pub mod event_bus;
|
||||||
|
|
||||||
|
pub use executor::*;
|
||||||
|
pub use network::*;
|
||||||
|
pub use render::*;
|
||||||
|
pub use system::*;
|
||||||
|
}
|
||||||
|
|
||||||
pub mod watcher {
|
pub mod watcher {
|
||||||
pub mod executor;
|
pub mod executor;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod renderer;
|
pub mod renderer;
|
||||||
pub mod watcher;
|
pub mod watcher;
|
||||||
|
|
||||||
pub use watcher::*;
|
pub use executor::*;
|
||||||
pub use executor::*;
|
pub use parser::*;
|
||||||
pub use parser::*;
|
pub use renderer::*;
|
||||||
pub use renderer::*;
|
pub use watcher::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod protocol {
|
pub mod protocol {
|
||||||
pub mod message;
|
pub mod message;
|
||||||
pub use message::*;
|
pub use message::*;
|
||||||
|
|
||||||
pub mod connection;
|
pub mod connection;
|
||||||
pub use connection::*;
|
pub use connection::*;
|
||||||
|
|
||||||
pub mod connector;
|
pub mod connector;
|
||||||
pub use connector::*;
|
pub use connector::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod core {
|
pub mod core {
|
||||||
pub mod block;
|
pub mod block;
|
||||||
pub use block::*;
|
pub use block::*;
|
||||||
|
|
||||||
pub mod blockchain;
|
pub mod blockchain;
|
||||||
pub use blockchain::*;
|
pub use blockchain::*;
|
||||||
|
|
||||||
pub mod tx;
|
pub mod tx;
|
||||||
pub use tx::*;
|
pub use tx::*;
|
||||||
|
|
||||||
pub mod data;
|
|
||||||
pub use data::*;
|
|
||||||
|
|
||||||
|
pub mod data;
|
||||||
|
pub use data::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod seeds_constants;
|
pub mod seeds_constants;
|
||||||
|
|
||||||
use crate::watcher::renderer::{RenderPane, RenderCommand};
|
use crate::watcher::renderer::{RenderCommand, RenderPane};
|
||||||
|
|
||||||
pub fn log(msg: String) {
|
pub fn log(msg: String) {
|
||||||
crate::event_bus::publish_render_event(RenderCommand::RenderStringToPane{
|
crate::bus::publish_render_event(RenderCommand::RenderStringToPane {
|
||||||
pane: RenderPane::CliOutput,
|
pane: RenderPane::CliOutput,
|
||||||
str: msg
|
str: msg,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use blockchain::watcher::Watcher;
|
|
||||||
use blockchain::args;
|
use blockchain::args;
|
||||||
|
use blockchain::watcher::Watcher;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
// // src/main.rs
|
// // src/main.rs
|
||||||
@ -7,40 +7,41 @@ use clap::Parser;
|
|||||||
//
|
//
|
||||||
// #[global_allocator]
|
// #[global_allocator]
|
||||||
// static GLOBAL: Jemalloc = Jemalloc;
|
// static GLOBAL: Jemalloc = Jemalloc;
|
||||||
//
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), std::io::Error> {
|
async fn main() -> Result<(), std::io::Error> {
|
||||||
|
let args = args::CliArgs::parse();
|
||||||
|
|
||||||
let args = args::CliArgs::parse();
|
let mut watcher = Watcher::build()
|
||||||
|
.file(args.seed_file)
|
||||||
|
.addr(args.addr)
|
||||||
|
.seed(args.seed)
|
||||||
|
.debug(args.debug)
|
||||||
|
.render(args.render)
|
||||||
|
.bootstrap(args.bootstrap)
|
||||||
|
.start()
|
||||||
|
.await;
|
||||||
|
|
||||||
let mut watcher = Watcher::build()
|
crossterm::execute!(
|
||||||
.file(args.seed_file)
|
std::io::stdout(),
|
||||||
.addr(args.addr)
|
crossterm::event::EnableBracketedPaste,
|
||||||
.seed(args.seed)
|
crossterm::event::EnableFocusChange,
|
||||||
.render(args.render)
|
crossterm::event::EnableMouseCapture,
|
||||||
.bootstrap(args.bootstrap)
|
)?;
|
||||||
.start().await;
|
|
||||||
|
|
||||||
crossterm::execute!(
|
loop {
|
||||||
std::io::stdout(),
|
if !watcher.poll().await.is_ok_and(|b| b) {
|
||||||
crossterm::event::EnableBracketedPaste,
|
break;
|
||||||
crossterm::event::EnableFocusChange,
|
}
|
||||||
crossterm::event::EnableMouseCapture,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if !watcher.poll().await.is_ok_and(|b| b) {
|
|
||||||
break ;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
crossterm::execute!(
|
crossterm::execute!(
|
||||||
std::io::stdout(),
|
std::io::stdout(),
|
||||||
crossterm::event::DisableBracketedPaste,
|
crossterm::event::DisableBracketedPaste,
|
||||||
crossterm::event::DisableFocusChange,
|
crossterm::event::DisableFocusChange,
|
||||||
crossterm::event::DisableMouseCapture
|
crossterm::event::DisableMouseCapture
|
||||||
)?;
|
)?;
|
||||||
ratatui::restore();
|
ratatui::restore();
|
||||||
println!("Hello, world!");
|
println!("Hello, world!");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,389 +0,0 @@
|
|||||||
use crate::core::{self, Blockchain, NetworkData, ValidationError};
|
|
||||||
use crate::error::print_error_chain;
|
|
||||||
use crate::event_bus::{publish_system_event, SystemEvent};
|
|
||||||
use crate::protocol::ProtocolMessage;
|
|
||||||
|
|
||||||
use crate::seeds_constants::SEED_NODES;
|
|
||||||
use crate::protocol::{Connector, ConnectorCommand};
|
|
||||||
use crate::watcher::executor::ExecutorCommand;
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::net::SocketAddr;
|
|
||||||
use vlogger::*;
|
|
||||||
use tokio::sync::mpsc;
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
use crate::log;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct TcpPeer {
|
|
||||||
pub id: Uuid,
|
|
||||||
pub addr: SocketAddr,
|
|
||||||
pub sender: tokio::sync::mpsc::Sender<ProtocolMessage>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TcpPeer {
|
|
||||||
pub fn new(
|
|
||||||
id: Uuid,
|
|
||||||
addr: SocketAddr,
|
|
||||||
sender: tokio::sync::mpsc::Sender<ProtocolMessage>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
id,
|
|
||||||
addr,
|
|
||||||
sender
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct NativeNode {
|
|
||||||
pub tcp_connector: Option<mpsc::Sender<ConnectorCommand>>,
|
|
||||||
pub id: Uuid,
|
|
||||||
pub addr: Option<SocketAddr>,
|
|
||||||
pub tcp_peers: HashMap<Uuid, TcpPeer>,
|
|
||||||
pub chain: core::Blockchain,
|
|
||||||
listner_handle: Option<tokio::task::JoinHandle<()>>,
|
|
||||||
exec_tx: mpsc::Sender<ExecutorCommand>,
|
|
||||||
rx: mpsc::Receiver<NodeCommand>,
|
|
||||||
tx: mpsc::Sender<NodeCommand>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum NodeCommand {
|
|
||||||
AddPeer(TcpPeer),
|
|
||||||
RemovePeer { peer_id: Uuid },
|
|
||||||
ProcessMessage { peer_id: Uuid, message: ProtocolMessage },
|
|
||||||
ProcessNetworkData(NetworkData),
|
|
||||||
StartListner(SocketAddr),
|
|
||||||
PingAddr(String),
|
|
||||||
PingId(String),
|
|
||||||
CreateBlock,
|
|
||||||
ListBlocks,
|
|
||||||
ListPeers,
|
|
||||||
ShowId,
|
|
||||||
DumpBlocks(String),
|
|
||||||
ConnectToSeeds,
|
|
||||||
ConnectTcpPeer(String),
|
|
||||||
BootStrap,
|
|
||||||
Exit,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NativeNode {
|
|
||||||
pub fn peer_addresses(&self) -> Vec<SocketAddr> {
|
|
||||||
let mut addr: Vec<SocketAddr> = self.tcp_peers.iter().map(|p| p.1.addr.to_string().parse::<SocketAddr>().unwrap()).collect();
|
|
||||||
if let Some(a) = self.addr {
|
|
||||||
addr.push(a.clone());
|
|
||||||
}
|
|
||||||
addr
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn list_peers(&self) -> String {
|
|
||||||
let mut ret = String::from("Peer List\n-----------\n");
|
|
||||||
for (i, p) in self.tcp_peers.iter().enumerate() {
|
|
||||||
ret.push_str(format!("Peer #{i}: {}\n", p.1.id).as_str())
|
|
||||||
}
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn show_id(&self) {
|
|
||||||
log(msg!(DEBUG, "Node Id: {}", self.id))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn remove_tcp_peer(&mut self, peer_id: Uuid) {
|
|
||||||
log(msg!(DEBUG, "Removing Peer {peer_id}"));
|
|
||||||
self.tcp_peers.remove_entry(&peer_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn add_tcp_peer(&mut self, peer: TcpPeer) {
|
|
||||||
log(msg!(DEBUG, "Added Peer from address: {}", peer.addr));
|
|
||||||
self.tcp_peers.insert(peer.id, peer);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn new_with_id(
|
|
||||||
id: uuid::Uuid,
|
|
||||||
exec_tx: mpsc::Sender<ExecutorCommand>,
|
|
||||||
addr: Option<SocketAddr>,
|
|
||||||
blocks_json: &str,
|
|
||||||
) -> Self {
|
|
||||||
let (tx, rx) = mpsc::channel::<NodeCommand>(100);
|
|
||||||
Self {
|
|
||||||
id,
|
|
||||||
tcp_peers: HashMap::new(),
|
|
||||||
chain: Blockchain::build(blocks_json).unwrap_or(Default::default()),
|
|
||||||
addr,
|
|
||||||
exec_tx,
|
|
||||||
listner_handle: None,
|
|
||||||
tcp_connector: None,
|
|
||||||
tx,
|
|
||||||
rx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn new(
|
|
||||||
addr: Option<SocketAddr>,
|
|
||||||
blocks_json: &str,
|
|
||||||
exec_tx: mpsc::Sender<ExecutorCommand>,
|
|
||||||
) -> Self {
|
|
||||||
let (tx, rx) = mpsc::channel::<NodeCommand>(100);
|
|
||||||
let chain = Blockchain::build(blocks_json)
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
print_error_chain(e.into());
|
|
||||||
Default::default()
|
|
||||||
});
|
|
||||||
Self {
|
|
||||||
id: Uuid::new_v4(),
|
|
||||||
tcp_peers: HashMap::new(),
|
|
||||||
chain,
|
|
||||||
addr,
|
|
||||||
exec_tx,
|
|
||||||
listner_handle: None,
|
|
||||||
tcp_connector: None,
|
|
||||||
tx,
|
|
||||||
rx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn process_message(&mut self, peer_id: uuid::Uuid, message: ProtocolMessage) {
|
|
||||||
|
|
||||||
match message {
|
|
||||||
ProtocolMessage::BootstrapRequest { .. } => {
|
|
||||||
log(msg!(DEBUG, "Received BootstrapRequest from {peer_id}"));
|
|
||||||
let peer = &self.tcp_peers[&peer_id];
|
|
||||||
let resp = ProtocolMessage::BootstrapResponse {
|
|
||||||
blocks: serde_json::to_string(&self.chain.blocks().to_vec()).unwrap_or_else(|e| {
|
|
||||||
log(msg!(WARNING, "Failed to serde Chain for BootstrapResponse: {e}"));
|
|
||||||
Default::default()
|
|
||||||
})
|
|
||||||
};
|
|
||||||
peer.sender.send(resp).await.unwrap();
|
|
||||||
log(msg!(DEBUG, "Send BootstrapResponse to {peer_id}"));
|
|
||||||
},
|
|
||||||
ProtocolMessage::BootstrapResponse { blocks } => {
|
|
||||||
log(msg!(DEBUG, "Received BootstrapResponse from seed"));
|
|
||||||
self.chain = core::Blockchain::build(&blocks).unwrap();
|
|
||||||
},
|
|
||||||
ProtocolMessage::Ping {peer_id} => {
|
|
||||||
log(msg!(DEBUG, "Received Ping from {peer_id}"));
|
|
||||||
let resp = ProtocolMessage::Pong { peer_id: self.id.clone() };
|
|
||||||
let peer = &self.tcp_peers[&peer_id];
|
|
||||||
peer.sender.send(resp).await.unwrap();
|
|
||||||
},
|
|
||||||
ProtocolMessage::GetPeersRequest { peer_id } => {
|
|
||||||
log(msg!(DEBUG, "Received GetPeersRequest from {peer_id}"));
|
|
||||||
let peers = self.peer_addresses();
|
|
||||||
let resp = ProtocolMessage::GetPeersResponse { peer_addresses: peers };
|
|
||||||
let peer = &self.tcp_peers[&peer_id];
|
|
||||||
peer.sender.send(resp).await.unwrap();
|
|
||||||
}
|
|
||||||
ProtocolMessage::Block { block, ..} => {
|
|
||||||
log(msg!(DEBUG, "Received Block from {peer_id}"));
|
|
||||||
self.chain.add_block(block.clone())
|
|
||||||
}
|
|
||||||
ProtocolMessage::NetworkData { data, ..} => {
|
|
||||||
log(msg!(DEBUG, "Received NetworkData from {peer_id}"));
|
|
||||||
self.chain.apply(data).unwrap()
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
log(msg!(DEBUG, "TODO: implement this message type"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn send_message_to_peer_addr(&self, addr: SocketAddr, msg: ProtocolMessage) {
|
|
||||||
if let Some((_, peer)) = self.tcp_peers.iter().find(|(_, v)| v.addr == addr) {
|
|
||||||
if let Err(e) = peer.sender.send(msg).await {
|
|
||||||
log(msg!(ERROR, "Error Sending message to peer: {e}"));
|
|
||||||
}
|
|
||||||
log(msg!(DEBUG, "Sent BootstrapRequest to seed"));
|
|
||||||
} else {
|
|
||||||
log(msg!(ERROR, "Error Sending message to peer: peer not in list"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn send_message_to_peer_id(&self, id: Uuid, msg: ProtocolMessage) {
|
|
||||||
if let Some(peer) = self.tcp_peers.get(&id) {
|
|
||||||
if let Err(e) = peer.sender.send(msg).await {
|
|
||||||
log(msg!(ERROR, "Error Sending message to peer: {e}"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_message_to_seed(&self, msg: ProtocolMessage) {
|
|
||||||
for seed in SEED_NODES.iter() {
|
|
||||||
if let Some(_) = self.tcp_peers.iter().find(|(_, v)| v.addr == *seed) {
|
|
||||||
self.send_message_to_peer_addr(*seed, msg).await;
|
|
||||||
return ;
|
|
||||||
} else {
|
|
||||||
self.send_message_to_peer_addr(*seed, msg).await;
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log(msg!(ERROR, "No Seed Nodes Avaliable"));
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn bootstrap(&mut self) -> Result<(), ValidationError> {
|
|
||||||
log(msg!(DEBUG, "Bootstrapping"));
|
|
||||||
|
|
||||||
let message = ProtocolMessage::BootstrapRequest{peer_id: self.id, version: "".to_string()};
|
|
||||||
self.send_message_to_seed(message).await;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn broadcast_network_data(&self, data: NetworkData) {
|
|
||||||
for (id, peer) in &self.tcp_peers {
|
|
||||||
let message = ProtocolMessage::NetworkData{peer_id: self.id, data: data.clone()};
|
|
||||||
peer.sender.send(message).await.unwrap();
|
|
||||||
log(msg!(DEBUG, "Send Transaction message to {id}"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn broadcast_block(&self, block: &core::Block) {
|
|
||||||
for (id, peer) in &self.tcp_peers {
|
|
||||||
let message = ProtocolMessage::Block {
|
|
||||||
peer_id: self.id,
|
|
||||||
height: self.chain.blocks().len() as u64,
|
|
||||||
block: block.clone()
|
|
||||||
};
|
|
||||||
peer.sender.send(message).await.unwrap();
|
|
||||||
log(msg!(DEBUG, "Send Block message to {id}"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tx(&self) -> mpsc::Sender<NodeCommand> {
|
|
||||||
return self.tx.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn exec_tx(&self) -> mpsc::Sender<ExecutorCommand> {
|
|
||||||
return self.exec_tx.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn network_data(&mut self, data: NetworkData) {
|
|
||||||
match self.chain.apply(data) {
|
|
||||||
Ok(_) => log(msg!(DEBUG, "NetworkData Applied")),
|
|
||||||
Err(e) => print_error_chain(e.into())
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn connector_cmd(&self, cmd: ConnectorCommand) {
|
|
||||||
match &self.tcp_connector {
|
|
||||||
Some(t) => {
|
|
||||||
match t.send(cmd).await {
|
|
||||||
Ok(()) => {},
|
|
||||||
Err(e) => log(msg!(ERROR, "Failed to Send Command to connector: {}", e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => log(msg!(ERROR, "No Connector Availiable"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn start_connection_listner(&mut self, addr: SocketAddr) {
|
|
||||||
|
|
||||||
log(msg!(DEBUG, "Starting Connection Listener"));
|
|
||||||
let (con_tx, con_rx) = mpsc::channel::<ConnectorCommand>(100);
|
|
||||||
|
|
||||||
self.tcp_connector = Some(con_tx);
|
|
||||||
|
|
||||||
self.listner_handle = Some(tokio::spawn({
|
|
||||||
let mut connector = Connector::new(self.id, addr, self.exec_tx(), con_rx);
|
|
||||||
log(msg!(DEBUG, "Connector Build"));
|
|
||||||
async move {
|
|
||||||
connector.start().await
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn connect_to_seed(&mut self) {
|
|
||||||
self.connector_cmd(ConnectorCommand::ConnectToTcpSeed(SEED_NODES[0])).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run(&mut self) {
|
|
||||||
|
|
||||||
if let Some(addr) = self.addr {
|
|
||||||
self.start_connection_listner(addr).await;
|
|
||||||
} else {
|
|
||||||
self.start_connection_listner(SocketAddr::new(std::net::IpAddr::V4(std::net::Ipv4Addr::new(0,0,0,0)), 8080)).await;
|
|
||||||
};
|
|
||||||
|
|
||||||
publish_system_event(SystemEvent::NodeStarted);
|
|
||||||
|
|
||||||
while let Some(command) = self.rx.recv().await {
|
|
||||||
match command {
|
|
||||||
NodeCommand::BootStrap => {
|
|
||||||
log(msg!(DEBUG, "Received NodeCommand::BootStrap"));
|
|
||||||
let _ = self.bootstrap().await;
|
|
||||||
},
|
|
||||||
NodeCommand::StartListner(addr) => {
|
|
||||||
self.start_connection_listner(addr).await;
|
|
||||||
},
|
|
||||||
NodeCommand::ConnectToSeeds => {
|
|
||||||
self.connect_to_seed().await;
|
|
||||||
},
|
|
||||||
NodeCommand::ConnectTcpPeer(addr) => {
|
|
||||||
log(msg!(DEBUG, "Received ConnectToPeer: {addr}"));
|
|
||||||
if let Ok(addr_sock) = addr.parse::<SocketAddr>() {
|
|
||||||
let mes = ConnectorCommand::ConnectToTcpPeer(addr_sock);
|
|
||||||
self.connector_cmd(mes).await;
|
|
||||||
} else {
|
|
||||||
log(msg!(ERROR, "Failed to Parse to sock_addr: {addr}"));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
NodeCommand::PingAddr(addr) => {
|
|
||||||
if let Ok(addr_sock) = addr.parse::<SocketAddr>() {
|
|
||||||
let mes = ProtocolMessage::Ping { peer_id: self.id };
|
|
||||||
self.send_message_to_peer_addr(addr_sock, mes).await;
|
|
||||||
} else {
|
|
||||||
log(msg!(ERROR, "Failed to Parse to sock_addr: {addr}"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NodeCommand::PingId(id) => {
|
|
||||||
if let Ok(id) = id.parse::<Uuid>() {
|
|
||||||
let mes = ProtocolMessage::Ping { peer_id: self.id };
|
|
||||||
self.send_message_to_peer_id(id, mes).await;
|
|
||||||
} else {
|
|
||||||
log(msg!(ERROR, "Failed to Parse to sock_addr: {id}"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NodeCommand::AddPeer(peer) => {
|
|
||||||
self.add_tcp_peer(peer).await;
|
|
||||||
},
|
|
||||||
NodeCommand::RemovePeer { peer_id } => {
|
|
||||||
self.remove_tcp_peer(peer_id).await;
|
|
||||||
}
|
|
||||||
NodeCommand::ProcessMessage { peer_id, message } => {
|
|
||||||
self.process_message(peer_id, message).await;
|
|
||||||
},
|
|
||||||
NodeCommand::ProcessNetworkData(data) => {
|
|
||||||
self.network_data(data.clone()).await;
|
|
||||||
self.broadcast_network_data(data).await;
|
|
||||||
},
|
|
||||||
NodeCommand::CreateBlock => {
|
|
||||||
log(msg!(DEBUG, "Received CreateBlock Command"));
|
|
||||||
let block = self.chain.create_block();
|
|
||||||
self.broadcast_block(&block).await;
|
|
||||||
},
|
|
||||||
NodeCommand::ListBlocks => {
|
|
||||||
log(msg!(DEBUG, "Received DebugListBlocks command"));
|
|
||||||
log(self.chain.list_blocks());
|
|
||||||
},
|
|
||||||
NodeCommand::ListPeers => {
|
|
||||||
log(msg!(DEBUG, "Received DebugListPeers command"));
|
|
||||||
log(self.list_peers());
|
|
||||||
},
|
|
||||||
NodeCommand::ShowId => {
|
|
||||||
log(msg!(DEBUG, "Received DebugListBlocks command"));
|
|
||||||
self.show_id().await;
|
|
||||||
},
|
|
||||||
NodeCommand::DumpBlocks(s) => {
|
|
||||||
self.chain.dump_blocks(s);
|
|
||||||
},
|
|
||||||
NodeCommand::Exit => {
|
|
||||||
log(msg!(DEBUG, "Node Exit"));
|
|
||||||
break ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
413
node/src/node/node.rs
Normal file
413
node/src/node/node.rs
Normal file
@ -0,0 +1,413 @@
|
|||||||
|
use crate::core::{self, Blockchain, NetworkData, ValidationError};
|
||||||
|
use crate::error::print_error_chain;
|
||||||
|
use crate::bus::{SystemEvent, publish_system_event};
|
||||||
|
use crate::protocol::ProtocolMessage;
|
||||||
|
|
||||||
|
use crate::protocol::{Connector, ConnectorCommand};
|
||||||
|
use crate::seeds_constants::SEED_NODES;
|
||||||
|
use crate::watcher::executor::ExecutorCommand;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use tokio::sync::mpsc;
|
||||||
|
use uuid::Uuid;
|
||||||
|
use vlogger::*;
|
||||||
|
|
||||||
|
use crate::log;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TcpPeer {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub addr: SocketAddr,
|
||||||
|
pub sender: tokio::sync::mpsc::Sender<ProtocolMessage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TcpPeer {
|
||||||
|
pub fn new(
|
||||||
|
id: Uuid,
|
||||||
|
addr: SocketAddr,
|
||||||
|
sender: tokio::sync::mpsc::Sender<ProtocolMessage>,
|
||||||
|
) -> Self {
|
||||||
|
Self { id, addr, sender }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct Node {
|
||||||
|
pub tcp_connector: Option<mpsc::Sender<ConnectorCommand>>,
|
||||||
|
pub id: Uuid,
|
||||||
|
pub addr: Option<SocketAddr>,
|
||||||
|
pub tcp_peers: HashMap<Uuid, TcpPeer>,
|
||||||
|
listner_handle: Option<tokio::task::JoinHandle<()>>,
|
||||||
|
exec_tx: mpsc::Sender<ExecutorCommand>,
|
||||||
|
rx: mpsc::Receiver<NodeCommand>,
|
||||||
|
tx: mpsc::Sender<NodeCommand>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum NodeError {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum NodeCommand {
|
||||||
|
AddPeer(TcpPeer),
|
||||||
|
RemovePeer {
|
||||||
|
peer_id: Uuid,
|
||||||
|
},
|
||||||
|
ProcessMessage {
|
||||||
|
peer_id: Uuid,
|
||||||
|
message: ProtocolMessage,
|
||||||
|
},
|
||||||
|
ProcessNetworkData(NetworkData),
|
||||||
|
StartListner(SocketAddr),
|
||||||
|
PingAddr(String),
|
||||||
|
PingId(String),
|
||||||
|
CreateBlock,
|
||||||
|
ListBlocks,
|
||||||
|
ListPeers,
|
||||||
|
ShowId,
|
||||||
|
DumpBlocks(String),
|
||||||
|
ConnectToSeeds,
|
||||||
|
ConnectTcpPeer(String),
|
||||||
|
BootStrap,
|
||||||
|
Exit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node {
|
||||||
|
pub fn peer_addresses(&self) -> Vec<SocketAddr> {
|
||||||
|
let mut addr: Vec<SocketAddr> = self
|
||||||
|
.tcp_peers
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.1.addr.to_string().parse::<SocketAddr>().unwrap())
|
||||||
|
.collect();
|
||||||
|
if let Some(a) = self.addr {
|
||||||
|
addr.push(a.clone());
|
||||||
|
}
|
||||||
|
addr
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list_peers(&self) -> String {
|
||||||
|
let mut ret = String::from("Peer List\n-----------\n");
|
||||||
|
for (i, p) in self.tcp_peers.iter().enumerate() {
|
||||||
|
ret.push_str(format!("Peer #{i}: {}\n", p.1.id).as_str())
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn show_id(&self) {
|
||||||
|
log(msg!(DEBUG, "Node Id: {}", self.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn remove_tcp_peer(&mut self, peer_id: Uuid) {
|
||||||
|
log(msg!(DEBUG, "Removing Peer {peer_id}"));
|
||||||
|
self.tcp_peers.remove_entry(&peer_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn add_tcp_peer(&mut self, peer: TcpPeer) {
|
||||||
|
log(msg!(DEBUG, "Added Peer from address: {}", peer.addr));
|
||||||
|
self.tcp_peers.insert(peer.id, peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn new_with_id(
|
||||||
|
id: uuid::Uuid,
|
||||||
|
exec_tx: mpsc::Sender<ExecutorCommand>,
|
||||||
|
addr: Option<SocketAddr>,
|
||||||
|
) -> Self {
|
||||||
|
let (tx, rx) = mpsc::channel::<NodeCommand>(100);
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
tcp_peers: HashMap::new(),
|
||||||
|
addr,
|
||||||
|
exec_tx,
|
||||||
|
listner_handle: None,
|
||||||
|
tcp_connector: None,
|
||||||
|
tx,
|
||||||
|
rx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn new(
|
||||||
|
addr: Option<SocketAddr>,
|
||||||
|
blocks_json: &str,
|
||||||
|
exec_tx: mpsc::Sender<ExecutorCommand>,
|
||||||
|
) -> Self {
|
||||||
|
let (tx, rx) = mpsc::channel::<NodeCommand>(100);
|
||||||
|
let chain = Blockchain::build(blocks_json).unwrap_or_else(|e| {
|
||||||
|
print_error_chain(e.into());
|
||||||
|
Default::default()
|
||||||
|
});
|
||||||
|
Self {
|
||||||
|
id: Uuid::new_v4(),
|
||||||
|
tcp_peers: HashMap::new(),
|
||||||
|
addr,
|
||||||
|
exec_tx,
|
||||||
|
listner_handle: None,
|
||||||
|
tcp_connector: None,
|
||||||
|
tx,
|
||||||
|
rx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_blocks(&mut self, ) -> Result<Vec<core::Block>, NodeError> {
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn process_message(&mut self, peer_id: uuid::Uuid, message: ProtocolMessage) {
|
||||||
|
match message {
|
||||||
|
ProtocolMessage::BootstrapRequest { .. } => {
|
||||||
|
log(msg!(DEBUG, "Received BootstrapRequest from {peer_id}"));
|
||||||
|
let peer = &self.tcp_peers[&peer_id];
|
||||||
|
let blocks = self.get_blocks();
|
||||||
|
let resp = ProtocolMessage::BootstrapResponse {
|
||||||
|
blocks: serde_json::to_string(&self.chain.blocks().to_vec()).unwrap_or_else(
|
||||||
|
|e| {
|
||||||
|
log(msg!(
|
||||||
|
WARNING,
|
||||||
|
"Failed to serde Chain for BootstrapResponse: {e}"
|
||||||
|
));
|
||||||
|
Default::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
};
|
||||||
|
peer.sender.send(resp).await.unwrap();
|
||||||
|
log(msg!(DEBUG, "Send BootstrapResponse to {peer_id}"));
|
||||||
|
}
|
||||||
|
ProtocolMessage::BootstrapResponse { blocks } => {
|
||||||
|
log(msg!(DEBUG, "Received BootstrapResponse from seed"));
|
||||||
|
self.chain = core::Blockchain::build(&blocks).unwrap();
|
||||||
|
}
|
||||||
|
ProtocolMessage::Ping { peer_id } => {
|
||||||
|
log(msg!(DEBUG, "Received Ping from {peer_id}"));
|
||||||
|
let resp = ProtocolMessage::Pong {
|
||||||
|
peer_id: self.id.clone(),
|
||||||
|
};
|
||||||
|
let peer = &self.tcp_peers[&peer_id];
|
||||||
|
peer.sender.send(resp).await.unwrap();
|
||||||
|
}
|
||||||
|
ProtocolMessage::GetPeersRequest { peer_id } => {
|
||||||
|
log(msg!(DEBUG, "Received GetPeersRequest from {peer_id}"));
|
||||||
|
let peers = self.peer_addresses();
|
||||||
|
let resp = ProtocolMessage::GetPeersResponse {
|
||||||
|
peer_addresses: peers,
|
||||||
|
};
|
||||||
|
let peer = &self.tcp_peers[&peer_id];
|
||||||
|
peer.sender.send(resp).await.unwrap();
|
||||||
|
}
|
||||||
|
ProtocolMessage::Block { block, .. } => {
|
||||||
|
log(msg!(DEBUG, "Received Block from {peer_id}"));
|
||||||
|
self.chain.add_block(block.clone())
|
||||||
|
}
|
||||||
|
ProtocolMessage::NetworkData { data, .. } => {
|
||||||
|
log(msg!(DEBUG, "Received NetworkData from {peer_id}"));
|
||||||
|
self.chain.apply(data).unwrap()
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
log(msg!(DEBUG, "TODO: implement this message type"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_message_to_peer_addr(&self, addr: SocketAddr, msg: ProtocolMessage) {
|
||||||
|
if let Some((_, peer)) = self.tcp_peers.iter().find(|(_, v)| v.addr == addr) {
|
||||||
|
if let Err(e) = peer.sender.send(msg).await {
|
||||||
|
log(msg!(ERROR, "Error Sending message to peer: {e}"));
|
||||||
|
}
|
||||||
|
log(msg!(DEBUG, "Sent BootstrapRequest to seed"));
|
||||||
|
} else {
|
||||||
|
log(msg!(
|
||||||
|
ERROR,
|
||||||
|
"Error Sending message to peer: peer not in list"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_message_to_peer_id(&self, id: Uuid, msg: ProtocolMessage) {
|
||||||
|
if let Some(peer) = self.tcp_peers.get(&id) {
|
||||||
|
if let Err(e) = peer.sender.send(msg).await {
|
||||||
|
log(msg!(ERROR, "Error Sending message to peer: {e}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_message_to_seed(&self, msg: ProtocolMessage) {
|
||||||
|
for seed in SEED_NODES.iter() {
|
||||||
|
if let Some(_) = self.tcp_peers.iter().find(|(_, v)| v.addr == *seed) {
|
||||||
|
self.send_message_to_peer_addr(*seed, msg).await;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
self.send_message_to_peer_addr(*seed, msg).await;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log(msg!(ERROR, "No Seed Nodes Avaliable"));
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn bootstrap(&mut self) -> Result<(), ValidationError> {
|
||||||
|
log(msg!(DEBUG, "Bootstrapping"));
|
||||||
|
|
||||||
|
let message = ProtocolMessage::BootstrapRequest {
|
||||||
|
peer_id: self.id,
|
||||||
|
version: "".to_string(),
|
||||||
|
};
|
||||||
|
self.send_message_to_seed(message).await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn broadcast_network_data(&self, data: NetworkData) {
|
||||||
|
for (id, peer) in &self.tcp_peers {
|
||||||
|
let message = ProtocolMessage::NetworkData {
|
||||||
|
peer_id: self.id,
|
||||||
|
data: data.clone(),
|
||||||
|
};
|
||||||
|
peer.sender.send(message).await.unwrap();
|
||||||
|
log(msg!(DEBUG, "Send Transaction message to {id}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn broadcast_block(&self, block: &core::Block) {
|
||||||
|
for (id, peer) in &self.tcp_peers {
|
||||||
|
let message = ProtocolMessage::Block {
|
||||||
|
peer_id: self.id,
|
||||||
|
height: block.head().height as u64,
|
||||||
|
block: block.clone(),
|
||||||
|
};
|
||||||
|
peer.sender.send(message).await.unwrap();
|
||||||
|
log(msg!(DEBUG, "Send Block message to {id}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tx(&self) -> mpsc::Sender<NodeCommand> {
|
||||||
|
return self.tx.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exec_tx(&self) -> mpsc::Sender<ExecutorCommand> {
|
||||||
|
return self.exec_tx.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn network_data(&mut self, data: NetworkData) {
|
||||||
|
match self.chain.apply(data) {
|
||||||
|
Ok(_) => log(msg!(DEBUG, "NetworkData Applied")),
|
||||||
|
Err(e) => print_error_chain(e.into()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn connector_cmd(&self, cmd: ConnectorCommand) {
|
||||||
|
match &self.tcp_connector {
|
||||||
|
Some(t) => match t.send(cmd).await {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(e) => log(msg!(ERROR, "Failed to Send Command to connector: {}", e)),
|
||||||
|
},
|
||||||
|
None => log(msg!(ERROR, "No Connector Availiable")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn start_connection_listner(&mut self, addr: SocketAddr) {
|
||||||
|
log(msg!(DEBUG, "Starting Connection Listener"));
|
||||||
|
let (con_tx, con_rx) = mpsc::channel::<ConnectorCommand>(100);
|
||||||
|
|
||||||
|
self.tcp_connector = Some(con_tx);
|
||||||
|
|
||||||
|
self.listner_handle = Some(tokio::spawn({
|
||||||
|
let mut connector = Connector::new(self.id, addr, self.exec_tx(), con_rx);
|
||||||
|
log(msg!(DEBUG, "Connector Build"));
|
||||||
|
async move { connector.start().await }
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn connect_to_seed(&mut self) {
|
||||||
|
self.connector_cmd(ConnectorCommand::ConnectToTcpSeed(SEED_NODES[0]))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run(&mut self) {
|
||||||
|
if let Some(addr) = self.addr {
|
||||||
|
self.start_connection_listner(addr).await;
|
||||||
|
} else {
|
||||||
|
self.start_connection_listner(SocketAddr::new(
|
||||||
|
std::net::IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 0)),
|
||||||
|
8080,
|
||||||
|
))
|
||||||
|
.await;
|
||||||
|
};
|
||||||
|
|
||||||
|
publish_system_event(SystemEvent::NodeStarted);
|
||||||
|
|
||||||
|
while let Some(command) = self.rx.recv().await {
|
||||||
|
match command {
|
||||||
|
NodeCommand::BootStrap => {
|
||||||
|
log(msg!(DEBUG, "Received NodeCommand::BootStrap"));
|
||||||
|
let _ = self.bootstrap().await;
|
||||||
|
}
|
||||||
|
NodeCommand::StartListner(addr) => {
|
||||||
|
self.start_connection_listner(addr).await;
|
||||||
|
}
|
||||||
|
NodeCommand::ConnectToSeeds => {
|
||||||
|
self.connect_to_seed().await;
|
||||||
|
}
|
||||||
|
NodeCommand::ConnectTcpPeer(addr) => {
|
||||||
|
log(msg!(DEBUG, "Received ConnectToPeer: {addr}"));
|
||||||
|
if let Ok(addr_sock) = addr.parse::<SocketAddr>() {
|
||||||
|
let mes = ConnectorCommand::ConnectToTcpPeer(addr_sock);
|
||||||
|
self.connector_cmd(mes).await;
|
||||||
|
} else {
|
||||||
|
log(msg!(ERROR, "Failed to Parse to sock_addr: {addr}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NodeCommand::PingAddr(addr) => {
|
||||||
|
if let Ok(addr_sock) = addr.parse::<SocketAddr>() {
|
||||||
|
let mes = ProtocolMessage::Ping { peer_id: self.id };
|
||||||
|
self.send_message_to_peer_addr(addr_sock, mes).await;
|
||||||
|
} else {
|
||||||
|
log(msg!(ERROR, "Failed to Parse to sock_addr: {addr}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NodeCommand::PingId(id) => {
|
||||||
|
if let Ok(id) = id.parse::<Uuid>() {
|
||||||
|
let mes = ProtocolMessage::Ping { peer_id: self.id };
|
||||||
|
self.send_message_to_peer_id(id, mes).await;
|
||||||
|
} else {
|
||||||
|
log(msg!(ERROR, "Failed to Parse to sock_addr: {id}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NodeCommand::AddPeer(peer) => {
|
||||||
|
self.add_tcp_peer(peer).await;
|
||||||
|
}
|
||||||
|
NodeCommand::RemovePeer { peer_id } => {
|
||||||
|
self.remove_tcp_peer(peer_id).await;
|
||||||
|
}
|
||||||
|
NodeCommand::ProcessMessage { peer_id, message } => {
|
||||||
|
self.process_message(peer_id, message).await;
|
||||||
|
}
|
||||||
|
NodeCommand::ProcessNetworkData(data) => {
|
||||||
|
self.network_data(data.clone()).await;
|
||||||
|
self.broadcast_network_data(data).await;
|
||||||
|
}
|
||||||
|
NodeCommand::CreateBlock => {
|
||||||
|
log(msg!(DEBUG, "Received CreateBlock Command"));
|
||||||
|
let block = self.chain.create_block();
|
||||||
|
self.broadcast_block(&block).await;
|
||||||
|
}
|
||||||
|
NodeCommand::ListBlocks => {
|
||||||
|
log(msg!(DEBUG, "Received DebugListBlocks command"));
|
||||||
|
log(self.chain.list_blocks());
|
||||||
|
}
|
||||||
|
NodeCommand::ListPeers => {
|
||||||
|
log(msg!(DEBUG, "Received DebugListPeers command"));
|
||||||
|
log(self.list_peers());
|
||||||
|
}
|
||||||
|
NodeCommand::ShowId => {
|
||||||
|
log(msg!(DEBUG, "Received DebugListBlocks command"));
|
||||||
|
self.show_id().await;
|
||||||
|
}
|
||||||
|
NodeCommand::DumpBlocks(s) => {
|
||||||
|
self.chain.dump_blocks(s);
|
||||||
|
}
|
||||||
|
NodeCommand::Exit => {
|
||||||
|
log(msg!(DEBUG, "Node Exit"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
use tokio::net;
|
use crate::node::node;
|
||||||
use crate::protocol::ProtocolMessage;
|
use crate::protocol::ProtocolMessage;
|
||||||
use crate::watcher::ExecutorCommand;
|
use crate::watcher::ExecutorCommand;
|
||||||
use crate::native_node::node;
|
use tokio::net;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
use super::Connector;
|
use super::Connector;
|
||||||
@ -11,82 +11,83 @@ use vlogger::*;
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Connection {
|
pub struct Connection {
|
||||||
node_id: uuid::Uuid,
|
|
||||||
peer_id: uuid::Uuid,
|
|
||||||
stream: net::TcpStream,
|
|
||||||
exec_tx: mpsc::Sender<ExecutorCommand>,
|
|
||||||
rx: mpsc::Receiver<ProtocolMessage>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Connection {
|
|
||||||
pub fn new(
|
|
||||||
node_id: uuid::Uuid,
|
node_id: uuid::Uuid,
|
||||||
peer_id: uuid::Uuid,
|
peer_id: uuid::Uuid,
|
||||||
stream: net::TcpStream,
|
stream: net::TcpStream,
|
||||||
exec_tx: mpsc::Sender<ExecutorCommand>,
|
exec_tx: mpsc::Sender<ExecutorCommand>,
|
||||||
rx: mpsc::Receiver<ProtocolMessage>,
|
rx: mpsc::Receiver<ProtocolMessage>,
|
||||||
) -> Self {
|
}
|
||||||
Self {
|
|
||||||
node_id,
|
impl Connection {
|
||||||
peer_id,
|
pub fn new(
|
||||||
stream,
|
node_id: uuid::Uuid,
|
||||||
rx,
|
peer_id: uuid::Uuid,
|
||||||
exec_tx
|
stream: net::TcpStream,
|
||||||
}
|
exec_tx: mpsc::Sender<ExecutorCommand>,
|
||||||
}
|
rx: mpsc::Receiver<ProtocolMessage>,
|
||||||
|
) -> Self {
|
||||||
async fn log(&self, msg: String) {
|
Self {
|
||||||
let _ = self.exec_tx.send(ExecutorCommand::Print(msg)).await;
|
node_id,
|
||||||
}
|
peer_id,
|
||||||
|
stream,
|
||||||
pub async fn start(mut self) {
|
rx,
|
||||||
tokio::spawn(async move {
|
exec_tx,
|
||||||
self.log(msg!(DEBUG, "Started Message Handler for {}", self.peer_id)).await;
|
}
|
||||||
|
}
|
||||||
loop {
|
|
||||||
tokio::select! {
|
async fn log(&self, msg: String) {
|
||||||
response_result = self.rx.recv() => {
|
let _ = self.exec_tx.send(ExecutorCommand::Print(msg)).await;
|
||||||
match response_result {
|
}
|
||||||
Some(response) => {
|
|
||||||
if let Err(e) = Connector::send_message(&mut self.stream, &response).await {
|
pub async fn start(mut self) {
|
||||||
self.log(msg!(ERROR, "Failed to send response to {}: {}", self.peer_id, e)).await;
|
tokio::spawn(async move {
|
||||||
break;
|
self.log(msg!(DEBUG, "Started Message Handler for {}", self.peer_id))
|
||||||
}
|
.await;
|
||||||
},
|
|
||||||
None => {
|
loop {
|
||||||
self.log(msg!(DEBUG, "Response channel closed for {}", self.peer_id)).await;
|
tokio::select! {
|
||||||
break;
|
response_result = self.rx.recv() => {
|
||||||
}
|
match response_result {
|
||||||
}
|
Some(response) => {
|
||||||
}
|
if let Err(e) = Connector::send_message(&mut self.stream, &response).await {
|
||||||
|
self.log(msg!(ERROR, "Failed to send response to {}: {}", self.peer_id, e)).await;
|
||||||
message_result = Connector::receive_message(&mut self.stream) => {
|
break;
|
||||||
match message_result {
|
}
|
||||||
Ok(message) => {
|
},
|
||||||
self.log(msg!(DEBUG, "Received Message from {}", self.peer_id)).await;
|
None => {
|
||||||
|
self.log(msg!(DEBUG, "Response channel closed for {}", self.peer_id)).await;
|
||||||
let command = ExecutorCommand::Node(node::NodeCommand::ProcessMessage {
|
break;
|
||||||
peer_id: self.peer_id,
|
}
|
||||||
message: message.clone()
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
if self.exec_tx.send(command).await.is_err() {
|
message_result = Connector::receive_message(&mut self.stream) => {
|
||||||
self.log(msg!(ERROR, "Failed to send command to main thread from {}", self.peer_id)).await;
|
match message_result {
|
||||||
break;
|
Ok(message) => {
|
||||||
}
|
self.log(msg!(DEBUG, "Received Message from {}", self.peer_id)).await;
|
||||||
},
|
|
||||||
Err(e) => {
|
let command = ExecutorCommand::Node(node::NodeCommand::ProcessMessage {
|
||||||
self.log(msg!(WARNING, "Connection to {} closed: {}", self.peer_id, e.message)).await;
|
peer_id: self.peer_id,
|
||||||
let cmd = ExecutorCommand::Node(node::NodeCommand::RemovePeer {
|
message: message.clone()
|
||||||
peer_id: self.peer_id
|
});
|
||||||
});
|
|
||||||
self.exec_tx.send(cmd).await.unwrap();
|
if self.exec_tx.send(command).await.is_err() {
|
||||||
break;
|
self.log(msg!(ERROR, "Failed to send command to main thread from {}", self.peer_id)).await;
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
Err(e) => {
|
||||||
}
|
self.log(msg!(WARNING, "Connection to {} closed: {}", self.peer_id, e.message)).await;
|
||||||
});
|
let cmd = ExecutorCommand::Node(node::NodeCommand::RemovePeer {
|
||||||
}
|
peer_id: self.peer_id
|
||||||
|
});
|
||||||
|
self.exec_tx.send(cmd).await.unwrap();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,31 +1,31 @@
|
|||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
use std::net::SocketAddr;
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
use tokio::net;
|
use tokio::net;
|
||||||
use std::net::SocketAddr;
|
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use vlogger::*;
|
use vlogger::*;
|
||||||
|
|
||||||
use crate::error::print_error_chain;
|
use crate::error::print_error_chain;
|
||||||
use crate::log;
|
use crate::log;
|
||||||
|
|
||||||
use thiserror::*;
|
|
||||||
use super::Connection;
|
use super::Connection;
|
||||||
use crate::native_node::error;
|
use crate::bus::*;
|
||||||
|
use crate::node::error;
|
||||||
|
use crate::node::node;
|
||||||
use crate::protocol::ProtocolMessage;
|
use crate::protocol::ProtocolMessage;
|
||||||
use crate::watcher::ExecutorCommand;
|
use crate::watcher::ExecutorCommand;
|
||||||
use crate::native_node::node;
|
use thiserror::*;
|
||||||
use crate::event_bus::*;
|
|
||||||
|
|
||||||
pub enum ConnectorCommand {
|
pub enum ConnectorCommand {
|
||||||
ConnectToTcpPeer(SocketAddr),
|
ConnectToTcpPeer(SocketAddr),
|
||||||
ConnectToTcpSeed(SocketAddr),
|
ConnectToTcpSeed(SocketAddr),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Connector {
|
pub struct Connector {
|
||||||
node_id: uuid::Uuid,
|
node_id: uuid::Uuid,
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
exec_tx: mpsc::Sender<ExecutorCommand>,
|
exec_tx: mpsc::Sender<ExecutorCommand>,
|
||||||
rx: mpsc::Receiver<ConnectorCommand>,
|
rx: mpsc::Receiver<ConnectorCommand>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
@ -37,236 +37,279 @@ pub enum ConnectorError {
|
|||||||
const MAX_LISTNER_TRIES: usize = 5;
|
const MAX_LISTNER_TRIES: usize = 5;
|
||||||
|
|
||||||
impl Connector {
|
impl Connector {
|
||||||
|
pub fn new(
|
||||||
pub fn new(
|
node_id: uuid::Uuid,
|
||||||
node_id: uuid::Uuid,
|
addr: SocketAddr,
|
||||||
addr: SocketAddr,
|
exec_tx: mpsc::Sender<ExecutorCommand>,
|
||||||
exec_tx: mpsc::Sender<ExecutorCommand>,
|
rx: mpsc::Receiver<ConnectorCommand>,
|
||||||
rx: mpsc::Receiver<ConnectorCommand>,
|
) -> Self {
|
||||||
) -> Self {
|
Self {
|
||||||
Self {
|
node_id,
|
||||||
node_id,
|
addr,
|
||||||
addr,
|
exec_tx,
|
||||||
exec_tx,
|
rx,
|
||||||
rx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn start(&mut self) {
|
|
||||||
let mut listner: Option<tokio::net::TcpListener> = None;
|
|
||||||
let mut listner_err = None;
|
|
||||||
for _ in 0..MAX_LISTNER_TRIES {
|
|
||||||
match tokio::net::TcpListener::bind(self.addr).await {
|
|
||||||
Ok(l) => {
|
|
||||||
log(msg!(DEBUG, "Listening on address: {}", self.addr));
|
|
||||||
listner = Some(l);
|
|
||||||
break ;
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
|
||||||
self.addr.set_port(self.addr.port() + 1);
|
|
||||||
listner_err = Some(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
if let Some(listener) = listner {
|
|
||||||
loop {
|
|
||||||
tokio::select! {
|
|
||||||
cmd_result = self.rx.recv() => {
|
|
||||||
match cmd_result {
|
|
||||||
Some(cmd) => {
|
|
||||||
self.execute_cmd(cmd).await;
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
log(msg!(DEBUG, "Command channel closed"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
accept_result = listener.accept() => {
|
|
||||||
match accept_result {
|
|
||||||
Ok((stream, addr)) => {
|
|
||||||
log(msg!(DEBUG, "Accepted connection from {}", addr));
|
|
||||||
self.establish_connection_inbound(stream, addr).await;
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
log(msg!(ERROR, "Failed to accept connection: {}", e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log(msg!(FATAL, "Failed to start TCP Listener: {}", listner_err.unwrap()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn execute_cmd(&mut self, cmd: ConnectorCommand) {
|
pub async fn start(&mut self) {
|
||||||
match cmd {
|
let mut listner: Option<tokio::net::TcpListener> = None;
|
||||||
ConnectorCommand::ConnectToTcpPeer(addr) => {
|
let mut listner_err = None;
|
||||||
self.connect_to_peer(addr).await
|
for _ in 0..MAX_LISTNER_TRIES {
|
||||||
}
|
match tokio::net::TcpListener::bind(self.addr).await {
|
||||||
ConnectorCommand::ConnectToTcpSeed(addr) => {
|
Ok(l) => {
|
||||||
self.connect_to_seed(addr).await;
|
log(msg!(DEBUG, "Listening on address: {}", self.addr));
|
||||||
}
|
listner = Some(l);
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
|
Err(e) => {
|
||||||
pub async fn connect_to_seed(&self, addr: SocketAddr) {
|
self.addr.set_port(self.addr.port() + 1);
|
||||||
match net::TcpStream::connect(addr).await
|
listner_err = Some(e);
|
||||||
.with_context(|| format!("Connecting to {}", addr)){
|
}
|
||||||
Ok(stream) => {
|
|
||||||
self.establish_connection_to_seed(stream, addr).await
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
// let err = ConnectorError::ConnectionError(e.into());
|
|
||||||
print_error_chain(e.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn connect_to_peer(&self, addr: SocketAddr) {
|
|
||||||
match net::TcpStream::connect(addr).await {
|
|
||||||
Ok(stream) => {
|
|
||||||
self.establish_connection_outbound(stream, addr).await
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
let err = ConnectorError::ConnectionError(e.into());
|
|
||||||
print_error_chain(err.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn establish_connection_to_seed(
|
|
||||||
&self,
|
|
||||||
mut stream: tokio::net::TcpStream,
|
|
||||||
addr: SocketAddr,
|
|
||||||
) {
|
|
||||||
let handshake = ProtocolMessage::Handshake {
|
|
||||||
peer_id: self.node_id,
|
|
||||||
version: "".to_string()
|
|
||||||
};
|
|
||||||
match Connector::send_message(&mut stream, &handshake).await {
|
|
||||||
Ok(()) => {
|
|
||||||
if let Ok(mes) = Connector::receive_message(&mut stream).await {
|
|
||||||
let (ch_tx, ch_rx) = mpsc::channel::<ProtocolMessage>(100);
|
|
||||||
let peer = match mes {
|
|
||||||
ProtocolMessage::HandshakeAck { peer_id, .. } => {
|
|
||||||
node::TcpPeer::new(peer_id, addr, ch_tx)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
log(msg!(ERROR, "Invalid Message On Connetion Establishment: {mes}"));
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let cmd = ExecutorCommand::Node(node::NodeCommand::AddPeer(peer.clone()));
|
|
||||||
publish_network_event(NetworkEvent::SeedConnected(addr.to_string()));
|
|
||||||
let _ = self.exec_tx.send(cmd).await;
|
|
||||||
Connection::new(self.node_id, peer.id, stream, self.exec_tx.clone(), ch_rx).start().await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(e) => print_error_chain(e.into())
|
if let Some(listener) = listner {
|
||||||
}
|
loop {
|
||||||
}
|
tokio::select! {
|
||||||
|
cmd_result = self.rx.recv() => {
|
||||||
async fn establish_connection_outbound(
|
match cmd_result {
|
||||||
&self,
|
Some(cmd) => {
|
||||||
mut stream: tokio::net::TcpStream,
|
self.execute_cmd(cmd).await;
|
||||||
addr: SocketAddr,
|
}
|
||||||
) {
|
None => {
|
||||||
let handshake = ProtocolMessage::Handshake {
|
log(msg!(DEBUG, "Command channel closed"));
|
||||||
peer_id: self.node_id,
|
break;
|
||||||
version: "".to_string()
|
}
|
||||||
};
|
}
|
||||||
match Connector::send_message(&mut stream, &handshake).await {
|
}
|
||||||
Ok(()) => {
|
accept_result = listener.accept() => {
|
||||||
if let Ok(mes) = Connector::receive_message(&mut stream).await {
|
match accept_result {
|
||||||
let (ch_tx, ch_rx) = mpsc::channel::<ProtocolMessage>(100);
|
Ok((stream, addr)) => {
|
||||||
let peer = match mes {
|
log(msg!(DEBUG, "Accepted connection from {}", addr));
|
||||||
ProtocolMessage::HandshakeAck { peer_id, .. } => {
|
self.establish_connection_inbound(stream, addr).await;
|
||||||
node::TcpPeer::new(peer_id, addr, ch_tx)
|
}
|
||||||
}
|
Err(e) => {
|
||||||
_ => {
|
log(msg!(ERROR, "Failed to accept connection: {}", e));
|
||||||
log(msg!(ERROR, "Invalid Message On Connetion Establishment: {mes}"));
|
}
|
||||||
return ;
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
let cmd = ExecutorCommand::Node(node::NodeCommand::AddPeer(peer.clone()));
|
}
|
||||||
let _ = self.exec_tx.send(cmd).await;
|
} else {
|
||||||
Connection::new(self.node_id, peer.id, stream, self.exec_tx.clone(), ch_rx).start().await;
|
log(msg!(
|
||||||
}
|
FATAL,
|
||||||
|
"Failed to start TCP Listener: {}",
|
||||||
|
listner_err.unwrap()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Err(e) => print_error_chain(e.into()),
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn establish_connection_inbound(
|
async fn execute_cmd(&mut self, cmd: ConnectorCommand) {
|
||||||
&self,
|
match cmd {
|
||||||
mut stream: tokio::net::TcpStream,
|
ConnectorCommand::ConnectToTcpPeer(addr) => self.connect_to_peer(addr).await,
|
||||||
addr: SocketAddr,
|
ConnectorCommand::ConnectToTcpSeed(addr) => {
|
||||||
) {
|
self.connect_to_seed(addr).await;
|
||||||
if let Ok(mes) = Connector::receive_message(&mut stream).await {
|
}
|
||||||
let (ch_tx, ch_rx) = mpsc::channel::<ProtocolMessage>(100);
|
}
|
||||||
let peer = match mes {
|
}
|
||||||
ProtocolMessage::Handshake { peer_id, .. } => {
|
|
||||||
let ack = ProtocolMessage::HandshakeAck {
|
pub async fn connect_to_seed(&self, addr: SocketAddr) {
|
||||||
|
match net::TcpStream::connect(addr)
|
||||||
|
.await
|
||||||
|
.with_context(|| format!("Connecting to {}", addr))
|
||||||
|
{
|
||||||
|
Ok(stream) => self.establish_connection_to_seed(stream, addr).await,
|
||||||
|
Err(e) => {
|
||||||
|
// let err = ConnectorError::ConnectionError(e.into());
|
||||||
|
print_error_chain(e.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn connect_to_peer(&self, addr: SocketAddr) {
|
||||||
|
match net::TcpStream::connect(addr).await {
|
||||||
|
Ok(stream) => self.establish_connection_outbound(stream, addr).await,
|
||||||
|
Err(e) => {
|
||||||
|
let err = ConnectorError::ConnectionError(e.into());
|
||||||
|
print_error_chain(err.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn establish_connection_to_seed(
|
||||||
|
&self,
|
||||||
|
mut stream: tokio::net::TcpStream,
|
||||||
|
addr: SocketAddr,
|
||||||
|
) {
|
||||||
|
let handshake = ProtocolMessage::Handshake {
|
||||||
peer_id: self.node_id,
|
peer_id: self.node_id,
|
||||||
version: "".to_string()
|
version: "".to_string(),
|
||||||
};
|
};
|
||||||
match Connector::send_message(&mut stream, &ack).await {
|
match Connector::send_message(&mut stream, &handshake).await {
|
||||||
Ok(()) => node::TcpPeer::new(peer_id, addr, ch_tx),
|
Ok(()) => {
|
||||||
Err(e) => return print_error_chain(e.into()),
|
if let Ok(mes) = Connector::receive_message(&mut stream).await {
|
||||||
}
|
let (ch_tx, ch_rx) = mpsc::channel::<ProtocolMessage>(100);
|
||||||
|
let peer = match mes {
|
||||||
|
ProtocolMessage::HandshakeAck { peer_id, .. } => {
|
||||||
|
node::TcpPeer::new(peer_id, addr, ch_tx)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
log(msg!(
|
||||||
|
ERROR,
|
||||||
|
"Invalid Message On Connetion Establishment: {mes}"
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let cmd = ExecutorCommand::Node(node::NodeCommand::AddPeer(peer.clone()));
|
||||||
|
publish_network_event(NetworkEvent::SeedConnected(addr.to_string()));
|
||||||
|
let _ = self.exec_tx.send(cmd).await;
|
||||||
|
Connection::new(self.node_id, peer.id, stream, self.exec_tx.clone(), ch_rx)
|
||||||
|
.start()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => print_error_chain(e.into()),
|
||||||
}
|
}
|
||||||
_ => {
|
|
||||||
log(msg!(ERROR, "Invalid Message On Connetion Establishment: {mes}"));
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let cmd = ExecutorCommand::Node(node::NodeCommand::AddPeer(peer.clone()));
|
|
||||||
let _ = self.exec_tx.send(cmd).await;
|
|
||||||
Connection::new(self.node_id, peer.id, stream, self.exec_tx.clone(), ch_rx).start().await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn send_message(stream: &mut net::TcpStream, message: &ProtocolMessage) -> Result<(), error::NetworkError> {
|
|
||||||
let json = serde_json::to_string(message)
|
|
||||||
.map_err(|e| error::NetworkError { message: format!("Failed to serialize: {}", e) })?;
|
|
||||||
let data = json.as_bytes();
|
|
||||||
|
|
||||||
let len = data.len() as u32;
|
|
||||||
stream.write_all(&len.to_be_bytes()).await
|
|
||||||
.map_err(|e| error::NetworkError { message: format!("Failed to write data: {}", e) })?;
|
|
||||||
|
|
||||||
stream.write_all(data).await
|
|
||||||
.map_err(|e| error::NetworkError { message: format!("Failed to write data: {}", e) })?;
|
|
||||||
stream.flush().await
|
|
||||||
.map_err(|e| error::NetworkError { message: format!("Failed to flush stream: {}", e) })?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn receive_message(stream: &mut tokio::net::TcpStream) -> Result<ProtocolMessage, error::NetworkError> {
|
|
||||||
let mut len_bytes = [0u8; 4];
|
|
||||||
stream.read_exact(&mut len_bytes).await
|
|
||||||
.map_err(|e| error::NetworkError { message: format!("Failed to read length: {}", e) })?;
|
|
||||||
|
|
||||||
let len = u32::from_be_bytes(len_bytes) as usize;
|
|
||||||
|
|
||||||
if len >= super::message::MAX_MESSAGE_SIZE {
|
|
||||||
return Err(error::NetworkError{
|
|
||||||
message: format!("NetworkError: Inbound Message too large: max = {}, got = {len}", super::message::MAX_MESSAGE_SIZE)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut data = vec![0u8; len];
|
async fn establish_connection_outbound(
|
||||||
stream.read_exact(&mut data).await
|
&self,
|
||||||
.map_err(|e| error::NetworkError { message: format!("Failed to read data: {}", e) })?;
|
mut stream: tokio::net::TcpStream,
|
||||||
|
addr: SocketAddr,
|
||||||
|
) {
|
||||||
|
let handshake = ProtocolMessage::Handshake {
|
||||||
|
peer_id: self.node_id,
|
||||||
|
version: "".to_string(),
|
||||||
|
};
|
||||||
|
match Connector::send_message(&mut stream, &handshake).await {
|
||||||
|
Ok(()) => {
|
||||||
|
if let Ok(mes) = Connector::receive_message(&mut stream).await {
|
||||||
|
let (ch_tx, ch_rx) = mpsc::channel::<ProtocolMessage>(100);
|
||||||
|
let peer = match mes {
|
||||||
|
ProtocolMessage::HandshakeAck { peer_id, .. } => {
|
||||||
|
node::TcpPeer::new(peer_id, addr, ch_tx)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
log(msg!(
|
||||||
|
ERROR,
|
||||||
|
"Invalid Message On Connetion Establishment: {mes}"
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let cmd = ExecutorCommand::Node(node::NodeCommand::AddPeer(peer.clone()));
|
||||||
|
let _ = self.exec_tx.send(cmd).await;
|
||||||
|
Connection::new(self.node_id, peer.id, stream, self.exec_tx.clone(), ch_rx)
|
||||||
|
.start()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => print_error_chain(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let json = String::from_utf8(data)
|
async fn establish_connection_inbound(
|
||||||
.map_err(|e| error::NetworkError { message: format!("Invalid UTF-8: {}", e) })?;
|
&self,
|
||||||
|
mut stream: tokio::net::TcpStream,
|
||||||
|
addr: SocketAddr,
|
||||||
|
) {
|
||||||
|
if let Ok(mes) = Connector::receive_message(&mut stream).await {
|
||||||
|
let (ch_tx, ch_rx) = mpsc::channel::<ProtocolMessage>(100);
|
||||||
|
let peer = match mes {
|
||||||
|
ProtocolMessage::Handshake { peer_id, .. } => {
|
||||||
|
let ack = ProtocolMessage::HandshakeAck {
|
||||||
|
peer_id: self.node_id,
|
||||||
|
version: "".to_string(),
|
||||||
|
};
|
||||||
|
match Connector::send_message(&mut stream, &ack).await {
|
||||||
|
Ok(()) => node::TcpPeer::new(peer_id, addr, ch_tx),
|
||||||
|
Err(e) => return print_error_chain(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
log(msg!(
|
||||||
|
ERROR,
|
||||||
|
"Invalid Message On Connetion Establishment: {mes}"
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let cmd = ExecutorCommand::Node(node::NodeCommand::AddPeer(peer.clone()));
|
||||||
|
let _ = self.exec_tx.send(cmd).await;
|
||||||
|
Connection::new(self.node_id, peer.id, stream, self.exec_tx.clone(), ch_rx)
|
||||||
|
.start()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let message: ProtocolMessage = serde_json::from_str(&json)
|
pub async fn send_message(
|
||||||
.map_err(|e| error::NetworkError { message: format!("JSON parse error: {}", e) })?;
|
stream: &mut net::TcpStream,
|
||||||
|
message: &ProtocolMessage,
|
||||||
|
) -> Result<(), error::NetworkError> {
|
||||||
|
let json = serde_json::to_string(message).map_err(|e| error::NetworkError {
|
||||||
|
message: format!("Failed to serialize: {}", e),
|
||||||
|
})?;
|
||||||
|
let data = json.as_bytes();
|
||||||
|
|
||||||
Ok(message)
|
let len = data.len() as u32;
|
||||||
}
|
stream
|
||||||
|
.write_all(&len.to_be_bytes())
|
||||||
|
.await
|
||||||
|
.map_err(|e| error::NetworkError {
|
||||||
|
message: format!("Failed to write data: {}", e),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
stream
|
||||||
|
.write_all(data)
|
||||||
|
.await
|
||||||
|
.map_err(|e| error::NetworkError {
|
||||||
|
message: format!("Failed to write data: {}", e),
|
||||||
|
})?;
|
||||||
|
stream.flush().await.map_err(|e| error::NetworkError {
|
||||||
|
message: format!("Failed to flush stream: {}", e),
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn receive_message(
|
||||||
|
stream: &mut tokio::net::TcpStream,
|
||||||
|
) -> Result<ProtocolMessage, error::NetworkError> {
|
||||||
|
let mut len_bytes = [0u8; 4];
|
||||||
|
stream
|
||||||
|
.read_exact(&mut len_bytes)
|
||||||
|
.await
|
||||||
|
.map_err(|e| error::NetworkError {
|
||||||
|
message: format!("Failed to read length: {}", e),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let len = u32::from_be_bytes(len_bytes) as usize;
|
||||||
|
|
||||||
|
if len >= super::message::MAX_MESSAGE_SIZE {
|
||||||
|
return Err(error::NetworkError {
|
||||||
|
message: format!(
|
||||||
|
"NetworkError: Inbound Message too large: max = {}, got = {len}",
|
||||||
|
super::message::MAX_MESSAGE_SIZE
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut data = vec![0u8; len];
|
||||||
|
stream
|
||||||
|
.read_exact(&mut data)
|
||||||
|
.await
|
||||||
|
.map_err(|e| error::NetworkError {
|
||||||
|
message: format!("Failed to read data: {}", e),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let json = String::from_utf8(data).map_err(|e| error::NetworkError {
|
||||||
|
message: format!("Invalid UTF-8: {}", e),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let message: ProtocolMessage =
|
||||||
|
serde_json::from_str(&json).map_err(|e| error::NetworkError {
|
||||||
|
message: format!("JSON parse error: {}", e),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,31 +1,50 @@
|
|||||||
use crate::core::{self, NetworkData};
|
use crate::core::{self, NetworkData};
|
||||||
use std::net::SocketAddr;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
pub const MAX_MESSAGE_SIZE: usize = 1_000_000;
|
pub const MAX_MESSAGE_SIZE: usize = 1_000_000;
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
||||||
pub enum ProtocolMessage {
|
pub enum ProtocolMessage {
|
||||||
BootstrapRequest {
|
BootstrapRequest {
|
||||||
peer_id: uuid::Uuid,
|
peer_id: uuid::Uuid,
|
||||||
version: String
|
version: String,
|
||||||
},
|
},
|
||||||
BootstrapResponse {
|
BootstrapResponse {
|
||||||
blocks: String
|
blocks: String,
|
||||||
},
|
},
|
||||||
GetPeersRequest {
|
GetPeersRequest {
|
||||||
peer_id: uuid::Uuid
|
peer_id: uuid::Uuid,
|
||||||
},
|
},
|
||||||
GetPeersResponse {
|
GetPeersResponse {
|
||||||
peer_addresses: Vec<SocketAddr>
|
peer_addresses: Vec<SocketAddr>,
|
||||||
},
|
},
|
||||||
Handshake { peer_id: uuid::Uuid, version: String },
|
Handshake {
|
||||||
HandshakeAck { peer_id: uuid::Uuid, version: String },
|
peer_id: uuid::Uuid,
|
||||||
Block { peer_id: uuid::Uuid, height: u64, block: core::Block },
|
version: String,
|
||||||
NetworkData { peer_id: uuid::Uuid, data: NetworkData },
|
},
|
||||||
Ping { peer_id: uuid::Uuid },
|
HandshakeAck {
|
||||||
Pong { peer_id: uuid::Uuid },
|
peer_id: uuid::Uuid,
|
||||||
Disconnect { peer_id: uuid::Uuid },
|
version: String,
|
||||||
|
},
|
||||||
|
Block {
|
||||||
|
peer_id: uuid::Uuid,
|
||||||
|
height: u64,
|
||||||
|
block: core::Block,
|
||||||
|
},
|
||||||
|
NetworkData {
|
||||||
|
peer_id: uuid::Uuid,
|
||||||
|
data: NetworkData,
|
||||||
|
},
|
||||||
|
Ping {
|
||||||
|
peer_id: uuid::Uuid,
|
||||||
|
},
|
||||||
|
Pong {
|
||||||
|
peer_id: uuid::Uuid,
|
||||||
|
},
|
||||||
|
Disconnect {
|
||||||
|
peer_id: uuid::Uuid,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ProtocolMessage {
|
impl fmt::Display for ProtocolMessage {
|
||||||
@ -49,7 +68,11 @@ impl fmt::Display for ProtocolMessage {
|
|||||||
ProtocolMessage::HandshakeAck { peer_id, version } => {
|
ProtocolMessage::HandshakeAck { peer_id, version } => {
|
||||||
write!(f, "HandshakeAck from {} (v{})", peer_id, version)
|
write!(f, "HandshakeAck from {} (v{})", peer_id, version)
|
||||||
}
|
}
|
||||||
ProtocolMessage::Block { peer_id, height, block: _ } => {
|
ProtocolMessage::Block {
|
||||||
|
peer_id,
|
||||||
|
height,
|
||||||
|
block: _,
|
||||||
|
} => {
|
||||||
write!(f, "Block #{} from {}", height, peer_id)
|
write!(f, "Block #{} from {}", height, peer_id)
|
||||||
}
|
}
|
||||||
ProtocolMessage::NetworkData { peer_id, data: _ } => {
|
ProtocolMessage::NetworkData { peer_id, data: _ } => {
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::net::{SocketAddr, IpAddr, Ipv4Addr};
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||||
|
|
||||||
pub static SEED_NODES: Lazy<[SocketAddr; 3]> = Lazy::new(|| [
|
pub static SEED_NODES: Lazy<[SocketAddr; 3]> = Lazy::new(|| {
|
||||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8333),
|
[
|
||||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)), 3000),
|
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8333),
|
||||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)), 5432),
|
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)), 3000),
|
||||||
]);
|
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)), 5432),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|||||||
@ -1,93 +1,101 @@
|
|||||||
use crate::{event_bus::{publish_render_event, publish_system_event, SystemEvent}, log, native_node::node::NodeCommand, watcher::renderer::*};
|
use crate::{
|
||||||
|
bus::{SystemEvent, publish_render_event, publish_system_event},
|
||||||
|
log,
|
||||||
|
node::node::NodeCommand,
|
||||||
|
watcher::renderer::*,
|
||||||
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use vlogger::*;
|
use vlogger::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
use super::RenderCommand;
|
||||||
pub enum ExecutorCommand {
|
|
||||||
NodeResponse(String),
|
|
||||||
Echo(Vec<String>),
|
|
||||||
Print(String),
|
|
||||||
InvalidCommand(String),
|
|
||||||
Node(NodeCommand),
|
|
||||||
Render(RenderCommand),
|
|
||||||
Exit
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum InProcessError {
|
pub enum InProcessError {
|
||||||
#[error("TODO: {0}")]
|
#[error("TODO: {0}")]
|
||||||
TODO(String),
|
TODO(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum ExecutorCommand {
|
||||||
|
NodeResponse(String),
|
||||||
|
Echo(Vec<String>),
|
||||||
|
Print(String),
|
||||||
|
InvalidCommand(String),
|
||||||
|
Node(NodeCommand),
|
||||||
|
Render(RenderCommand),
|
||||||
|
Exit,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct Executor {
|
pub struct Executor {
|
||||||
node_tx: mpsc::Sender<NodeCommand>,
|
node_tx: mpsc::Sender<NodeCommand>,
|
||||||
rx: mpsc::Receiver<ExecutorCommand>,
|
rx: mpsc::Receiver<ExecutorCommand>,
|
||||||
exit: bool
|
exit: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Executor {
|
impl Executor {
|
||||||
pub fn new(node_tx: mpsc::Sender<NodeCommand>, rx: mpsc::Receiver<ExecutorCommand>) -> Self {
|
pub fn new(node_tx: mpsc::Sender<NodeCommand>, rx: mpsc::Receiver<ExecutorCommand>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
node_tx,
|
node_tx,
|
||||||
rx,
|
rx,
|
||||||
exit: false
|
exit: false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run(&mut self) {
|
pub async fn run(&mut self) {
|
||||||
publish_system_event(SystemEvent::ExecutorStarted);
|
publish_system_event(SystemEvent::ExecutorStarted);
|
||||||
while !self.exit {
|
while !self.exit {
|
||||||
self.listen().await;
|
self.listen().await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async fn exit(&mut self) {
|
async fn exit(&mut self) {
|
||||||
log(msg!(DEBUG, "Executor Exit"));
|
log(msg!(DEBUG, "Executor Exit"));
|
||||||
self.exit = true
|
self.exit = true
|
||||||
}
|
|
||||||
|
|
||||||
async fn listen(&mut self) {
|
|
||||||
if let Some(cmd) = self.rx.recv().await {
|
|
||||||
let _ = self.execute(cmd).await;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_node_cmd(&self, cmd: NodeCommand) {
|
async fn listen(&mut self) {
|
||||||
self.node_tx.send(cmd).await.unwrap()
|
if let Some(cmd) = self.rx.recv().await {
|
||||||
}
|
let _ = self.execute(cmd).await;
|
||||||
|
}
|
||||||
async fn handle_node_cmd(&self, cmd: NodeCommand) {
|
}
|
||||||
self.send_node_cmd(cmd).await;
|
|
||||||
}
|
async fn send_node_cmd(&self, cmd: NodeCommand) {
|
||||||
|
self.node_tx.send(cmd).await.unwrap()
|
||||||
async fn echo(&self, s: Vec<String>) {
|
}
|
||||||
let mut str = s.join(" ");
|
|
||||||
str.push_str("\n");
|
async fn handle_node_cmd(&self, cmd: NodeCommand) {
|
||||||
let rd_cmd = RenderCommand::RenderStringToPane{
|
self.send_node_cmd(cmd).await;
|
||||||
str,
|
}
|
||||||
pane: RenderPane::CliOutput
|
|
||||||
};
|
async fn echo(&self, s: Vec<String>) {
|
||||||
publish_render_event(rd_cmd);
|
let mut str = s.join(" ");
|
||||||
}
|
str.push_str("\n");
|
||||||
|
let rd_cmd = RenderCommand::RenderStringToPane {
|
||||||
async fn invalid_command(&self, str: String) {
|
str,
|
||||||
let rd_cmd = RenderCommand::RenderStringToPane{
|
pane: RenderPane::CliOutput,
|
||||||
str,
|
};
|
||||||
pane: RenderPane::CliOutput
|
publish_render_event(rd_cmd);
|
||||||
};
|
}
|
||||||
publish_render_event(rd_cmd);
|
|
||||||
}
|
async fn invalid_command(&self, str: String) {
|
||||||
|
let rd_cmd = RenderCommand::RenderStringToPane {
|
||||||
async fn execute(&mut self, cmd: ExecutorCommand) {
|
str,
|
||||||
match cmd {
|
pane: RenderPane::CliOutput,
|
||||||
ExecutorCommand::NodeResponse(resp) => log(resp),
|
};
|
||||||
ExecutorCommand::Node(n) => self.handle_node_cmd(n).await,
|
publish_render_event(rd_cmd);
|
||||||
ExecutorCommand::Render(p) => publish_render_event(p),
|
}
|
||||||
ExecutorCommand::Echo(s) => self.echo(s).await,
|
|
||||||
ExecutorCommand::Print(s) => log(s),
|
async fn execute(&mut self, cmd: ExecutorCommand) {
|
||||||
ExecutorCommand::InvalidCommand(str) => self.invalid_command(str).await,
|
match cmd {
|
||||||
ExecutorCommand::Exit => self.exit().await,
|
ExecutorCommand::NodeResponse(resp) => log(resp),
|
||||||
|
ExecutorCommand::Node(n) => self.handle_node_cmd(n).await,
|
||||||
|
ExecutorCommand::Render(p) => publish_render_event(p),
|
||||||
|
ExecutorCommand::Echo(s) => self.echo(s).await,
|
||||||
|
ExecutorCommand::Print(s) => log(s),
|
||||||
|
ExecutorCommand::InvalidCommand(str) => self.invalid_command(str).await,
|
||||||
|
ExecutorCommand::Exit => self.exit().await,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,68 +1,63 @@
|
|||||||
|
use crate::cli::cli;
|
||||||
use crate::watcher::executor::ExecutorCommand;
|
use crate::watcher::executor::ExecutorCommand;
|
||||||
use vlogger::*;
|
use vlogger::*;
|
||||||
use crate::cli::cli;
|
|
||||||
|
|
||||||
use tokio::time::{timeout, Duration};
|
use tokio::time::{Duration, timeout};
|
||||||
|
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Parser {
|
pub struct Parser {
|
||||||
rx: mpsc::Receiver<ParserCommand>,
|
rx: mpsc::Receiver<ParserCommand>,
|
||||||
exec_tx: mpsc::Sender<ExecutorCommand>,
|
exec_tx: mpsc::Sender<ExecutorCommand>,
|
||||||
exit: bool
|
exit: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ParserCommand {
|
pub enum ParserCommand {
|
||||||
ParseCmdString(String),
|
ParseCmdString(String),
|
||||||
Exit
|
Exit,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parser {
|
impl Parser {
|
||||||
pub fn new(
|
pub fn new(rx: mpsc::Receiver<ParserCommand>, exec_tx: mpsc::Sender<ExecutorCommand>) -> Self {
|
||||||
rx: mpsc::Receiver<ParserCommand>,
|
Self {
|
||||||
exec_tx: mpsc::Sender<ExecutorCommand>
|
rx,
|
||||||
) -> Self {
|
exec_tx,
|
||||||
Self {
|
exit: false,
|
||||||
rx,
|
|
||||||
exec_tx,
|
|
||||||
exit: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn exit(&mut self) {
|
|
||||||
self.log(msg!(DEBUG, "Parser Exit")).await;
|
|
||||||
self.exit = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run(&mut self) {
|
|
||||||
self.log(msg!(INFO, "Started Parser")).await;
|
|
||||||
while !self.exit {
|
|
||||||
self.listen().await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn log(&self, msg: String) {
|
|
||||||
if let Err(e) = self.exec_tx.send(ExecutorCommand::Print(msg)).await {
|
|
||||||
log!(ERROR, "Error response from exec: {e}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn listen(&mut self) {
|
|
||||||
if let Ok(Some(mes)) = timeout(Duration::from_millis(400), self.rx.recv()).await {
|
|
||||||
match mes {
|
|
||||||
ParserCommand::ParseCmdString(s) => {
|
|
||||||
let argv: Vec<&str> = std::iter::once(" ")
|
|
||||||
.chain(s.split_whitespace())
|
|
||||||
.collect();
|
|
||||||
let cmd = cli(&argv);
|
|
||||||
let _ = self.exec_tx.send(cmd).await;
|
|
||||||
},
|
|
||||||
ParserCommand::Exit => {
|
|
||||||
self.exit().await;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
async fn exit(&mut self) {
|
||||||
|
self.log(msg!(DEBUG, "Parser Exit")).await;
|
||||||
|
self.exit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run(&mut self) {
|
||||||
|
self.log(msg!(INFO, "Started Parser")).await;
|
||||||
|
while !self.exit {
|
||||||
|
self.listen().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn log(&self, msg: String) {
|
||||||
|
if let Err(e) = self.exec_tx.send(ExecutorCommand::Print(msg)).await {
|
||||||
|
log!(ERROR, "Error response from exec: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn listen(&mut self) {
|
||||||
|
if let Ok(Some(mes)) = timeout(Duration::from_millis(400), self.rx.recv()).await {
|
||||||
|
match mes {
|
||||||
|
ParserCommand::ParseCmdString(s) => {
|
||||||
|
let argv: Vec<&str> =
|
||||||
|
std::iter::once(" ").chain(s.split_whitespace()).collect();
|
||||||
|
let cmd = cli(&argv);
|
||||||
|
let _ = self.exec_tx.send(cmd).await;
|
||||||
|
}
|
||||||
|
ParserCommand::Exit => {
|
||||||
|
self.exit().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -2,414 +2,404 @@ use crossterm::event::KeyCode;
|
|||||||
use ratatui::prelude::*;
|
use ratatui::prelude::*;
|
||||||
use ratatui::widgets::Wrap;
|
use ratatui::widgets::Wrap;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
buffer::Buffer,
|
Frame,
|
||||||
layout::Rect,
|
buffer::Buffer,
|
||||||
symbols::border,
|
layout::Rect,
|
||||||
widgets::{Block, Paragraph, Widget, List},
|
symbols::border,
|
||||||
Frame,
|
widgets::{Block, List, Paragraph, Widget},
|
||||||
};
|
};
|
||||||
|
|
||||||
use vlogger::*;
|
use vlogger::*;
|
||||||
|
|
||||||
use crate::event_bus::{publish_system_event, subscribe_render_event, SystemEvent};
|
use crate::bus::{SystemEvent, publish_system_event, subscribe_render_event};
|
||||||
use tokio::time::{interval, timeout, Duration};
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use tokio::time::{Duration, interval, timeout};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Renderer {
|
pub struct Renderer {
|
||||||
buffer: String,
|
buffer: String,
|
||||||
exit: bool,
|
exit: bool,
|
||||||
layout: RenderLayout,
|
layout: RenderLayout,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Pane {
|
pub struct Pane {
|
||||||
title: Option<String>,
|
title: Option<String>,
|
||||||
target: RenderPane,
|
target: RenderPane,
|
||||||
buffer: RenderBuffer,
|
buffer: RenderBuffer,
|
||||||
focused: bool,
|
focused: bool,
|
||||||
scroll: i16,
|
scroll: i16,
|
||||||
max_scroll: i16,
|
max_scroll: i16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, clap::ValueEnum)]
|
#[derive(Debug, PartialEq, Clone, clap::ValueEnum)]
|
||||||
pub enum RenderPane {
|
pub enum RenderPane {
|
||||||
#[value(name = "all", aliases = ["a"])]
|
#[value(name = "all", aliases = ["a"])]
|
||||||
All,
|
All,
|
||||||
#[value(aliases = ["i", "in"])]
|
#[value(aliases = ["i", "in"])]
|
||||||
CliInput,
|
CliInput,
|
||||||
#[value(aliases = ["o", "out"])]
|
#[value(aliases = ["o", "out"])]
|
||||||
CliOutput
|
CliOutput,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum RenderBuffer {
|
enum RenderBuffer {
|
||||||
List{ list: Vec<String>, index: usize },
|
List { list: Vec<String>, index: usize },
|
||||||
String(String)
|
String(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pane {
|
impl Pane {
|
||||||
fn render(&mut self, area: Rect, buf: &mut Buffer) {
|
fn render(&mut self, area: Rect, buf: &mut Buffer) {
|
||||||
let block = Block::bordered()
|
let block = Block::bordered()
|
||||||
.title({
|
.title({
|
||||||
if let Some(t) = &self.title {
|
if let Some(t) = &self.title {
|
||||||
t.clone()
|
t.clone()
|
||||||
} else {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.border_set(border::PLAIN)
|
|
||||||
.border_style({
|
|
||||||
if self.focused {
|
|
||||||
Style::new().green()
|
|
||||||
} else {
|
|
||||||
Style::new().white()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let inner_area = block.inner(area);
|
|
||||||
let content_width = inner_area.width as usize;
|
|
||||||
let content_height = inner_area.height as usize;
|
|
||||||
match &self.buffer {
|
|
||||||
RenderBuffer::String(s) => {
|
|
||||||
let wrapped_lines = s
|
|
||||||
.lines()
|
|
||||||
.map(|line| {
|
|
||||||
if line.is_empty() {
|
|
||||||
1
|
|
||||||
} else {
|
} else {
|
||||||
(line.len() + content_width - 1) / { content_width + (content_width == 0) as usize }
|
Default::default()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.sum::<usize>();
|
.border_set(border::PLAIN)
|
||||||
|
.border_style({
|
||||||
|
if self.focused {
|
||||||
|
Style::new().green()
|
||||||
|
} else {
|
||||||
|
Style::new().white()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let inner_area = block.inner(area);
|
||||||
|
let content_width = inner_area.width as usize;
|
||||||
|
let content_height = inner_area.height as usize;
|
||||||
|
match &self.buffer {
|
||||||
|
RenderBuffer::String(s) => {
|
||||||
|
let wrapped_lines = s
|
||||||
|
.lines()
|
||||||
|
.map(|line| {
|
||||||
|
if line.is_empty() {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
(line.len() + content_width - 1) / {
|
||||||
|
content_width + (content_width == 0) as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sum::<usize>();
|
||||||
|
|
||||||
self.max_scroll = if wrapped_lines > content_height {
|
self.max_scroll = if wrapped_lines > content_height {
|
||||||
(wrapped_lines - content_height) as i16
|
(wrapped_lines - content_height) as i16
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
let scroll_offset = self.max_scroll.saturating_sub(self.scroll) as u16;
|
let scroll_offset = self.max_scroll.saturating_sub(self.scroll) as u16;
|
||||||
|
|
||||||
Paragraph::new(s.clone())
|
Paragraph::new(s.clone())
|
||||||
.wrap(Wrap::default())
|
.wrap(Wrap::default())
|
||||||
.left_aligned()
|
.left_aligned()
|
||||||
.block(block)
|
.block(block)
|
||||||
.scroll((scroll_offset as u16, 0))
|
.scroll((scroll_offset as u16, 0))
|
||||||
.render(area, buf);
|
.render(area, buf);
|
||||||
}
|
}
|
||||||
RenderBuffer::List { list, .. } => {
|
RenderBuffer::List { list, .. } => {
|
||||||
let list_w = List::new(list
|
let list_w =
|
||||||
.iter()
|
List::new(list.iter().map(|s| {
|
||||||
.map(|s| {
|
format!("> {}", textwrap::fill(s, content_width.saturating_sub(2)))
|
||||||
format!("> {}", textwrap::fill(s, content_width.saturating_sub(2)))
|
}))
|
||||||
})).block(block);
|
.block(block);
|
||||||
Widget::render(list_w, area, buf);
|
Widget::render(list_w, area, buf);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum RenderCommand {
|
pub enum RenderCommand {
|
||||||
RenderStringToPane{
|
RenderStringToPane {
|
||||||
str: String,
|
str: String,
|
||||||
pane: RenderPane
|
pane: RenderPane,
|
||||||
},
|
},
|
||||||
RenderInput(KeyCode),
|
RenderInput(KeyCode),
|
||||||
ListMove{
|
ListMove {
|
||||||
pane: RenderPane,
|
pane: RenderPane,
|
||||||
index: usize
|
index: usize,
|
||||||
},
|
},
|
||||||
ChangeLayout(RenderLayoutKind),
|
ChangeLayout(RenderLayoutKind),
|
||||||
ClearPane(RenderPane),
|
ClearPane(RenderPane),
|
||||||
|
|
||||||
|
/// Mouse Events
|
||||||
/// Mouse Events
|
MouseClickLeft(u16, u16),
|
||||||
MouseClickLeft(u16, u16),
|
MouseScrollUp,
|
||||||
MouseScrollUp,
|
MouseScrollDown,
|
||||||
MouseScrollDown,
|
Exit,
|
||||||
Exit,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, clap::ValueEnum)]
|
#[derive(Debug, Clone, clap::ValueEnum)]
|
||||||
pub enum RenderLayoutKind {
|
pub enum RenderLayoutKind {
|
||||||
#[value(name = "horizontal", aliases = ["h"])]
|
#[value(name = "horizontal", aliases = ["h"])]
|
||||||
CliHorizontal,
|
CliHorizontal,
|
||||||
#[value(name = "vertical", aliases = ["v"])]
|
#[value(name = "vertical", aliases = ["v"])]
|
||||||
CliVertical
|
CliVertical,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RenderLayout {
|
pub struct RenderLayout {
|
||||||
kind: RenderLayoutKind,
|
kind: RenderLayoutKind,
|
||||||
panes: Vec<Pane>,
|
panes: Vec<Pane>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderLayoutKind {
|
impl RenderLayoutKind {
|
||||||
pub fn rects(&self, area: Rect) -> std::rc::Rc<[Rect]> {
|
pub fn rects(&self, area: Rect) -> std::rc::Rc<[Rect]> {
|
||||||
match self {
|
match self {
|
||||||
Self::CliHorizontal => {
|
Self::CliHorizontal => Layout::default()
|
||||||
Layout::default()
|
.direction(Direction::Vertical)
|
||||||
.direction(Direction::Vertical)
|
.constraints(vec![Constraint::Percentage(70), Constraint::Percentage(30)])
|
||||||
.constraints(vec![
|
.split(area),
|
||||||
Constraint::Percentage(70),
|
Self::CliVertical => Layout::default()
|
||||||
Constraint::Percentage(30),
|
.direction(Direction::Horizontal)
|
||||||
])
|
.constraints(vec![Constraint::Percentage(30), Constraint::Percentage(70)])
|
||||||
.split(area)
|
.split(area),
|
||||||
}
|
}
|
||||||
Self::CliVertical => {
|
|
||||||
Layout::default()
|
|
||||||
.direction(Direction::Horizontal)
|
|
||||||
.constraints(vec![
|
|
||||||
Constraint::Percentage(30),
|
|
||||||
Constraint::Percentage(70),
|
|
||||||
])
|
|
||||||
.split(area)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate(&self) -> RenderLayout {
|
pub fn generate(&self) -> RenderLayout {
|
||||||
match self {
|
match self {
|
||||||
RenderLayoutKind::CliVertical => {
|
RenderLayoutKind::CliVertical => RenderLayout {
|
||||||
RenderLayout {
|
kind: self.clone(),
|
||||||
kind: self.clone(),
|
panes: vec![
|
||||||
panes: vec![
|
Pane {
|
||||||
Pane {
|
title: Some(" Input Pane ".to_string()),
|
||||||
title: Some(" Input Pane ".to_string()),
|
target: RenderPane::CliInput,
|
||||||
target: RenderPane::CliInput,
|
buffer: RenderBuffer::List {
|
||||||
buffer: RenderBuffer::List{
|
list: vec![String::new()],
|
||||||
list: vec![String::new()],
|
index: 0,
|
||||||
index: 0
|
},
|
||||||
},
|
focused: true,
|
||||||
focused: true,
|
scroll: 0,
|
||||||
scroll: 0,
|
max_scroll: 0,
|
||||||
max_scroll: 0,
|
},
|
||||||
|
Pane {
|
||||||
|
title: Some(" Output Pane ".to_string()),
|
||||||
|
target: RenderPane::CliOutput,
|
||||||
|
buffer: RenderBuffer::String(String::new()),
|
||||||
|
focused: false,
|
||||||
|
scroll: 0,
|
||||||
|
max_scroll: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
Pane {
|
RenderLayoutKind::CliHorizontal => RenderLayout {
|
||||||
title: Some(" Output Pane ".to_string()),
|
kind: self.clone(),
|
||||||
target: RenderPane::CliOutput,
|
panes: vec![
|
||||||
buffer: RenderBuffer::String(String::new()),
|
Pane {
|
||||||
focused: false,
|
title: Some(" Output Pane ".to_string()),
|
||||||
scroll: 0,
|
target: RenderPane::CliOutput,
|
||||||
max_scroll: 0,
|
buffer: RenderBuffer::String(String::new()),
|
||||||
|
focused: false,
|
||||||
|
scroll: 0,
|
||||||
|
max_scroll: 0,
|
||||||
|
},
|
||||||
|
Pane {
|
||||||
|
title: Some(" Input Pane ".to_string()),
|
||||||
|
target: RenderPane::CliInput,
|
||||||
|
buffer: RenderBuffer::List {
|
||||||
|
list: vec![String::new()],
|
||||||
|
index: 0,
|
||||||
|
},
|
||||||
|
focused: true,
|
||||||
|
scroll: 0,
|
||||||
|
max_scroll: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
RenderLayoutKind::CliHorizontal => {
|
|
||||||
RenderLayout {
|
|
||||||
kind: self.clone(),
|
|
||||||
panes: vec![
|
|
||||||
Pane {
|
|
||||||
title: Some(" Output Pane ".to_string()),
|
|
||||||
target: RenderPane::CliOutput,
|
|
||||||
buffer: RenderBuffer::String(String::new()),
|
|
||||||
focused: false,
|
|
||||||
scroll: 0,
|
|
||||||
max_scroll: 0,
|
|
||||||
},
|
|
||||||
Pane {
|
|
||||||
title: Some(" Input Pane ".to_string()),
|
|
||||||
target: RenderPane::CliInput,
|
|
||||||
buffer: RenderBuffer::List{
|
|
||||||
list: vec![String::new()],
|
|
||||||
index: 0
|
|
||||||
},
|
|
||||||
focused: true,
|
|
||||||
scroll: 0,
|
|
||||||
max_scroll: 0,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
impl Renderer {
|
impl Renderer {
|
||||||
pub fn new(layout: RenderLayoutKind) -> Self {
|
pub fn new(layout: RenderLayoutKind) -> Self {
|
||||||
Self {
|
Self {
|
||||||
buffer: String::new(),
|
buffer: String::new(),
|
||||||
exit: false,
|
exit: false,
|
||||||
layout: layout.generate(),
|
layout: layout.generate(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run(&mut self) -> io::Result<()> {
|
pub async fn run(&mut self) -> io::Result<()> {
|
||||||
self.log(msg!(INFO, "Started Renderer"));
|
self.log(msg!(INFO, "Started Renderer"));
|
||||||
let mut rx = subscribe_render_event();
|
let mut rx = subscribe_render_event();
|
||||||
let mut terminal = ratatui::init();
|
let mut terminal = ratatui::init();
|
||||||
publish_system_event(SystemEvent::RendererStarted);
|
publish_system_event(SystemEvent::RendererStarted);
|
||||||
|
|
||||||
let mut render_interval = interval(Duration::from_millis(32)); // 60 FPS
|
let mut render_interval = interval(Duration::from_millis(32)); // 60 FPS
|
||||||
|
|
||||||
while !self.exit {
|
while !self.exit {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
_ = render_interval.tick() => {
|
_ = render_interval.tick() => {
|
||||||
terminal.draw(|frame| self.draw(frame))?;
|
terminal.draw(|frame| self.draw(frame))?;
|
||||||
|
}
|
||||||
|
mes = rx.recv() => {
|
||||||
|
if let Ok(mes) = mes {
|
||||||
|
let frame = terminal.get_frame();
|
||||||
|
let rects = self.layout.kind.rects(frame.area());
|
||||||
|
self.apply(mes, rects);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mes = rx.recv() => {
|
}
|
||||||
if let Ok(mes) = mes {
|
ratatui::restore();
|
||||||
let frame = terminal.get_frame();
|
Ok(())
|
||||||
let rects = self.layout.kind.rects(frame.area());
|
}
|
||||||
self.apply(mes, rects);
|
|
||||||
|
fn log(&mut self, msg: String) {
|
||||||
|
self.buffer.push_str(&msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(&mut self, frame: &mut Frame) {
|
||||||
|
frame.render_widget(self, frame.area());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit(&mut self) {
|
||||||
|
log!(DEBUG, "Renderer Exit");
|
||||||
|
self.exit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn buffer_extend<S: AsRef<str>>(&mut self, input: S) {
|
||||||
|
self.buffer.push_str(input.as_ref());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn input_pane(&mut self) -> Option<&mut Pane> {
|
||||||
|
self.layout
|
||||||
|
.panes
|
||||||
|
.iter_mut()
|
||||||
|
.find(|p| p.target == RenderPane::CliInput)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn focused(&mut self) -> Option<&mut Pane> {
|
||||||
|
self.layout.panes.iter_mut().find(|p| p.focused == true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_mouse_click_left(&mut self, x: u16, y: u16, rects: std::rc::Rc<[Rect]>) {
|
||||||
|
for (i, r) in rects.iter().enumerate() {
|
||||||
|
if r.contains(layout::Position { x, y }) {
|
||||||
|
self.layout.panes[i].focused = true;
|
||||||
|
} else {
|
||||||
|
self.layout.panes[i].focused = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply(&mut self, mes: RenderCommand, rects: std::rc::Rc<[Rect]>) {
|
||||||
|
match mes {
|
||||||
|
RenderCommand::MouseClickLeft(x, y) => {
|
||||||
|
self.handle_mouse_click_left(x, y, rects);
|
||||||
|
}
|
||||||
|
RenderCommand::MouseScrollUp => {
|
||||||
|
if let Some(p) = self.focused() {
|
||||||
|
if p.scroll < p.max_scroll {
|
||||||
|
p.scroll += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RenderCommand::MouseScrollDown => {
|
||||||
|
if let Some(p) = self.focused() {
|
||||||
|
if p.scroll > i16::MIN {
|
||||||
|
p.scroll -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RenderCommand::RenderInput(k) => {
|
||||||
|
if let Some(p) = self.layout.panes.iter_mut().find(|p| p.focused) {
|
||||||
|
match k {
|
||||||
|
KeyCode::Char(c) => {
|
||||||
|
if let RenderBuffer::List { list, index } = &mut p.buffer {
|
||||||
|
list[*index].push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyCode::Backspace => {
|
||||||
|
if let RenderBuffer::List { list, index } = &mut p.buffer {
|
||||||
|
list[*index].pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyCode::Enter => {
|
||||||
|
if let RenderBuffer::List { list, index } = &mut p.buffer {
|
||||||
|
list.push(String::new());
|
||||||
|
*index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderCommand::ListMove { pane, index } => {
|
||||||
|
if let Some(p) = self.layout.panes.iter_mut().find(|p| p.target == pane) {
|
||||||
|
if let RenderBuffer::List { list, index: idx } = &mut p.buffer {
|
||||||
|
if index > 0 && index < list.len() {
|
||||||
|
list[*idx] = list[index].clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderCommand::RenderStringToPane { str, pane } => {
|
||||||
|
if let Some(p) = self.layout.panes.iter_mut().find(|p| p.target == pane) {
|
||||||
|
match &mut p.buffer {
|
||||||
|
RenderBuffer::List { list, index } => {
|
||||||
|
list.push(str);
|
||||||
|
*index += 1;
|
||||||
|
}
|
||||||
|
RenderBuffer::String(s) => s.push_str(&str),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RenderCommand::Exit => {
|
||||||
|
self.exit();
|
||||||
|
}
|
||||||
|
RenderCommand::ChangeLayout(l) => {
|
||||||
|
self.layout = l.generate();
|
||||||
|
}
|
||||||
|
RenderCommand::ClearPane(pane) => {
|
||||||
|
if matches!(pane, RenderPane::All) {
|
||||||
|
for p in self.layout.panes.iter_mut() {
|
||||||
|
match &mut p.buffer {
|
||||||
|
RenderBuffer::List { list, index } => {
|
||||||
|
list.clear();
|
||||||
|
*index = 0;
|
||||||
|
list.push(String::new());
|
||||||
|
}
|
||||||
|
RenderBuffer::String(s) => s.clear(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Some(p) = self.layout.panes.iter_mut().find(|p| p.target == pane) {
|
||||||
|
match &mut p.buffer {
|
||||||
|
RenderBuffer::List { list, index } => {
|
||||||
|
list.clear();
|
||||||
|
*index = 0;
|
||||||
|
list.push(String::new());
|
||||||
|
}
|
||||||
|
RenderBuffer::String(s) => s.clear(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ratatui::restore();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn log(&mut self, msg: String) {
|
async fn listen(
|
||||||
self.buffer.push_str(&msg)
|
&mut self,
|
||||||
}
|
rx: &mut tokio::sync::broadcast::Receiver<RenderCommand>,
|
||||||
|
) -> Result<RenderCommand, ()> {
|
||||||
pub fn draw(&mut self, frame: &mut Frame) {
|
if let Ok(Ok(mes)) = timeout(Duration::from_millis(400), rx.recv()).await {
|
||||||
frame.render_widget(self, frame.area());
|
return Ok(mes);
|
||||||
}
|
}
|
||||||
|
Err(())
|
||||||
fn exit(&mut self) {
|
|
||||||
log!(DEBUG, "Renderer Exit");
|
|
||||||
self.exit = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn buffer_extend<S: AsRef<str>>(&mut self, input: S) {
|
|
||||||
self.buffer.push_str(input.as_ref());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn input_pane(&mut self) -> Option<&mut Pane> {
|
|
||||||
self.layout.panes.iter_mut().find(|p| p.target == RenderPane::CliInput)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn focused(&mut self) -> Option<&mut Pane> {
|
|
||||||
self.layout.panes.iter_mut().find(|p| p.focused == true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_mouse_click_left(&mut self, x: u16, y: u16, rects: std::rc::Rc<[Rect]>) {
|
|
||||||
for (i, r) in rects.iter().enumerate() {
|
|
||||||
if r.contains(layout::Position{x, y}) {
|
|
||||||
self.layout.panes[i].focused = true;
|
|
||||||
} else {
|
|
||||||
self.layout.panes[i].focused = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn apply(&mut self, mes: RenderCommand, rects: std::rc::Rc<[Rect]>) {
|
|
||||||
match mes {
|
|
||||||
RenderCommand::MouseClickLeft(x, y) => {
|
|
||||||
self.handle_mouse_click_left(x, y, rects);
|
|
||||||
}
|
|
||||||
RenderCommand::MouseScrollUp => {
|
|
||||||
if let Some(p) = self.focused() {
|
|
||||||
if p.scroll < p.max_scroll {
|
|
||||||
p.scroll += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RenderCommand::MouseScrollDown => {
|
|
||||||
if let Some(p) = self.focused() {
|
|
||||||
if p.scroll > i16::MIN {
|
|
||||||
p.scroll -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RenderCommand::RenderInput(k) => {
|
|
||||||
if let Some(p) = self.layout.panes.iter_mut().find(|p| p.focused) {
|
|
||||||
match k {
|
|
||||||
KeyCode::Char(c) => {
|
|
||||||
if let RenderBuffer::List{list, index} = &mut p.buffer {
|
|
||||||
list[*index].push(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
KeyCode::Backspace => {
|
|
||||||
if let RenderBuffer::List{list, index} = &mut p.buffer {
|
|
||||||
list[*index].pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
KeyCode::Enter => {
|
|
||||||
if let RenderBuffer::List{list, index} = &mut p.buffer {
|
|
||||||
list.push(String::new());
|
|
||||||
*index += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
RenderCommand::ListMove{ pane, index } => {
|
|
||||||
if let Some(p) = self.layout.panes.iter_mut().find(|p| p.target == pane) {
|
|
||||||
if let RenderBuffer::List { list, index: idx } = &mut p.buffer {
|
|
||||||
if index > 0 && index < list.len() {
|
|
||||||
list[*idx] = list[index].clone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderCommand::RenderStringToPane{ str, pane } => {
|
|
||||||
if let Some(p) = self.layout.panes.iter_mut().find(|p| p.target == pane) {
|
|
||||||
match &mut p.buffer {
|
|
||||||
RenderBuffer::List { list, index } => {
|
|
||||||
list.push(str);
|
|
||||||
*index += 1;
|
|
||||||
}
|
|
||||||
RenderBuffer::String(s) => {
|
|
||||||
s.push_str(&str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RenderCommand::Exit => {
|
|
||||||
self.exit();
|
|
||||||
}
|
|
||||||
RenderCommand::ChangeLayout(l) => {
|
|
||||||
self.layout = l.generate();
|
|
||||||
}
|
|
||||||
RenderCommand::ClearPane(pane) => {
|
|
||||||
if matches!(pane, RenderPane::All) {
|
|
||||||
for p in self.layout.panes.iter_mut() {
|
|
||||||
match &mut p.buffer {
|
|
||||||
RenderBuffer::List { list, index } => {
|
|
||||||
list.clear();
|
|
||||||
*index = 0;
|
|
||||||
list.push(String::new());
|
|
||||||
}
|
|
||||||
RenderBuffer::String(s) => s.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if let Some(p) = self.layout.panes.iter_mut().find(|p| p.target == pane) {
|
|
||||||
match &mut p.buffer {
|
|
||||||
RenderBuffer::List { list, index } => {
|
|
||||||
list.clear();
|
|
||||||
*index = 0;
|
|
||||||
list.push(String::new());
|
|
||||||
}
|
|
||||||
RenderBuffer::String(s) => s.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn listen(&mut self, rx: &mut tokio::sync::broadcast::Receiver<RenderCommand>) -> Result<RenderCommand, ()> {
|
|
||||||
if let Ok(Ok(mes)) = timeout(Duration::from_millis(400), rx.recv()).await {
|
|
||||||
return Ok(mes);
|
|
||||||
}
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for &mut Renderer {
|
impl Widget for &mut Renderer {
|
||||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
|
let rects = self.layout.kind.rects(area);
|
||||||
let rects = self.layout.kind.rects(area);
|
for (i, p) in self.layout.panes.iter_mut().enumerate() {
|
||||||
for (i, p) in self.layout.panes.iter_mut().enumerate() {
|
p.render(rects[i], buf)
|
||||||
p.render(rects[i], buf)
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,316 +1,346 @@
|
|||||||
use crossterm::event::{self, Event, KeyCode, KeyEventKind, MouseButton, MouseEventKind};
|
use crossterm::event::{self, Event, KeyCode, KeyEventKind, MouseButton, MouseEventKind};
|
||||||
use memory_stats::memory_stats;
|
use memory_stats::memory_stats;
|
||||||
|
use std::{
|
||||||
|
io::{self, Write},
|
||||||
|
net::SocketAddr,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use std::{io::{self, Write}, net::SocketAddr, time::Duration};
|
|
||||||
|
|
||||||
use crate::{error::print_error_chain, event_bus::{subscribe_system_event, NetworkEvent, SystemEvent}, native_node::node::{NativeNode, NodeCommand}};
|
use crate::{
|
||||||
|
error::print_error_chain,
|
||||||
|
bus::{NetworkEvent, SystemEvent, subscribe_system_event},
|
||||||
|
node::node::{Node, NodeCommand},
|
||||||
|
};
|
||||||
use vlogger::*;
|
use vlogger::*;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
use crate::bus::{ publish_render_event };
|
||||||
use crate::log;
|
use crate::log;
|
||||||
use crate::event_bus::publish_render_event;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct Watcher {
|
pub struct Watcher {
|
||||||
parser_tx: mpsc::Sender<ParserCommand>,
|
parser_tx: mpsc::Sender<ParserCommand>,
|
||||||
node_tx: mpsc::Sender<NodeCommand>,
|
node_tx: mpsc::Sender<NodeCommand>,
|
||||||
exec_tx: mpsc::Sender<ExecutorCommand>,
|
exec_tx: mpsc::Sender<ExecutorCommand>,
|
||||||
cmd_buffer: String,
|
cmd_buffer: String,
|
||||||
cmd_history: Vec<String>,
|
cmd_history: Vec<String>,
|
||||||
history_index: usize,
|
history_index: usize,
|
||||||
handles: Vec<tokio::task::JoinHandle<()>>
|
handles: Vec<tokio::task::JoinHandle<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Watcher {
|
impl Watcher {
|
||||||
pub fn build() -> WatcherBuilder {
|
pub fn build() -> WatcherBuilder {
|
||||||
WatcherBuilder::new()
|
WatcherBuilder::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parser_tx(&self) -> mpsc::Sender<ParserCommand> {
|
pub fn parser_tx(&self) -> mpsc::Sender<ParserCommand> {
|
||||||
self.parser_tx.clone()
|
self.parser_tx.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exec_tx(&self) -> mpsc::Sender<ExecutorCommand> {
|
pub fn exec_tx(&self) -> mpsc::Sender<ExecutorCommand> {
|
||||||
self.exec_tx.clone()
|
self.exec_tx.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn exit(self) {
|
pub async fn exit(self) {
|
||||||
let rd_mes = RenderCommand::Exit;
|
let rd_mes = RenderCommand::Exit;
|
||||||
let pr_mes = ParserCommand::Exit;
|
let pr_mes = ParserCommand::Exit;
|
||||||
let exec_mes = ExecutorCommand::Exit;
|
let exec_mes = ExecutorCommand::Exit;
|
||||||
let node_mes = NodeCommand::Exit;
|
let node_mes = NodeCommand::Exit;
|
||||||
publish_render_event(rd_mes);
|
publish_render_event(rd_mes);
|
||||||
let _ = self.parser_tx.send(pr_mes).await;
|
let _ = self.parser_tx.send(pr_mes).await;
|
||||||
let _ = self.exec_tx.send(exec_mes).await;
|
let _ = self.exec_tx.send(exec_mes).await;
|
||||||
let _ = self.node_tx.send(node_mes).await;
|
let _ = self.node_tx.send(node_mes).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn log_memory() {
|
pub async fn log_memory() {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let id = format!("{}_{}", current_timestamp(), std::process::id());
|
let id = format!("{}_{}", current_timestamp(), std::process::id());
|
||||||
let mut path = std::path::PathBuf::new();
|
let mut path = std::path::PathBuf::new();
|
||||||
path.push("./proc/");
|
path.push("./proc/");
|
||||||
path.push(id);
|
path.push(id);
|
||||||
let mut mem_map = std::fs::OpenOptions::new().create(true).write(true).truncate(true).open(path).unwrap();
|
let mut mem_map = std::fs::OpenOptions::new()
|
||||||
loop {
|
.create(true)
|
||||||
let _ = tokio::time::sleep(Duration::from_secs(30)).await;
|
.write(true)
|
||||||
if let Some(usage) = memory_stats() {
|
.truncate(true)
|
||||||
let current = current_timestamp();
|
.open(path)
|
||||||
let _ = mem_map.write_all(msg!(INFO, "{}: Physical memory usage: {} MB", current, usage.physical_mem / 1024 / 1024).as_bytes());
|
.unwrap();
|
||||||
let _ = mem_map.write_all(msg!(INFO, "{}: Virtual memory usage: {} MB", current, usage.virtual_mem / 1024 / 1024).as_bytes());
|
loop {
|
||||||
}
|
let _ = tokio::time::sleep(Duration::from_secs(10)).await;
|
||||||
}
|
if let Some(usage) = memory_stats() {
|
||||||
});
|
let current = current_timestamp();
|
||||||
}
|
let _ = mem_map.write_all(
|
||||||
|
msg!(
|
||||||
|
INFO,
|
||||||
pub async fn poll(&mut self) -> io::Result<bool> {
|
"{}: Physical memory usage: {} MB",
|
||||||
match event::read()? {
|
current,
|
||||||
Event::Mouse(event) => {
|
usage.physical_mem / 1024 / 1024
|
||||||
match event.kind {
|
)
|
||||||
MouseEventKind::ScrollUp => { publish_render_event(RenderCommand::MouseScrollUp); }
|
.as_bytes(),
|
||||||
MouseEventKind::ScrollDown => { publish_render_event(RenderCommand::MouseScrollDown); }
|
);
|
||||||
MouseEventKind::Down(b) => {
|
let _ = mem_map.write_all(
|
||||||
match b {
|
msg!(
|
||||||
MouseButton::Left => {
|
INFO,
|
||||||
publish_render_event(RenderCommand::MouseClickLeft(event.column, event.row));
|
"{}: Virtual memory usage: {} MB",
|
||||||
}
|
current,
|
||||||
_ => {}
|
usage.virtual_mem / 1024 / 1024
|
||||||
}
|
)
|
||||||
}
|
.as_bytes(),
|
||||||
_ => {}
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::Key(k) if k.kind == KeyEventKind::Press => {
|
});
|
||||||
match k.code {
|
}
|
||||||
KeyCode::Char(c) => {
|
|
||||||
self.cmd_buffer.push(c);
|
pub async fn poll(&mut self) -> io::Result<bool> {
|
||||||
let message = RenderCommand::RenderInput(k.code);
|
match event::read()? {
|
||||||
publish_render_event(message);
|
Event::Mouse(event) => match event.kind {
|
||||||
}
|
MouseEventKind::ScrollUp => {
|
||||||
KeyCode::Backspace => {
|
publish_render_event(RenderCommand::MouseScrollUp);
|
||||||
self.cmd_buffer.pop();
|
}
|
||||||
let message = RenderCommand::RenderInput(k.code);
|
MouseEventKind::ScrollDown => {
|
||||||
publish_render_event(message);
|
publish_render_event(RenderCommand::MouseScrollDown);
|
||||||
},
|
}
|
||||||
KeyCode::Enter => {
|
MouseEventKind::Down(b) => match b {
|
||||||
let rd_mes = RenderCommand::RenderInput(k.code);
|
MouseButton::Left => {
|
||||||
let pr_mes = ParserCommand::ParseCmdString(self.cmd_buffer.clone());
|
publish_render_event(RenderCommand::MouseClickLeft(
|
||||||
let _ = self.parser_tx.send(pr_mes).await;
|
event.column,
|
||||||
publish_render_event(rd_mes);
|
event.row,
|
||||||
self.cmd_buffer.clear();
|
));
|
||||||
}
|
}
|
||||||
KeyCode::Up => {
|
_ => {}
|
||||||
if self.history_index > 0 {
|
},
|
||||||
self.history_index -= 1;
|
_ => {}
|
||||||
let rd_mes = RenderCommand::ListMove{
|
},
|
||||||
pane: RenderPane::CliInput,
|
Event::Key(k) if k.kind == KeyEventKind::Press => {
|
||||||
index: self.history_index,
|
match k.code {
|
||||||
};
|
KeyCode::Char(c) => {
|
||||||
publish_render_event(rd_mes);
|
self.cmd_buffer.push(c);
|
||||||
}
|
let message = RenderCommand::RenderInput(k.code);
|
||||||
}
|
publish_render_event(message);
|
||||||
KeyCode::Down => {
|
}
|
||||||
if self.history_index < self.cmd_buffer.len() {
|
KeyCode::Backspace => {
|
||||||
self.history_index += 1;
|
self.cmd_buffer.pop();
|
||||||
let rd_mes = RenderCommand::ListMove{
|
let message = RenderCommand::RenderInput(k.code);
|
||||||
pane: RenderPane::CliInput,
|
publish_render_event(message);
|
||||||
index: self.history_index,
|
}
|
||||||
};
|
KeyCode::Enter => {
|
||||||
publish_render_event(rd_mes);
|
let rd_mes = RenderCommand::RenderInput(k.code);
|
||||||
}
|
let pr_mes = ParserCommand::ParseCmdString(self.cmd_buffer.clone());
|
||||||
}
|
let _ = self.parser_tx.send(pr_mes).await;
|
||||||
KeyCode::Esc => {
|
publish_render_event(rd_mes);
|
||||||
return Ok(false);
|
self.cmd_buffer.clear();
|
||||||
}
|
}
|
||||||
_ => {}
|
KeyCode::Up => {
|
||||||
};
|
if self.history_index > 0 {
|
||||||
}
|
self.history_index -= 1;
|
||||||
_ => {}
|
let rd_mes = RenderCommand::ListMove {
|
||||||
|
pane: RenderPane::CliInput,
|
||||||
|
index: self.history_index,
|
||||||
|
};
|
||||||
|
publish_render_event(rd_mes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyCode::Down => {
|
||||||
|
if self.history_index < self.cmd_buffer.len() {
|
||||||
|
self.history_index += 1;
|
||||||
|
let rd_mes = RenderCommand::ListMove {
|
||||||
|
pane: RenderPane::CliInput,
|
||||||
|
index: self.history_index,
|
||||||
|
};
|
||||||
|
publish_render_event(rd_mes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyCode::Esc => {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
Ok(true)
|
||||||
}
|
}
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct WatcherBuilder {
|
pub struct WatcherBuilder {
|
||||||
addr: Option<SocketAddr>,
|
addr: Option<SocketAddr>,
|
||||||
seed_file: Option<String>,
|
database: Option<String>,
|
||||||
bootstrap: bool,
|
bootstrap: bool,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
seed: bool,
|
seed: bool,
|
||||||
render: bool,
|
render: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WatcherBuilder {
|
impl WatcherBuilder {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
|
||||||
|
|
||||||
pub fn addr(mut self, addr: Option<SocketAddr>) -> Self {
|
|
||||||
self.addr = addr;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn file(mut self, seed_file: Option<String>) -> Self {
|
|
||||||
self.seed_file = seed_file;
|
|
||||||
self.seed = true;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn debug(mut self, debug: bool) -> Self {
|
|
||||||
self.debug = debug;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bootstrap(mut self, bootstrap: bool) -> Self {
|
|
||||||
self.bootstrap = bootstrap;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render(mut self, render: bool) -> Self {
|
|
||||||
self.render = render;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn seed(mut self, seed: bool) -> Self {
|
|
||||||
self.seed = seed;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn start(mut self) -> Watcher {
|
|
||||||
let (parser_tx, parser_rx) = mpsc::channel::<ParserCommand>(100);
|
|
||||||
let (exec_tx, exec_rx) = mpsc::channel::<ExecutorCommand>(100);
|
|
||||||
let mut sys_event = subscribe_system_event();
|
|
||||||
|
|
||||||
if self.debug {
|
|
||||||
Watcher::log_memory().await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let render_handle = if self.render {
|
pub fn addr(mut self, addr: Option<SocketAddr>) -> Self {
|
||||||
Some(tokio::spawn({
|
self.addr = addr;
|
||||||
async move {
|
self
|
||||||
let _ = Renderer::new(RenderLayoutKind::CliHorizontal).run().await;
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
for i in 0..3 {
|
|
||||||
if let Ok(ev) = sys_event.recv().await {
|
|
||||||
match ev {
|
|
||||||
SystemEvent::RendererStarted => {
|
|
||||||
log(msg!(INFO, "Renderer Started"));
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
_ => { log(msg!(WARNING, "Wrong Event: {ev:?}! Retrying... (try {i})")) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn database(mut self, database: Option<String>) -> Self {
|
||||||
|
self.database = database;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
let parser_handle = tokio::spawn({
|
pub fn debug(mut self, debug: bool) -> Self {
|
||||||
let exec_tx = exec_tx.clone();
|
self.debug = debug;
|
||||||
async move {
|
self
|
||||||
let _ = Parser::new(parser_rx, exec_tx).run().await;
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
log(msg!(DEBUG, "Seed File: {:?}", self.seed_file));
|
pub fn bootstrap(mut self, bootstrap: bool) -> Self {
|
||||||
let blocks = self.seed_file
|
self.bootstrap = bootstrap;
|
||||||
.as_ref()
|
self
|
||||||
.and_then(|path| {
|
}
|
||||||
log(msg!(INFO, "Reading chain data from {path}"));
|
|
||||||
match std::fs::read_to_string(path) {
|
pub fn render(mut self, render: bool) -> Self {
|
||||||
Ok(s) => Some(s),
|
self.render = render;
|
||||||
Err(e) => {
|
self
|
||||||
print_error_chain(e.into());
|
}
|
||||||
|
|
||||||
|
pub fn seed(mut self, seed: bool) -> Self {
|
||||||
|
self.seed = seed;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn start(mut self) -> Watcher {
|
||||||
|
let (parser_tx, parser_rx) = mpsc::channel::<ParserCommand>(100);
|
||||||
|
let (exec_tx, exec_rx) = mpsc::channel::<ExecutorCommand>(100);
|
||||||
|
let mut sys_event = subscribe_system_event();
|
||||||
|
|
||||||
|
if self.debug {
|
||||||
|
Watcher::log_memory().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
let render_handle = if self.render {
|
||||||
|
Some(tokio::spawn({
|
||||||
|
async move {
|
||||||
|
let _ = Renderer::new(RenderLayoutKind::CliHorizontal).run().await;
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
None
|
None
|
||||||
},
|
};
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
if self.seed {
|
for i in 0..3 {
|
||||||
self.addr = Some(crate::seeds_constants::SEED_NODES[0]);
|
if let Ok(ev) = sys_event.recv().await {
|
||||||
}
|
match ev {
|
||||||
|
SystemEvent::RendererStarted => {
|
||||||
let mut node = NativeNode::new(self.addr.clone(), &blocks, exec_tx.clone()).await;
|
log(msg!(INFO, "Renderer Started"));
|
||||||
log(msg!(INFO, "Built Node"));
|
break;
|
||||||
|
}
|
||||||
let executor_handle = tokio::spawn({
|
_ => log(msg!(WARNING, "Wrong Event: {ev:?}! Retrying... (try {i})")),
|
||||||
let node_tx = node.tx();
|
}
|
||||||
async move {
|
|
||||||
let _ = Executor::new(node_tx, exec_rx).run().await;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
for i in 0..3 {
|
|
||||||
if let Ok(ev) = sys_event.recv().await {
|
|
||||||
match ev {
|
|
||||||
SystemEvent::ExecutorStarted => {
|
|
||||||
log(msg!(INFO, "Executor Started"));
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
_ => { log(msg!(WARNING, "Wrong Event: {ev:?}! Retrying... (try {i})")) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let node_tx = node.tx();
|
|
||||||
let node_handle = tokio::spawn({
|
|
||||||
async move {
|
|
||||||
node.run().await;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
for i in 0..3 {
|
|
||||||
if let Ok(ev) = sys_event.recv().await {
|
|
||||||
match ev {
|
|
||||||
SystemEvent::NodeStarted => {
|
|
||||||
log(msg!(INFO, "Executor Started"));
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
_ => { log(msg!(WARNING, "Wrong Event: {ev:?}! Retrying... (try {i})")) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.bootstrap {
|
|
||||||
let exec_tx = exec_tx.clone();
|
|
||||||
|
|
||||||
tokio::spawn(async move {
|
|
||||||
let seed_cmd = ExecutorCommand::Node(NodeCommand::ConnectToSeeds);
|
|
||||||
let mut ev_rx = crate::event_bus::subscribe_network_event();
|
|
||||||
let _ = exec_tx.send(seed_cmd).await;
|
|
||||||
|
|
||||||
while let Ok(e) = ev_rx.recv().await {
|
|
||||||
match e {
|
|
||||||
NetworkEvent::SeedConnected(_) => {
|
|
||||||
let bootstrap_cmd = ExecutorCommand::Node(NodeCommand::BootStrap);
|
|
||||||
let _ = exec_tx.send(bootstrap_cmd).await;
|
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
let parser_handle = tokio::spawn({
|
||||||
Watcher {
|
let exec_tx = exec_tx.clone();
|
||||||
node_tx,
|
async move {
|
||||||
cmd_history: Vec::new(),
|
let _ = Parser::new(parser_rx, exec_tx).run().await;
|
||||||
history_index: 0,
|
}
|
||||||
parser_tx,
|
});
|
||||||
exec_tx,
|
|
||||||
cmd_buffer: String::new(),
|
log(msg!(DEBUG, "Database Location: {:?}", self.database));
|
||||||
handles: {
|
let db = self
|
||||||
let mut h = vec![parser_handle, executor_handle, node_handle];
|
.database
|
||||||
if render_handle.is_some() {
|
.as_ref()
|
||||||
h.push(render_handle.unwrap());
|
.and_then(|path| {
|
||||||
|
log(msg!(INFO, "Reading chain data from {path}"));
|
||||||
|
match std::fs::read_to_string(path) {
|
||||||
|
Ok(s) => Some(s),
|
||||||
|
Err(e) => {
|
||||||
|
print_error_chain(e.into());
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
if self.seed {
|
||||||
|
self.addr = Some(crate::seeds_constants::SEED_NODES[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut node = Node::new(self.addr.clone(), &blocks, exec_tx.clone()).await;
|
||||||
|
log(msg!(INFO, "Built Node"));
|
||||||
|
|
||||||
|
let executor_handle = tokio::spawn({
|
||||||
|
let node_tx = node.tx();
|
||||||
|
async move {
|
||||||
|
let _ = Executor::new(node_tx, exec_rx).run().await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for i in 0..3 {
|
||||||
|
if let Ok(ev) = sys_event.recv().await {
|
||||||
|
match ev {
|
||||||
|
SystemEvent::ExecutorStarted => {
|
||||||
|
log(msg!(INFO, "Executor Started"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => log(msg!(WARNING, "Wrong Event: {ev:?}! Retrying... (try {i})")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let node_tx = node.tx();
|
||||||
|
let node_handle = tokio::spawn({
|
||||||
|
async move {
|
||||||
|
node.run().await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for i in 0..3 {
|
||||||
|
if let Ok(ev) = sys_event.recv().await {
|
||||||
|
match ev {
|
||||||
|
SystemEvent::NodeStarted => {
|
||||||
|
log(msg!(INFO, "Executor Started"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => log(msg!(WARNING, "Wrong Event: {ev:?}! Retrying... (try {i})")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.bootstrap {
|
||||||
|
let exec_tx = exec_tx.clone();
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let seed_cmd = ExecutorCommand::Node(NodeCommand::ConnectToSeeds);
|
||||||
|
let mut ev_rx = crate::bus::subscribe_network_event();
|
||||||
|
let _ = exec_tx.send(seed_cmd).await;
|
||||||
|
|
||||||
|
while let Ok(e) = ev_rx.recv().await {
|
||||||
|
match e {
|
||||||
|
NetworkEvent::SeedConnected(_) => {
|
||||||
|
let bootstrap_cmd = ExecutorCommand::Node(NodeCommand::BootStrap);
|
||||||
|
let _ = exec_tx.send(bootstrap_cmd).await;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Watcher {
|
||||||
|
node_tx,
|
||||||
|
cmd_history: Vec::new(),
|
||||||
|
history_index: 0,
|
||||||
|
parser_tx,
|
||||||
|
exec_tx,
|
||||||
|
cmd_buffer: String::new(),
|
||||||
|
handles: {
|
||||||
|
let mut h = vec![parser_handle, executor_handle, node_handle];
|
||||||
|
if render_handle.is_some() {
|
||||||
|
h.push(render_handle.unwrap());
|
||||||
|
}
|
||||||
|
h
|
||||||
|
},
|
||||||
}
|
}
|
||||||
h
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user