1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.

// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

//! A `RocksDB` instance for storing parachain data; availability data, and approvals.

#[cfg(feature = "full-node")]
use {
	polkadot_node_subsystem_util::database::Database, std::io, std::path::PathBuf, std::sync::Arc,
};

#[cfg(feature = "full-node")]
mod upgrade;

const LOG_TARGET: &str = "parachain::db";

#[cfg(any(test, feature = "full-node"))]
pub(crate) mod columns {
	pub mod v0 {
		pub const NUM_COLUMNS: u32 = 3;
	}

	pub mod v1 {
		pub const NUM_COLUMNS: u32 = 5;

		pub const COL_AVAILABILITY_DATA: u32 = 0;
		pub const COL_AVAILABILITY_META: u32 = 1;
		pub const COL_APPROVAL_DATA: u32 = 2;
		pub const COL_CHAIN_SELECTION_DATA: u32 = 3;
		pub const COL_DISPUTE_COORDINATOR_DATA: u32 = 4;
		pub const ORDERED_COL: &[u32] =
			&[COL_AVAILABILITY_META, COL_CHAIN_SELECTION_DATA, COL_DISPUTE_COORDINATOR_DATA];
	}
}

/// Columns used by different subsystems.
#[cfg(any(test, feature = "full-node"))]
#[derive(Debug, Clone)]
pub struct ColumnsConfig {
	/// The column used by the av-store for data.
	pub col_availability_data: u32,
	/// The column used by the av-store for meta information.
	pub col_availability_meta: u32,
	/// The column used by approval voting for data.
	pub col_approval_data: u32,
	/// The column used by chain selection for data.
	pub col_chain_selection_data: u32,
	/// The column used by dispute coordinator for data.
	pub col_dispute_coordinator_data: u32,
}

/// The real columns used by the parachains DB.
#[cfg(any(test, feature = "full-node"))]
pub const REAL_COLUMNS: ColumnsConfig = ColumnsConfig {
	col_availability_data: columns::v1::COL_AVAILABILITY_DATA,
	col_availability_meta: columns::v1::COL_AVAILABILITY_META,
	col_approval_data: columns::v1::COL_APPROVAL_DATA,
	col_chain_selection_data: columns::v1::COL_CHAIN_SELECTION_DATA,
	col_dispute_coordinator_data: columns::v1::COL_DISPUTE_COORDINATOR_DATA,
};

#[derive(PartialEq)]
pub(crate) enum DatabaseKind {
	ParityDB,
	RocksDB,
}

/// The cache size for each column, in megabytes.
#[derive(Debug, Clone)]
pub struct CacheSizes {
	/// Cache used by availability data.
	pub availability_data: usize,
	/// Cache used by availability meta.
	pub availability_meta: usize,
	/// Cache used by approval data.
	pub approval_data: usize,
}

impl Default for CacheSizes {
	fn default() -> Self {
		CacheSizes { availability_data: 25, availability_meta: 1, approval_data: 5 }
	}
}

#[cfg(feature = "full-node")]
pub(crate) fn other_io_error(err: String) -> io::Error {
	io::Error::new(io::ErrorKind::Other, err)
}

/// Open the database on disk, creating it if it doesn't exist.
#[cfg(feature = "full-node")]
pub fn open_creating_rocksdb(
	root: PathBuf,
	cache_sizes: CacheSizes,
) -> io::Result<Arc<dyn Database>> {
	use kvdb_rocksdb::{Database, DatabaseConfig};

	let path = root.join("parachains").join("db");

	let mut db_config = DatabaseConfig::with_columns(columns::v1::NUM_COLUMNS);

	let _ = db_config
		.memory_budget
		.insert(columns::v1::COL_AVAILABILITY_DATA, cache_sizes.availability_data);
	let _ = db_config
		.memory_budget
		.insert(columns::v1::COL_AVAILABILITY_META, cache_sizes.availability_meta);
	let _ = db_config
		.memory_budget
		.insert(columns::v1::COL_APPROVAL_DATA, cache_sizes.approval_data);

	let path_str = path
		.to_str()
		.ok_or_else(|| other_io_error(format!("Bad database path: {:?}", path)))?;

	std::fs::create_dir_all(&path_str)?;
	upgrade::try_upgrade_db(&path, DatabaseKind::RocksDB)?;
	let db = Database::open(&db_config, &path_str)?;
	let db = polkadot_node_subsystem_util::database::kvdb_impl::DbAdapter::new(
		db,
		columns::v1::ORDERED_COL,
	);

	Ok(Arc::new(db))
}

/// Open a parity db database.
#[cfg(feature = "full-node")]
pub fn open_creating_paritydb(
	root: PathBuf,
	_cache_sizes: CacheSizes,
) -> io::Result<Arc<dyn Database>> {
	let path = root.join("parachains");
	let path_str = path
		.to_str()
		.ok_or_else(|| other_io_error(format!("Bad database path: {:?}", path)))?;

	std::fs::create_dir_all(&path_str)?;
	upgrade::try_upgrade_db(&path, DatabaseKind::ParityDB)?;

	let db = parity_db::Db::open_or_create(&upgrade::paritydb_version_1_config(&path))
		.map_err(|err| io::Error::new(io::ErrorKind::Other, format!("{:?}", err)))?;

	let db = polkadot_node_subsystem_util::database::paritydb_impl::DbAdapter::new(
		db,
		columns::v1::ORDERED_COL,
	);
	Ok(Arc::new(db))
}