Merge remote-tracking branch 'grin_ardocrat/lmdb_update' into grim

This commit is contained in:
ardocrat
2026-06-04 17:47:57 +03:00
31 changed files with 573 additions and 228 deletions
+33 -6
View File
@@ -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"]
+2 -1
View File
@@ -16,5 +16,6 @@
"github.copilot-chat"
]
}
}
},
"runArgs": ["--privileged", "-v", "/sys/fs/cgroup:/sys/fs/cgroup:ro", "-v", "/tmp:/tmp"]
}
+45
View File
@@ -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
+40
View File
@@ -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
View File
@@ -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
+1 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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'.
+3 -2
View File
@@ -43,7 +43,7 @@ use std::collections::HashMap;
use std::fs::{self, File};
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::sync::{mpsc, Arc};
use std::time::{Duration, Instant};
/// Orphan pool size is limited by MAX_ORPHAN_SIZE
@@ -171,8 +171,9 @@ impl Chain {
genesis: Block,
pow_verifier: fn(&BlockHeader) -> Result<(), pow::Error>,
archive_mode: bool,
db_migration_prog_tx: Option<mpsc::Sender<i8>>,
) -> Result<Chain, Error> {
let store = Arc::new(store::ChainStore::new(&db_root)?);
let store = Arc::new(store::ChainStore::new(&db_root, db_migration_prog_tx)?);
// open the txhashset, creating a new one if necessary
let mut txhashset = txhashset::TxHashSet::open(db_root.clone(), store.clone(), None)?;
+6 -2
View File
@@ -28,7 +28,7 @@ use grin_core::ser;
use grin_store as store;
use grin_store::{option_to_not_found, Error};
use std::convert::TryInto;
use std::sync::Arc;
use std::sync::{mpsc, Arc};
const STORE_SUBPATH: &str = "chain";
@@ -67,13 +67,17 @@ pub struct ChainStore {
impl ChainStore {
/// Create new chain store
pub fn new(db_root: &str) -> Result<ChainStore, Error> {
pub fn new(
db_root: &str,
db_migration_prog_tx: Option<mpsc::Sender<i8>>,
) -> Result<ChainStore, Error> {
let db = store::Store::new(
db_root,
None,
Some(STORE_SUBPATH),
DB_PREFIXES.to_vec(),
None,
db_migration_prog_tx,
)?;
Ok(ChainStore { db })
}
+1
View File
@@ -42,6 +42,7 @@ pub fn init_chain(dir_name: &str, genesis: Block) -> Chain {
genesis,
pow::verify_size,
false,
None,
)
.unwrap()
}
+2
View File
@@ -67,6 +67,7 @@ fn setup_with_status_adapter(dir_name: &str, genesis: Block, adapter: Arc<Status
genesis,
pow::verify_size,
false,
None,
)
.unwrap();
@@ -1057,6 +1058,7 @@ fn actual_diff_iter_output() {
genesis_block,
pow::verify_size,
false,
None,
)
.unwrap();
let iter = chain.difficulty_iter().unwrap();
+5 -5
View File
@@ -38,7 +38,7 @@ fn test_store_kernel_idx() {
let commit = Commitment::from_vec(vec![]);
let store = ChainStore::new(chain_dir).unwrap();
let store = ChainStore::new(chain_dir, None).unwrap();
let mut batch = store.batch().unwrap();
let index = store::nrd_recent_kernel_index();
@@ -185,7 +185,7 @@ fn test_store_kernel_idx_pop_back() {
let commit = Commitment::from_vec(vec![]);
let store = ChainStore::new(chain_dir).unwrap();
let store = ChainStore::new(chain_dir, None).unwrap();
let mut batch = store.batch().unwrap();
let index = store::nrd_recent_kernel_index();
@@ -293,7 +293,7 @@ fn test_store_kernel_idx_rewind() {
let commit = Commitment::from_vec(vec![]);
let store = ChainStore::new(chain_dir).unwrap();
let store = ChainStore::new(chain_dir, None).unwrap();
let mut batch = store.batch().unwrap();
let index = store::nrd_recent_kernel_index();
@@ -395,7 +395,7 @@ fn test_store_kernel_idx_multiple_commits() {
let commit = Commitment::from_vec(vec![]);
let commit2 = Commitment::from_vec(vec![1]);
let store = ChainStore::new(chain_dir).unwrap();
let store = ChainStore::new(chain_dir, None).unwrap();
let mut batch = store.batch().unwrap();
let index = store::nrd_recent_kernel_index();
@@ -483,7 +483,7 @@ fn test_store_kernel_idx_clear() -> Result<(), Error> {
let commit = Commitment::from_vec(vec![]);
let commit2 = Commitment::from_vec(vec![1]);
let store = ChainStore::new(chain_dir)?;
let store = ChainStore::new(chain_dir, None)?;
let index = store::nrd_recent_kernel_index();
// Add a couple of single entries to the index and commit the batch.
+1
View File
@@ -47,6 +47,7 @@ fn test_coinbase_maturity() {
genesis_block,
pow::verify_size,
false,
None,
)
.unwrap();
+2
View File
@@ -50,6 +50,7 @@ fn test_header_perf_impl(is_test_chain: bool, src_root_dir: &str, dest_root_dir:
genesis.clone(),
pow::verify_size,
false,
None,
)
.unwrap(),
);
@@ -62,6 +63,7 @@ fn test_header_perf_impl(is_test_chain: bool, src_root_dir: &str, dest_root_dir:
genesis.clone(),
pow::verify_size,
false,
None,
)
.unwrap(),
);
+2
View File
@@ -74,6 +74,7 @@ impl SegmenterResponder {
genesis,
pow::verify_size,
false,
None,
)
.unwrap(),
),
@@ -134,6 +135,7 @@ impl DesegmenterRequestor {
genesis,
pow::verify_size,
false,
None,
)
.unwrap(),
),
+1
View File
@@ -52,6 +52,7 @@ fn test_pibd_chain_validation_impl(is_test_chain: bool, src_root_dir: &str) {
genesis.clone(),
pow::verify_size,
false,
None,
)
.unwrap(),
);
+1 -1
View File
@@ -38,7 +38,7 @@ fn test_unexpected_zip() {
let db_root = format!(".grin_txhashset_zip");
clean_output_dir(&db_root);
{
let chain_store = ChainStore::new(&db_root).unwrap();
let chain_store = ChainStore::new(&db_root, None).unwrap();
let store = Arc::new(chain_store);
txhashset::TxHashSet::open(db_root.clone(), store.clone(), None).unwrap();
let head = BlockHeader::default();
+5 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -124,6 +124,7 @@ impl PeerStore {
Some(STORE_SUBPATH),
vec![PEER_PREFIX],
None,
None,
)?;
Ok(PeerStore { db })
}
+1 -1
View File
@@ -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,
+1
View File
@@ -55,6 +55,7 @@ pub fn init_chain(dir_name: &str, genesis: Block) -> Chain {
genesis,
pow::verify_size,
false,
None,
)
.unwrap()
}
+17 -1
View File
@@ -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,19 @@ impl DandelionEpoch {
self.relay_peer.clone()
}
}
/// Server initialization status.
pub enum ServerInitStatus {
/// Database loading.
LoadDatabase,
/// Database migration progress.
DBMigrationProgress(i8),
/// P2P server initialization.
StartSync,
/// API server initialization.
StartAPI,
/// Server instance after successful initialization.
FinishedLoading(Server),
/// Error on initialization.
ErrorLoading(Error),
}
+11 -9
View File
@@ -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(
+37 -14
View File
@@ -16,6 +16,7 @@
//! the peer-to-peer server, the blockchain and the transaction pool) and acts
//! as a facade.
use fs2::FileExt;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
@@ -25,8 +26,6 @@ use std::{
thread::{self, JoinHandle},
time::{self, Duration},
};
use fs2::FileExt;
use walkdir::WalkDir;
use crate::api;
@@ -39,7 +38,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 +51,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 +82,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 +112,7 @@ impl Server {
}
}
info_callback(serv, logs_rx);
Ok(())
Ok(serv)
}
// Exclusive (advisory) lock_file to ensure we do not run multiple
@@ -151,6 +144,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,12 +187,33 @@ impl Server {
info!("Starting server, genesis block: {}", genesis.hash());
if let Some(ref server_tx) = server_tx {
let _ = server_tx.send(ServerInitStatus::LoadDatabase);
}
let (db_migration_prog_tx, db_migration_prog_rx) = mpsc::channel::<i8>();
if let Some(ref server_tx) = server_tx {
let server_tx = server_tx.clone();
thread::spawn(move || loop {
match db_migration_prog_rx.recv() {
Ok(p) => {
if p == 100 {
break;
}
let _ = server_tx.send(ServerInitStatus::DBMigrationProgress(p));
}
Err(_) => break,
}
});
}
let shared_chain = Arc::new(chain::Chain::init(
config.db_root.clone(),
chain_adapter.clone(),
genesis.clone(),
pow::verify_size,
archive_mode,
Some(db_migration_prog_tx),
)?);
pool_adapter.set_chain(shared_chain.clone());
@@ -220,6 +235,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 +284,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());
+47 -37
View File
@@ -28,47 +28,54 @@ 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
/// Start node server at TUI or non-TUI mode.
pub fn start_server(
config: servers::ServerConfig,
logs_rx: Option<mpsc::Receiver<LogEntry>>,
api_chan: &'static mut (oneshot::Sender<()>, oneshot::Receiver<()>),
) {
start_server_tui(config, logs_rx, api_chan);
exit(0);
}
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
if config.run_tui.unwrap_or(false) {
let exit_code = 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,13 +86,16 @@ 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
}
}
};
exit(exit_code);
}
/// Handles the server part of the command line, mostly running, starting and
+100 -25
View File
@@ -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,103 @@ 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 {
return 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();
}
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);
}
ServerInitStatus::DBMigrationProgress(p) => {
let status = format!("Migrating database: {}%, please wait...", p);
self.init_status(status.as_str(), true);
}
}
}
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
}
}
+155 -102
View File
@@ -19,7 +19,7 @@ use heed::{Database, Env, EnvOpenOptions, RoTxn, RwTxn, WithoutTls};
use std::collections::HashMap;
use std::path::Path;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::{Arc, OnceLock};
use std::sync::{mpsc, Arc, OnceLock};
use std::time::Duration;
use std::{fs, thread};
@@ -87,6 +87,8 @@ const DEFAULT_DB_VERSION: ProtocolVersion = ProtocolVersion(3);
pub const DEFAULT_ENV_NAME: &'static str = "lmdb";
/// Default multi-database environment without prefixes.
const DEFAULT_MULTI_DB_ENV_NAME: &'static str = "multi_lmdb";
/// Migration completion marker in the default database.
const MIGRATION_COMPLETE_KEY: &[u8] = b"__grin_migration_complete";
/// Prefix key separator.
pub const PREFIX_KEY_SEPARATOR: u8 = b':';
@@ -159,6 +161,7 @@ impl Store {
db_name: Option<&str>,
prefixes: Vec<u8>,
max_readers: Option<u32>,
db_migration_prog_tx: Option<mpsc::Sender<i8>>,
) -> Result<Store, Error> {
let full_path = Path::new(root_path)
.join(DEFAULT_MULTI_DB_ENV_NAME)
@@ -225,25 +228,28 @@ impl Store {
}
// Database setup.
let r_env_map = env_map.read();
let env = r_env_map.get(&full_path).unwrap().env.clone();
let mut write = env.write_txn()?;
let def_name = db_name.unwrap_or(DEFAULT_ENV_NAME);
let def_db = env.create_database(&mut write, Some(def_name))?;
let mut dbs_map = HashMap::<u8, Database<Bytes, Bytes>>::new();
for p in prefixes {
let db = env.create_database(&mut write, Some(p.to_string().as_str()))?;
dbs_map.insert(p, db);
}
write.commit()?;
let s = {
let r_env_map = env_map.read();
let env = r_env_map.get(&full_path).unwrap().env.clone();
let mut write = env.write_txn()?;
let def_name = db_name.unwrap_or(DEFAULT_ENV_NAME);
let def_db = env.create_database(&mut write, Some(def_name))?;
let mut dbs_map = HashMap::<u8, Database<Bytes, Bytes>>::new();
for p in prefixes {
let db = env.create_database(&mut write, Some(p.to_string().as_str()))?;
dbs_map.insert(p, db);
}
write.commit()?;
let s = Store {
env: env.clone(),
env_path: full_path.clone(),
pre_dbs: Arc::new(dbs_map),
def_db,
version: DEFAULT_DB_VERSION,
alloc_chunk_size,
let s = Store {
env: env.clone(),
env_path: full_path.clone(),
pre_dbs: Arc::new(dbs_map),
def_db,
version: DEFAULT_DB_VERSION,
alloc_chunk_size,
};
s
};
// Migrate to default environment if needed.
@@ -251,25 +257,42 @@ impl Store {
if env_name != DEFAULT_MULTI_DB_ENV_NAME {
let migrate_from = Path::new(root_path).join(env_name);
if migrate_from.exists() {
match s.migrate_to_default_env(db_name, &migrate_from) {
Ok(_) => match fs::remove_dir_all(&migrate_from) {
Ok(_) => {}
let delete_old_db_file = || -> Result<(), Error> {
match fs::remove_dir_all(&migrate_from) {
Ok(_) => Ok(()),
Err(e) => {
return Err(Error::FileErr(format!(
"Can not remove old DB file: {:?}",
e
)));
}
},
Err(e) => {
error!("DB {} migration error: {:?}", env_name, e);
match s.clear() {
Ok(_) => {}
Err(e) => {
error!("Can not clear new DB after unsuccessful migration: {:?}", e)
}
};
if s.migration_complete()? {
if let Err(e) = delete_old_db_file() {
return Err(e);
}
} else {
let _ = s.clear();
match s.migrate_to_default_env(db_name, &migrate_from, db_migration_prog_tx) {
Ok(_) => {
if let Err(e) = delete_old_db_file() {
return Err(e);
}
}
return Err(e);
Err(e) => {
error!("DB {} migration error: {:?}", env_name, e);
match s.clear() {
Ok(_) => {}
Err(e) => {
error!(
"Can not clear new DB after unsuccessful migration: {:?}",
e
)
}
}
return Err(e);
}
}
}
}
@@ -278,13 +301,31 @@ impl Store {
Ok(s)
}
/// Check if migration has already completed successfully.
fn migration_complete(&self) -> Result<bool, Error> {
let read = self.env.read_txn()?;
Ok(self.def_db.get(&read, MIGRATION_COMPLETE_KEY)?.is_some())
}
/// Mark migration as successfully completed.
fn set_migration_complete(&self, write: &mut RwTxn<'_>) -> Result<(), Error> {
self.def_db.put(write, MIGRATION_COMPLETE_KEY, b"1")?;
Ok(())
}
/// Migrate database from provided path to default environment.
fn migrate_to_default_env(
&self,
from_name: Option<&str>,
from_path: &Path,
db_migration_prog_tx: Option<mpsc::Sender<i8>>,
) -> Result<(), Error> {
info!("Migrating DB {:?}, please wait...", from_path);
if let Some(migration_prog_tx) = &db_migration_prog_tx {
let _ = migration_prog_tx.send(0i8);
}
let from_env = unsafe {
let mut options = EnvOpenOptions::new().read_txn_without_tls();
let env_options = options.map_size(self.alloc_chunk_size).max_dbs(24);
@@ -307,7 +348,16 @@ impl Store {
let mut write_to = self.env.write_txn()?;
let read_from = from_env.read_txn()?;
let mut count = 0;
for kv in db_from.iter(&read_from)? {
let total = db_from.iter(&read_from)?.count();
let mut prev_prog = 0;
for (index, kv) in db_from.iter(&read_from)?.enumerate() {
if let Some(migration_prog_tx) = &db_migration_prog_tx {
let prog = 100 * index / total;
if prev_prog != prog && prog != 100 {
prev_prog = prog;
let _ = migration_prog_tx.send(prog as i8);
}
}
let (k, v) = kv?;
if k.len() > 1 && k[1] == PREFIX_KEY_SEPARATOR {
let db_name = k.split_at(1).0;
@@ -323,7 +373,13 @@ impl Store {
count += 1;
}
}
self.set_migration_complete(&mut write_to)?;
write_to.commit()?;
if let Some(migration_prog_tx) = &db_migration_prog_tx {
let _ = migration_prog_tx.send(100i8);
}
info!("Migrated {} records from {:?}", count, from_path);
Ok(())
}
@@ -367,7 +423,7 @@ impl Store {
/// Wait while database is resizing.
fn wait_for_resize(&self) {
loop {
if !ENV_MAP
if ENV_MAP
.get()
.unwrap()
.read()
@@ -375,11 +431,13 @@ impl Store {
.unwrap()
.resizing
.load(Ordering::Relaxed)
&& self.open_txs_count() == 0
{
break;
debug!("Wait on resizing DB {}", self.env_path);
thread::sleep(Duration::from_millis(100));
continue;
}
trace!("Wait on resizing DB {}", self.env_path);
thread::sleep(Duration::from_millis(100));
break;
}
}
@@ -399,9 +457,16 @@ impl Store {
let env_path = self.env_path.clone();
let env = self.env.clone();
{
let mut w_env_map = ENV_MAP.get().unwrap().write();
let env_state = w_env_map.get_mut(&env_path).unwrap();
env_state.resizing.store(true, Ordering::Relaxed);
}
// Resize immediately or at another thread to not interrupt current
// transaction waiting all open transactions to be closed.
if self.open_txs_count() != 0 {
debug!("Waiting txs to be closed before DB {} resize", env_path);
thread::spawn(move || {
loop {
let txs_count = ENV_MAP
@@ -414,19 +479,9 @@ impl Store {
.load(Ordering::Relaxed);
if txs_count == 0 {
debug!("Start resizing DB {}", env_path);
ENV_MAP
.get()
.unwrap()
.write()
.get_mut(&env_path)
.unwrap()
.resizing
.store(true, Ordering::Relaxed);
// Wait to make sure there are no more active txs left.
thread::sleep(Duration::from_millis(1000));
break;
}
thread::sleep(Duration::from_millis(10));
thread::sleep(Duration::from_millis(100));
}
unsafe {
@@ -443,17 +498,15 @@ impl Store {
});
return;
} else {
let mut w_env_map = ENV_MAP.get().unwrap().write();
let env_state = w_env_map.get_mut(&env_path).unwrap();
debug!("Start immediate resizing DB {}", env_path);
env_state.resizing.store(true, Ordering::Relaxed);
unsafe {
match env.resize(new_size) {
Ok(_) => debug!("End resizing DB {}", env_path),
Err(e) => error!("Resize DB {} error: {:?}", env_path, e),
}
}
let mut w_env_map = ENV_MAP.get().unwrap().write();
let env_state = w_env_map.get_mut(&env_path).unwrap();
env_state.resizing.store(false, Ordering::Relaxed);
}
}
@@ -577,27 +630,27 @@ impl Store {
self.wait_for_resize();
TxCounter::on_change_tx_count(&self.env_path, true);
match self.env.clone().static_read_txn() {
Ok(read) => {
let db_res = self.get_db(db_key);
match db_res {
Ok(db) => Ok(DatabaseIterator::new(
self,
Arc::new(db.clone()),
read,
deserialize,
)),
Err(e) => {
TxCounter::on_change_tx_count(&self.env_path, false);
Err(Error::from(e))
let res = {
match self.env.clone().static_read_txn() {
Ok(read) => {
let db_res = self.get_db(db_key);
match db_res {
Ok(db) => Ok(DatabaseIterator::new(
self,
Arc::new(db.clone()),
read,
deserialize,
)),
Err(e) => Err(Error::from(e)),
}
}
Err(e) => Err(Error::from(e)),
}
Err(e) => {
TxCounter::on_change_tx_count(&self.env_path, false);
Err(Error::from(e))
}
};
if res.is_err() {
TxCounter::on_change_tx_count(&self.env_path, false);
}
res
}
/// Builds a new batch to be used with this store.
@@ -605,13 +658,11 @@ impl Store {
self.maybe_resize();
TxCounter::on_change_tx_count(&self.env_path, true);
match Batch::new(self) {
Ok(batch) => Ok(batch),
Err(e) => {
TxCounter::on_change_tx_count(&self.env_path, false);
Err(e)
}
let res = { Batch::new(self) };
if res.is_err() {
TxCounter::on_change_tx_count(&self.env_path, false);
}
res
}
}
@@ -741,28 +792,27 @@ impl<'a> Batch<'a> {
self.store.wait_for_resize();
TxCounter::on_change_tx_count(&self.store.env_path, true);
let read = self.write.nested_read_txn();
match read {
Ok(read) => {
let db_res = self.store.get_db(db_key);
match db_res {
Ok(db) => Ok(DatabaseIterator::new(
self.store,
Arc::new(db.clone()),
read,
deserialize,
)),
Err(e) => {
TxCounter::on_change_tx_count(&self.store.env_path, false);
Err(Error::from(e))
let res = {
match self.write.nested_read_txn() {
Ok(read) => {
let db_res = self.store.get_db(db_key);
match db_res {
Ok(db) => Ok(DatabaseIterator::new(
self.store,
Arc::new(db.clone()),
read,
deserialize,
)),
Err(e) => Err(Error::from(e)),
}
}
Err(e) => Err(Error::from(e)),
}
Err(e) => {
TxCounter::on_change_tx_count(&self.store.env_path, false);
Err(Error::from(e))
}
};
if res.is_err() {
TxCounter::on_change_tx_count(&self.store.env_path, false);
}
res
}
/// Gets a `Readable` value from the database by provided key and deserialization strategy.
@@ -801,19 +851,22 @@ impl<'a> Batch<'a> {
/// commit, abandoned otherwise.
pub fn child(&mut self) -> Result<Batch<'_>, Error> {
TxCounter::on_change_tx_count(&self.store.env_path, true);
match self.store.env.nested_write_txn(&mut self.write) {
Ok(write) => Ok(Batch {
store: self.store,
write,
tx_counter: TxCounter {
env_path: self.store.env_path.clone(),
},
}),
Err(e) => {
TxCounter::on_change_tx_count(&self.store.env_path, false);
Err(Error::from(e))
let res = {
match self.store.env.nested_write_txn(&mut self.write) {
Ok(write) => Ok(Batch {
store: self.store,
write,
tx_counter: TxCounter {
env_path: self.store.env_path.clone(),
},
}),
Err(e) => Err(Error::from(e)),
}
};
if res.is_err() {
TxCounter::on_change_tx_count(&self.store.env_path, false);
}
res
}
}
+5 -4
View File
@@ -79,7 +79,7 @@ fn test_exists() -> Result<(), store::Error> {
setup(test_dir);
let prefix = b'P';
let store = store::Store::new(test_dir, Some("test1"), None, vec![prefix], None)?;
let store = store::Store::new(test_dir, Some("test1"), None, vec![prefix], None, None)?;
let key = [0, 0, 0, 1];
let value = [1, 1, 1, 1];
@@ -109,7 +109,7 @@ fn test_iter() -> Result<(), store::Error> {
setup(test_dir);
let prefix = b'P';
let store = store::Store::new(test_dir, Some("test1"), None, vec![prefix], None)?;
let store = store::Store::new(test_dir, Some("test1"), None, vec![prefix], None, None)?;
let key = [0, 0, 0, 1];
let value = [1, 1, 1, 1];
@@ -148,7 +148,7 @@ fn lmdb_allocate() -> Result<(), store::Error> {
// Allocate more than the initial chunk, ensuring
// the DB resizes underneath
{
let store = store::Store::new(test_dir, Some("test1"), None, vec![prefix], None)?;
let store = store::Store::new(test_dir, Some("test1"), None, vec![prefix], None, None)?;
for i in 0..WRITE_CHUNK_SIZE * 2 {
println!("Allocating chunk: {}", i);
@@ -164,7 +164,7 @@ fn lmdb_allocate() -> Result<(), store::Error> {
println!("***********************************");
// Open env again and keep adding
{
let store = store::Store::new(test_dir, Some("test1"), None, vec![prefix], None)?;
let store = store::Store::new(test_dir, Some("test1"), None, vec![prefix], None, None)?;
for i in 0..WRITE_CHUNK_SIZE * 2 {
println!("Allocating chunk: {}", i);
let chunk = PhatChunkStruct::new();
@@ -253,6 +253,7 @@ fn test_migration() -> Result<(), store::Error> {
Some(DEFAULT_ENV_NAME),
vec![test_prefix_1, test_prefix_2],
None,
None,
)?;
// Check we can see key value.