Merge branch 'staging' into lmdb_update
This commit is contained in:
@@ -1,6 +1,33 @@
|
||||
# Rust latest
|
||||
FROM mcr.microsoft.com/devcontainers/rust:latest
|
||||
|
||||
# Install Required Dependencies
|
||||
RUN apt-get -qq update
|
||||
RUN apt-get install -y build-essential cmake git libgit2-dev clang libncurses5-dev libncursesw5-dev zlib1g-dev pkg-config libssl-dev llvm
|
||||
# Rust latest
|
||||
FROM mcr.microsoft.com/devcontainers/rust:latest
|
||||
|
||||
# Install Required Dependencies
|
||||
RUN apt-get -qq update
|
||||
RUN apt-get install -y \
|
||||
build-essential \
|
||||
clang \
|
||||
cmake \
|
||||
fuse \
|
||||
gh \
|
||||
git \
|
||||
libgit2-dev \
|
||||
libncurses5-dev \
|
||||
libncursesw5-dev \
|
||||
libssl-dev \
|
||||
llvm \
|
||||
pkg-config \
|
||||
snapd \
|
||||
squashfuse \
|
||||
sudo \
|
||||
zlib1g-dev
|
||||
|
||||
RUN echo '#!/bin/bash\n\
|
||||
/usr/lib/snapd/snapd &\n\
|
||||
sleep 5\n\
|
||||
exec "$@"' > /start-snapd.sh && \
|
||||
chmod +x /start-snapd.sh
|
||||
|
||||
VOLUME ["/sys/fs/cgroup"]
|
||||
|
||||
ENTRYPOINT ["/start-snapd.sh"]
|
||||
CMD ["bash"]
|
||||
|
||||
@@ -16,5 +16,6 @@
|
||||
"github.copilot-chat"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"runArgs": ["--privileged", "-v", "/sys/fs/cgroup:/sys/fs/cgroup:ro", "-v", "/tmp:/tmp"]
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
name: Snap Package
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- staging
|
||||
|
||||
jobs:
|
||||
build-snap:
|
||||
name: Build snap
|
||||
strategy:
|
||||
matrix:
|
||||
runs-on: [ubuntu-latest, ubuntu-24.04-arm]
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Prepare snapcraft project
|
||||
run: |
|
||||
grade=devel
|
||||
if [ "${{ github.ref_name }}" = "master" ]; then
|
||||
grade=stable
|
||||
fi
|
||||
|
||||
mkdir -p snap
|
||||
sed "s/SNAP_GRADE/$grade/" .packaging/snaps/snapcraft.yaml > snap/snapcraft.yaml
|
||||
|
||||
- name: Build snap package
|
||||
id: build
|
||||
uses: snapcore/action-build@v1
|
||||
with:
|
||||
path: .
|
||||
|
||||
- name: Show snap package
|
||||
run: find . -maxdepth 1 -name "*.snap" -ls
|
||||
|
||||
- name: Upload snap artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: grin-snap
|
||||
path: "*.snap"
|
||||
if-no-files-found: error
|
||||
@@ -0,0 +1,40 @@
|
||||
name: grin
|
||||
adopt-info: grin
|
||||
summary: Minimal implementation of the Mimblewimble protocol
|
||||
description: |
|
||||
https://grin.mw/
|
||||
|
||||
confinement: strict
|
||||
grade: SNAP_GRADE
|
||||
base: core22
|
||||
architectures:
|
||||
- build-on: [arm64]
|
||||
build-for: [arm64]
|
||||
- build-on: [amd64]
|
||||
build-for: [amd64]
|
||||
|
||||
parts:
|
||||
grin:
|
||||
plugin: rust
|
||||
source: .
|
||||
override-pull: |
|
||||
craftctl default
|
||||
craftctl set version="$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n 1)"
|
||||
build-packages:
|
||||
- build-essential
|
||||
- clang
|
||||
- cmake
|
||||
- libncurses5-dev
|
||||
- libncursesw5-dev
|
||||
- libssl-dev
|
||||
- llvm
|
||||
- pkg-config
|
||||
- zlib1g-dev
|
||||
|
||||
apps:
|
||||
grin:
|
||||
environment:
|
||||
LC_ALL: C.UTF-8
|
||||
LANG: C.UTF-8
|
||||
command: bin/grin
|
||||
plugs: [network, network-bind]
|
||||
+41
-9
@@ -12,7 +12,36 @@ More documentation or updates/fixes to existing documentation are also very welc
|
||||
|
||||
We generally prefer you to PR your work earlier rather than later. This ensures everyone else has a better idea of what's being worked on, and can help reduce wasted effort. If work on your PR has just begun, please feel free to create the PR with [WIP] (work in progress) in the PR title, and let us know when it's ready for review in the comments.
|
||||
|
||||
Since mainnet has been released, the bar for having PRs accepted has been raised. Before submitting your PR for approval, please be ensure it:
|
||||
All changes should first be submitted against the `staging` branch and follow the PR rules below. The `staging` branch is tested by the community before changes are merged into `master` by an admin when appropriate.
|
||||
|
||||
```text
|
||||
Contributor branch
|
||||
+-- Pull request
|
||||
+-- staging branch
|
||||
+-- testing (community)
|
||||
+-- Admin merge
|
||||
+-- master
|
||||
```
|
||||
|
||||
## Creating a Pull Request
|
||||
|
||||
Create your work on a dedicated branch based on the latest `staging` branch.
|
||||
|
||||
```sh
|
||||
git checkout staging
|
||||
git pull
|
||||
git checkout -b my-change
|
||||
```
|
||||
|
||||
Fork the Grin repository and add your fork as a git remote.
|
||||
|
||||
```sh
|
||||
git remote add my-fork <your-fork-url>
|
||||
```
|
||||
|
||||
Push your branch to your fork and open a GitHub pull request from your fork's branch into the Grin repository's `staging` branch. You can do this through the GitHub web interface.
|
||||
|
||||
Since mainnet has been released, the bar for having PRs accepted has been raised. Before submitting your PR for approval, please ensure it:
|
||||
* Includes a proper description of what problems the PR addresses, as well as a detailed explanation as to what it changes
|
||||
* Explains whether/how the change is consensus breaking or breaks existing client functionality
|
||||
* Contains unit tests exercising new/changed functionality
|
||||
@@ -26,23 +55,26 @@ The development team will be happy to help and guide you with any of these point
|
||||
|
||||
# Find Us
|
||||
|
||||
When you are starting to contribute to grin, we really would appreciate if you come by the gitter chat channels.
|
||||
When you are starting to contribute to grin, we really would appreciate if you come by one of the community channels.
|
||||
|
||||
In case of problems with trying out grin, before starting to contribute, there's the [grincoin#support](https://keybase.io/team/grincoin) on Keybase. Write there about what you've done, what you want to do, and maybe paste logs through a text paste webservice.
|
||||
If you run into problems while trying out Grin before contributing, ask in [grincoin#support](https://keybase.io/team/grincoin) on Keybase. Describe what you've done, what you want to do, and include logs using a paste service if needed.
|
||||
|
||||
* Please [join the grincoin#general on Keybase](https://keybase.io/team/grincoin) to get a feeling for the community.
|
||||
* And see the developers chat channel [grincoin#dev on Keybase](https://keybase.io/team/grincoin) if you have questions about source code files.
|
||||
* Join the development discussion on [Telegram](https://t.me/grindevelopment).
|
||||
* [Grin Governance Council Public](https://t.me/Grin_Governance) channel where biweekly meetings are held for governance and development issues.
|
||||
* [Grin General Telegram channel](https://t.me/grinprivacy/1) for general community discussion.
|
||||
* Please [join grincoin#general on Keybase](https://keybase.io/team/grincoin) to get a feeling for the community.
|
||||
* See the developers' chat channel [grincoin#dev on Keybase](https://keybase.io/team/grincoin) if you have questions about source code files.
|
||||
If you explain what you're looking at and what you want to do, we'll try to help you along the way.
|
||||
* See `docs/*.md` and the folder structure explanations, [the wiki](https://github.com/mimblewimble/docs/wiki) and the official [Grin documentation](https://docs.grin.mw/).
|
||||
* Further information and discussions are in the [Forum](https://forum.grin.mw), the [website](https://grin.mw), the [mailing list](https://lists.launchpad.net/mimblewimble/) and news channels like the [Reddit/grincoin](https://www.reddit.com/r/grincoin/) and a (mostly unfiltered!) Twitter bot that collects headlines, mailing list posts, and reddit posts related to Mimblewimble/Grin: [@grinmw](https://twitter.com/grinmw)
|
||||
* Further information and discussions are in the [Forum](https://forum.grin.mw), the [website](https://grin.mw), the [mailing list](https://lists.launchpad.net/mimblewimble/), and news channels like [Reddit/grincoin](https://www.reddit.com/r/grincoin/).
|
||||
|
||||
## Testing
|
||||
|
||||
Run all tests with `cargo test --all` and please remember to test locally before creating a PR on github.
|
||||
Run all tests with `cargo test --all` and please remember to test locally before creating a PR against `staging`.
|
||||
|
||||
### Check Travis output
|
||||
### Check CI output
|
||||
|
||||
After creating a PR on github, the code will be tested automatically by Travis CI, and from the results you'll get a red or green light. The test can take a while, and you'll have a "yellow traffic light" on your PR until Travis CI is done testing.
|
||||
After creating a PR, the code will be tested automatically by CI. The test can take a while, and your PR may remain pending until CI is done testing.
|
||||
|
||||
### Building quality
|
||||
|
||||
|
||||
@@ -30,9 +30,8 @@ To get involved, read our [contributing docs](CONTRIBUTING.md).
|
||||
|
||||
Find us:
|
||||
|
||||
* Telegram: [Grin Development](https://t.me/grindevelopment)
|
||||
* Chat: [Keybase](https://keybase.io/team/grincoin), more instructions on how to join [here](https://grin.mw/community).
|
||||
* Mailing list: join the [~Mimblewimble team](https://launchpad.net/~mimblewimble) and subscribe on Launchpad.
|
||||
* Twitter for the Grin council: [@grincouncil](https://twitter.com/grincouncil)
|
||||
|
||||
## Getting Started
|
||||
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ use crate::{rest::*, BlockListing};
|
||||
use std::sync::Weak;
|
||||
|
||||
/// Main interface into all node API functions.
|
||||
/// Node APIs are split into two seperate blocks of functionality
|
||||
/// Node APIs are split into two separate blocks of functionality
|
||||
/// called the ['Owner'](struct.Owner.html) and ['Foreign'](struct.Foreign.html) APIs
|
||||
///
|
||||
/// Methods in this API are intended to be 'single use'.
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ use std::net::SocketAddr;
|
||||
use std::sync::Weak;
|
||||
|
||||
/// Main interface into all node API functions.
|
||||
/// Node APIs are split into two seperate blocks of functionality
|
||||
/// Node APIs are split into two separate blocks of functionality
|
||||
/// called the ['Owner'](struct.Owner.html) and ['Foreign'](struct.Foreign.html) APIs
|
||||
///
|
||||
/// Methods in this API are intended to be 'single use'.
|
||||
|
||||
@@ -262,8 +262,11 @@ fn comments() -> HashMap<String, String> {
|
||||
"host".to_string(),
|
||||
"
|
||||
#The interface on which to listen.
|
||||
#0.0.0.0 will listen on all interfaces, allowing others to interact
|
||||
#127.0.0.1 will listen on the local machine only
|
||||
#:: will listen on all IPv6 interfaces and may also accept IPv4 depending on OS socket settings
|
||||
#0.0.0.0 will listen on all IPv4 interfaces, allowing others to interact
|
||||
#Set host to 0.0.0.0 if only IPv4 listening is desired
|
||||
#127.0.0.1 will listen on the local machine only over IPv4
|
||||
#::1 will listen on the local machine only over IPv6
|
||||
"
|
||||
.to_string(),
|
||||
);
|
||||
|
||||
+3
-1
@@ -252,7 +252,9 @@ which can be signed by the attacker because Carol's blinding factor cancels out
|
||||
|
||||
This output (`(113 + 99)*G + 2*H`) requires that both the numbers 113 and 99 are known in order to be spent; the attacker
|
||||
would thus have successfully locked Carol's UTXO. The requirement for a range proof for the blinding factor prevents this
|
||||
because the attacker doesn't know the number 113 and thus neither (113 + 99). A more detailed description of range proofs is further detailed in the [range proof paper](https://eprint.iacr.org/2017/1066.pdf).
|
||||
because the attacker doesn't know the number 113 and thus neither (113 + 99). In other words, without knowing the private
|
||||
key (blinding factor), the attacker would not know the value in the output and would not be able to produce a valid range proof for it.
|
||||
A more detailed description of range proofs is further detailed in the [range proof paper](https://eprint.iacr.org/2017/1066.pdf).
|
||||
|
||||
#### Putting It All Together
|
||||
|
||||
|
||||
+2
-2
@@ -113,10 +113,10 @@ Now, (hopefully) armed with a basic understanding of what the Cuckoo Cycle algor
|
||||
|
||||
## Mining in Grin
|
||||
|
||||
The Cuckoo Cycle outlined above forms the basis of Grin's mining process, however Grin uses two variantion of Cuckoo Cycle in tandem with several other systems to create a Proof-of-Work.
|
||||
The Cuckoo Cycle outlined above forms the basis of Grin's mining process, however Grin uses two variations of Cuckoo Cycle in tandem with several other systems to create a Proof-of-Work.
|
||||
|
||||
1. for GPUs: Cuckaroo on 2^29 edges
|
||||
* Tweaked every 6 months to maitain ASIC resistance.
|
||||
* Tweaked every 6 months to maintain ASIC resistance.
|
||||
* 90% of rewards at launch, linearly decreasing to 0% in 2 years.
|
||||
* Variant of Cuckoo that enforces so-called ``mean'' mining.
|
||||
* Takes 5.5GB of memory (perhaps 4GB with slowdown).
|
||||
|
||||
+1
-1
@@ -293,7 +293,7 @@ pub struct P2PConfig {
|
||||
/// Default address for peer-to-peer connections.
|
||||
impl Default for P2PConfig {
|
||||
fn default() -> P2PConfig {
|
||||
let ipaddr = "0.0.0.0".parse().unwrap();
|
||||
let ipaddr = "::".parse().unwrap();
|
||||
P2PConfig {
|
||||
host: ipaddr,
|
||||
port: 3414,
|
||||
|
||||
@@ -19,7 +19,6 @@ use std::sync::Arc;
|
||||
use chrono::prelude::Utc;
|
||||
use rand::prelude::*;
|
||||
|
||||
use crate::api;
|
||||
use crate::chain;
|
||||
use crate::core::global::{ChainTypes, DEFAULT_FUTURE_TIME_LIMIT};
|
||||
use crate::core::{core, libtx, pow};
|
||||
@@ -28,6 +27,7 @@ use crate::p2p;
|
||||
use crate::pool;
|
||||
use crate::pool::types::DandelionConfig;
|
||||
use crate::store;
|
||||
use crate::{api, Server};
|
||||
|
||||
/// Error type wrapping underlying module errors.
|
||||
#[derive(Debug)]
|
||||
@@ -405,3 +405,17 @@ impl DandelionEpoch {
|
||||
self.relay_peer.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Server initialization status.
|
||||
pub enum ServerInitStatus {
|
||||
/// Database loading.
|
||||
LoadDatabase,
|
||||
/// P2P server initialization.
|
||||
StartSync,
|
||||
/// API server initialization.
|
||||
StartAPI,
|
||||
/// Server instance after successful initialization.
|
||||
FinishedLoading(Server),
|
||||
/// Error on initialization.
|
||||
ErrorLoading(Error),
|
||||
}
|
||||
|
||||
@@ -35,18 +35,20 @@ use crate::util::StopState;
|
||||
|
||||
/// DNS Seeds with contacts associated - Mainnet
|
||||
pub const MAINNET_DNS_SEEDS: &[&str] = &[
|
||||
"mainnet-seed.grinnode.live", // info@grinnode.live
|
||||
"grincoin.org", // xmpp:aglkm@conversations.im
|
||||
"main.gri.mw", // admin@gri.mw
|
||||
"mainnet.grinffindor.org", // support@grinffindor.org
|
||||
"main-seed.grin.money", // support@grinily.com
|
||||
"mainnet-seed.grinnode.live", // info@grinnode.live
|
||||
"grincoin.org", // xmpp:aglkm@conversations.im
|
||||
"main.gri.mw", // admin@gri.mw
|
||||
"mainnet.grinffindor.org", // support@grinffindor.org
|
||||
"main-seed.grin.money", // support@grinily.com
|
||||
"mainnet.fountainoffairfortune.it", // support@fountainoffairfortune.it
|
||||
];
|
||||
/// DNS Seeds with contacts associated - Testnet
|
||||
pub const TESTNET_DNS_SEEDS: &[&str] = &[
|
||||
"testnet.grincoin.org", // xmpp:aglkm@conversations.im
|
||||
"test.gri.mw", // admin@gri.mw
|
||||
"testnet.grinffindor.org", // support@grinffindor.org
|
||||
"test-seed.grin.money", // support@grinily.com
|
||||
"testnet.grincoin.org", // xmpp:aglkm@conversations.im
|
||||
"test.gri.mw", // admin@gri.mw
|
||||
"testnet.grinffindor.org", // support@grinffindor.org
|
||||
"test-seed.grin.money", // support@grinily.com
|
||||
"testnet.fountainoffairfortune.it", // support@fountainoffairfortune.it
|
||||
];
|
||||
|
||||
pub fn connect_and_monitor(
|
||||
|
||||
+19
-12
@@ -39,7 +39,7 @@ use crate::common::hooks::{init_chain_hooks, init_net_hooks};
|
||||
use crate::common::stats::{
|
||||
ChainStats, DiffBlock, DiffStats, PeerStats, ServerStateInfo, ServerStats, TxStats,
|
||||
};
|
||||
use crate::common::types::{Error, ServerConfig, StratumServerConfig};
|
||||
use crate::common::types::{Error, ServerConfig, ServerInitStatus, StratumServerConfig};
|
||||
use crate::core::core::hash::{Hashed, ZERO_HASH};
|
||||
use crate::core::ser::ProtocolVersion;
|
||||
use crate::core::{consensus, genesis, global, pow};
|
||||
@@ -52,7 +52,6 @@ use crate::pool;
|
||||
use crate::util::file::get_first_line;
|
||||
use crate::util::{RwLock, StopState};
|
||||
use futures::channel::oneshot;
|
||||
use grin_util::logger::LogEntry;
|
||||
|
||||
/// Arcified thread-safe TransactionPool with type parameters used by server components
|
||||
pub type ServerTxPool = Arc<RwLock<pool::TransactionPool<PoolToChainAdapter, PoolToNetAdapter>>>;
|
||||
@@ -84,20 +83,16 @@ impl Server {
|
||||
/// Instantiates and starts a new server. Optionally takes a callback
|
||||
/// for the server to send an ARC copy of itself, to allow another process
|
||||
/// to poll info about the server status
|
||||
pub fn start<F>(
|
||||
pub fn start(
|
||||
config: ServerConfig,
|
||||
logs_rx: Option<mpsc::Receiver<LogEntry>>,
|
||||
mut info_callback: F,
|
||||
stop_state: Option<Arc<StopState>>,
|
||||
server_tx: Option<mpsc::Sender<ServerInitStatus>>,
|
||||
api_chan: &'static mut (oneshot::Sender<()>, oneshot::Receiver<()>),
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
F: FnMut(Server, Option<mpsc::Receiver<LogEntry>>),
|
||||
{
|
||||
) -> Result<Server, Error> {
|
||||
let mining_config = config.stratum_mining_config.clone();
|
||||
let enable_test_miner = config.run_test_miner;
|
||||
let test_miner_wallet_url = config.test_miner_wallet_url.clone();
|
||||
let serv = Server::new(config, stop_state, api_chan)?;
|
||||
let serv = Server::new(config, stop_state, server_tx, api_chan)?;
|
||||
|
||||
if let Some(c) = mining_config {
|
||||
let enable_stratum_server = c.enable_stratum_server;
|
||||
@@ -118,8 +113,7 @@ impl Server {
|
||||
}
|
||||
}
|
||||
|
||||
info_callback(serv, logs_rx);
|
||||
Ok(())
|
||||
Ok(serv)
|
||||
}
|
||||
|
||||
// Exclusive (advisory) lock_file to ensure we do not run multiple
|
||||
@@ -151,6 +145,7 @@ impl Server {
|
||||
pub fn new(
|
||||
config: ServerConfig,
|
||||
stop_state: Option<Arc<StopState>>,
|
||||
server_tx: Option<mpsc::Sender<ServerInitStatus>>,
|
||||
api_chan: &'static mut (oneshot::Sender<()>, oneshot::Receiver<()>),
|
||||
) -> Result<Server, Error> {
|
||||
// Obtain our lock_file or fail immediately with an error.
|
||||
@@ -193,6 +188,10 @@ impl Server {
|
||||
|
||||
info!("Starting server, genesis block: {}", genesis.hash());
|
||||
|
||||
if let Some(ref server_tx) = server_tx {
|
||||
let _ = server_tx.send(ServerInitStatus::LoadDatabase);
|
||||
}
|
||||
|
||||
let shared_chain = Arc::new(chain::Chain::init(
|
||||
config.db_root.clone(),
|
||||
chain_adapter.clone(),
|
||||
@@ -220,6 +219,10 @@ impl Server {
|
||||
};
|
||||
debug!("Capabilities: {:?}", capabilities);
|
||||
|
||||
if let Some(ref server_tx) = server_tx {
|
||||
let _ = server_tx.send(ServerInitStatus::StartSync);
|
||||
}
|
||||
|
||||
let p2p_server = Arc::new(p2p::Server::new(
|
||||
&config.db_root,
|
||||
capabilities,
|
||||
@@ -265,6 +268,10 @@ impl Server {
|
||||
}
|
||||
})?;
|
||||
|
||||
if let Some(ref server_tx) = server_tx {
|
||||
let _ = server_tx.send(ServerInitStatus::StartAPI);
|
||||
}
|
||||
|
||||
info!("Starting rest apis at: {}", &config.api_http_addr);
|
||||
let api_secret = get_first_line(config.api_secret_path.clone());
|
||||
let foreign_api_secret = get_first_line(config.foreign_api_secret_path.clone());
|
||||
|
||||
+45
-28
@@ -28,7 +28,10 @@ use crate::tui::ui;
|
||||
use futures::channel::oneshot;
|
||||
use grin_p2p::msg::PeerAddrs;
|
||||
use grin_p2p::PeerAddr;
|
||||
use grin_servers::common::types::ServerInitStatus;
|
||||
use grin_servers::Server;
|
||||
use grin_util::logger::LogEntry;
|
||||
use grin_util::StopState;
|
||||
use std::sync::mpsc;
|
||||
|
||||
/// wrap below to allow UI to clean up on stop
|
||||
@@ -37,38 +40,50 @@ pub fn start_server(
|
||||
logs_rx: Option<mpsc::Receiver<LogEntry>>,
|
||||
api_chan: &'static mut (oneshot::Sender<()>, oneshot::Receiver<()>),
|
||||
) {
|
||||
start_server_tui(config, logs_rx, api_chan);
|
||||
exit(0);
|
||||
exit(start_server_tui(config, logs_rx, api_chan));
|
||||
}
|
||||
|
||||
fn start_server_tui(
|
||||
config: servers::ServerConfig,
|
||||
logs_rx: Option<mpsc::Receiver<LogEntry>>,
|
||||
api_chan: &'static mut (oneshot::Sender<()>, oneshot::Receiver<()>),
|
||||
) {
|
||||
// Run the UI controller.. here for now for simplicity to access
|
||||
// everything it might need
|
||||
) -> i32 {
|
||||
if config.run_tui.unwrap_or(false) {
|
||||
warn!("Starting GRIN in UI mode...");
|
||||
servers::Server::start(
|
||||
config,
|
||||
logs_rx,
|
||||
|serv: servers::Server, logs_rx: Option<mpsc::Receiver<LogEntry>>| {
|
||||
let mut controller = ui::Controller::new(logs_rx.unwrap()).unwrap_or_else(|e| {
|
||||
panic!("Error loading UI controller: {}", e);
|
||||
});
|
||||
controller.run(serv);
|
||||
},
|
||||
None,
|
||||
api_chan,
|
||||
)
|
||||
.unwrap();
|
||||
// Run the UI controller.
|
||||
let (serv_tx, serv_rx) = mpsc::channel::<ServerInitStatus>();
|
||||
let mut controller = ui::Controller::new(logs_rx, serv_rx).unwrap_or_else(|e| {
|
||||
panic!("Error loading UI controller: {}", e);
|
||||
});
|
||||
let serv_tx_clone = serv_tx.clone();
|
||||
let stop_state = Arc::new(StopState::new());
|
||||
let stop_state_clone = stop_state.clone();
|
||||
thread::spawn(move || {
|
||||
match Server::start(
|
||||
config,
|
||||
Some(stop_state_clone.clone()),
|
||||
Some(serv_tx_clone.clone()),
|
||||
api_chan,
|
||||
) {
|
||||
Ok(s) => {
|
||||
if stop_state_clone.is_stopped() {
|
||||
s.stop();
|
||||
return;
|
||||
}
|
||||
let _ = serv_tx_clone.send(ServerInitStatus::FinishedLoading(s));
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = serv_tx_clone.send(ServerInitStatus::ErrorLoading(e));
|
||||
}
|
||||
}
|
||||
});
|
||||
let exit_code = controller.run();
|
||||
stop_state.stop();
|
||||
exit_code
|
||||
} else {
|
||||
warn!("Starting GRIN w/o UI...");
|
||||
servers::Server::start(
|
||||
config,
|
||||
logs_rx,
|
||||
|serv: servers::Server, _: Option<mpsc::Receiver<LogEntry>>| {
|
||||
match Server::start(config, None, None, api_chan) {
|
||||
Ok(s) => {
|
||||
let running = Arc::new(AtomicBool::new(true));
|
||||
let r = running.clone();
|
||||
ctrlc::set_handler(move || {
|
||||
@@ -79,12 +94,14 @@ fn start_server_tui(
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
}
|
||||
warn!("Received SIGINT (Ctrl+C) or SIGTERM (kill).");
|
||||
serv.stop();
|
||||
},
|
||||
None,
|
||||
api_chan,
|
||||
)
|
||||
.unwrap();
|
||||
s.stop();
|
||||
0
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error starting GRIN: {:?}", e);
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+95
-24
@@ -15,6 +15,12 @@
|
||||
//! Basic TUI to better output the overall system status and status
|
||||
//! of various subsystems
|
||||
|
||||
use super::constants::MAIN_MENU;
|
||||
use crate::built_info;
|
||||
use crate::servers::Server;
|
||||
use crate::tui::constants::{ROOT_STACK, VIEW_BASIC_STATUS, VIEW_MINING, VIEW_PEER_SYNC};
|
||||
use crate::tui::types::{TUIStatusListener, UIMessage};
|
||||
use crate::tui::{logs, menu, mining, peers, status, version};
|
||||
use chrono::prelude::Utc;
|
||||
use cursive::direction::Orientation;
|
||||
use cursive::theme::BaseColor::{Black, Blue, Cyan, White};
|
||||
@@ -29,24 +35,20 @@ use cursive::views::{
|
||||
CircularFocus, Dialog, LinearLayout, Panel, SelectView, StackView, TextView, ViewRef,
|
||||
};
|
||||
use cursive::{CursiveRunnable, CursiveRunner};
|
||||
use std::sync::mpsc;
|
||||
use std::{thread, time};
|
||||
|
||||
use super::constants::MAIN_MENU;
|
||||
use crate::built_info;
|
||||
use crate::servers::Server;
|
||||
use crate::tui::constants::{ROOT_STACK, VIEW_BASIC_STATUS, VIEW_MINING, VIEW_PEER_SYNC};
|
||||
use crate::tui::types::{TUIStatusListener, UIMessage};
|
||||
use crate::tui::{logs, menu, mining, peers, status, version};
|
||||
use grin_core::global;
|
||||
use grin_servers::common::types::{Error, ServerInitStatus};
|
||||
use grin_util::logger::LogEntry;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{mpsc, Arc};
|
||||
use std::{thread, time};
|
||||
|
||||
pub struct UI {
|
||||
cursive: CursiveRunner<CursiveRunnable>,
|
||||
ui_rx: mpsc::Receiver<UIMessage>,
|
||||
ui_tx: mpsc::Sender<UIMessage>,
|
||||
controller_tx: mpsc::Sender<ControllerMessage>,
|
||||
logs_rx: mpsc::Receiver<LogEntry>,
|
||||
logs_rx: Option<mpsc::Receiver<LogEntry>>,
|
||||
show_dialog: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
fn modify_theme(theme: &mut Theme) {
|
||||
@@ -65,7 +67,7 @@ impl UI {
|
||||
/// Create a new UI
|
||||
pub fn new(
|
||||
controller_tx: mpsc::Sender<ControllerMessage>,
|
||||
logs_rx: mpsc::Receiver<LogEntry>,
|
||||
logs_rx: Option<mpsc::Receiver<LogEntry>>,
|
||||
) -> UI {
|
||||
let (ui_tx, ui_rx) = mpsc::channel::<UIMessage>();
|
||||
|
||||
@@ -75,6 +77,7 @@ impl UI {
|
||||
ui_rx,
|
||||
controller_tx,
|
||||
logs_rx,
|
||||
show_dialog: Arc::new(AtomicBool::new(false)),
|
||||
};
|
||||
|
||||
// Create UI objects, etc
|
||||
@@ -102,7 +105,7 @@ impl UI {
|
||||
built_info::PKG_VERSION,
|
||||
global::get_chain_type()
|
||||
),
|
||||
Color::Dark(BaseColor::Green),
|
||||
Dark(BaseColor::Green),
|
||||
));
|
||||
|
||||
let main_layer = LinearLayout::new(Orientation::Vertical)
|
||||
@@ -117,17 +120,21 @@ impl UI {
|
||||
let mut theme = grin_ui.cursive.current_theme().clone();
|
||||
modify_theme(&mut theme);
|
||||
grin_ui.cursive.set_theme(theme);
|
||||
|
||||
grin_ui.cursive.add_fullscreen_layer(main_layer);
|
||||
|
||||
// Configure a callback (shutdown, for the first test)
|
||||
let controller_tx_clone = grin_ui.controller_tx.clone();
|
||||
let show_dialog_clone = grin_ui.show_dialog.clone();
|
||||
grin_ui.cursive.add_global_callback('q', move |c| {
|
||||
if show_dialog_clone.load(Ordering::Relaxed) {
|
||||
c.pop_layer();
|
||||
}
|
||||
let content = StyledString::styled("Shutting down...", Color::Light(BaseColor::Yellow));
|
||||
c.add_layer(CircularFocus::new(Dialog::around(TextView::new(content))).wrap_tab());
|
||||
controller_tx_clone
|
||||
.send(ControllerMessage::Shutdown)
|
||||
.unwrap();
|
||||
let _ = controller_tx_clone.send(ControllerMessage::Shutdown);
|
||||
});
|
||||
|
||||
grin_ui.cursive.set_fps(3);
|
||||
grin_ui
|
||||
}
|
||||
@@ -139,8 +146,10 @@ impl UI {
|
||||
return false;
|
||||
}
|
||||
|
||||
while let Some(message) = self.logs_rx.try_iter().next() {
|
||||
logs::TUILogsView::update(&mut self.cursive, message);
|
||||
if let Some(logs_rx) = &self.logs_rx {
|
||||
while let Some(message) = logs_rx.try_iter().next() {
|
||||
logs::TUILogsView::update(&mut self.cursive, message);
|
||||
}
|
||||
}
|
||||
|
||||
// Process any pending UI messages
|
||||
@@ -174,6 +183,8 @@ impl UI {
|
||||
pub struct Controller {
|
||||
rx: mpsc::Receiver<ControllerMessage>,
|
||||
ui: UI,
|
||||
serv_rx: mpsc::Receiver<ServerInitStatus>,
|
||||
server: Option<Server>,
|
||||
}
|
||||
|
||||
pub enum ControllerMessage {
|
||||
@@ -182,39 +193,99 @@ pub enum ControllerMessage {
|
||||
|
||||
impl Controller {
|
||||
/// Create a new controller
|
||||
pub fn new(logs_rx: mpsc::Receiver<LogEntry>) -> Result<Controller, String> {
|
||||
pub fn new(
|
||||
logs_rx: Option<mpsc::Receiver<LogEntry>>,
|
||||
serv_rx: mpsc::Receiver<ServerInitStatus>,
|
||||
) -> Result<Controller, String> {
|
||||
let (tx, rx) = mpsc::channel::<ControllerMessage>();
|
||||
Ok(Controller {
|
||||
rx,
|
||||
ui: UI::new(tx, logs_rx),
|
||||
serv_rx,
|
||||
server: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Server initialization status.
|
||||
pub fn init_status(&mut self, text: &str, pop: bool) {
|
||||
if pop {
|
||||
self.ui.cursive.pop_layer();
|
||||
}
|
||||
let content = StyledString::styled(text, Color::Light(BaseColor::Green));
|
||||
self.ui
|
||||
.cursive
|
||||
.add_layer(CircularFocus::new(Dialog::around(TextView::new(content))).wrap_tab());
|
||||
self.ui.show_dialog.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Server initialization error.
|
||||
pub fn init_error(&mut self, e: Error) {
|
||||
let content = StyledString::styled(format!("{:?}", e), Color::Light(BaseColor::Red));
|
||||
self.ui.cursive.add_layer(
|
||||
CircularFocus::new(Dialog::around(TextView::new(content)).button("Exit", |s| {
|
||||
s.quit();
|
||||
}))
|
||||
.wrap_tab(),
|
||||
);
|
||||
self.ui.show_dialog.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Server UI after initialization.
|
||||
pub fn server(&mut self, server: &Server) {
|
||||
if let Ok(stats) = server.get_server_stats() {
|
||||
self.ui.ui_tx.send(UIMessage::UpdateStatus(stats)).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Run the controller
|
||||
pub fn run(&mut self, server: Server) {
|
||||
pub fn run(&mut self) -> i32 {
|
||||
self.init_status("Starting server...", false);
|
||||
|
||||
let stat_update_interval = 1;
|
||||
let mut next_stat_update = Utc::now().timestamp() + stat_update_interval;
|
||||
let delay = time::Duration::from_millis(50);
|
||||
let mut exit_code = 0;
|
||||
while self.ui.step() {
|
||||
if let Some(message) = self.rx.try_iter().next() {
|
||||
match message {
|
||||
ControllerMessage::Shutdown => {
|
||||
warn!("Shutdown in progress, please wait");
|
||||
self.ui.stop();
|
||||
server.stop();
|
||||
return;
|
||||
if let Some(s) = self.server.take() {
|
||||
s.stop();
|
||||
}
|
||||
return exit_code;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(m) = self.serv_rx.try_iter().next() {
|
||||
match m {
|
||||
ServerInitStatus::LoadDatabase => self.init_status("Loading database...", true),
|
||||
ServerInitStatus::StartSync => self.init_status("Start syncing...", true),
|
||||
ServerInitStatus::StartAPI => self.init_status("Starting API...", true),
|
||||
ServerInitStatus::FinishedLoading(s) => {
|
||||
self.ui.cursive.pop_layer();
|
||||
self.ui.show_dialog.store(false, Ordering::Relaxed);
|
||||
self.server = Some(s)
|
||||
}
|
||||
ServerInitStatus::ErrorLoading(e) => {
|
||||
exit_code = 1;
|
||||
self.init_error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if Utc::now().timestamp() > next_stat_update {
|
||||
next_stat_update = Utc::now().timestamp() + stat_update_interval;
|
||||
if let Ok(stats) = server.get_server_stats() {
|
||||
self.ui.ui_tx.send(UIMessage::UpdateStatus(stats)).unwrap();
|
||||
if let Some(server) = &self.server {
|
||||
if let Ok(stats) = server.get_server_stats() {
|
||||
self.ui.ui_tx.send(UIMessage::UpdateStatus(stats)).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
thread::sleep(delay);
|
||||
}
|
||||
server.stop();
|
||||
exit_code
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user