This repository has been archived by the owner on Nov 15, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Availability/Extrinsic store #465
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
49bc5d9
begin availability store
rphmeier e0c7f48
Merge remote-tracking branch 'upstream/master' into rh-availability-s…
rphmeier 81933fc
ensure availability before issuing statements
rphmeier b9b8d68
use io::Error in table router
rphmeier 00db521
initialize av-store in service and make available in network
rphmeier 4b40d77
encode borrowed values
rphmeier cd13aa2
finalization for availability store
rphmeier d4a7cd0
integrate fully into consensus
rphmeier 21f4ff4
Merge remote-tracking branch 'upstream/master' into rh-availability-s…
rphmeier 1063d07
update WASM
rphmeier 88fef7d
pull availability data out of the store
rphmeier 91f9839
prune available data which does not need to be kept anymore
rphmeier File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file modified
BIN
+8 Bytes
(100%)
demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm
Binary file not shown.
Binary file modified
BIN
+9 Bytes
(100%)
demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
[package] | ||
name = "polkadot-availability-store" | ||
description = "Persistent database for parachain data" | ||
version = "0.1.0" | ||
authors = ["Parity Technologies <admin@parity.io>"] | ||
|
||
[dependencies] | ||
polkadot-primitives = { path = "../primitives" } | ||
parking_lot = "0.4" | ||
log = "0.3" | ||
substrate-codec = { path = "../../substrate/codec" } | ||
substrate-primitives = { path = "../../substrate/primitives" } | ||
kvdb = { git = "https://github.com/paritytech/parity.git" } | ||
kvdb-rocksdb = { git = "https://github.com/paritytech/parity.git" } | ||
kvdb-memorydb = { git = "https://github.com/paritytech/parity.git" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,258 @@ | ||
// Copyright 2018 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. | ||
|
||
// You should have received a copy of the GNU General Public License | ||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
//! Persistent database for parachain data. | ||
|
||
extern crate polkadot_primitives; | ||
extern crate parking_lot; | ||
extern crate substrate_codec as codec; | ||
extern crate substrate_primitives; | ||
extern crate kvdb; | ||
extern crate kvdb_rocksdb; | ||
extern crate kvdb_memorydb; | ||
|
||
#[macro_use] | ||
extern crate log; | ||
|
||
use codec::{Encode, Decode}; | ||
use kvdb::{KeyValueDB, DBTransaction}; | ||
use kvdb_rocksdb::{Database, DatabaseConfig}; | ||
use polkadot_primitives::Hash; | ||
use polkadot_primitives::parachain::{Id as ParaId, BlockData, Extrinsic}; | ||
|
||
use std::collections::HashSet; | ||
use std::path::PathBuf; | ||
use std::sync::Arc; | ||
use std::io; | ||
|
||
mod columns { | ||
pub const DATA: Option<u32> = Some(0); | ||
pub const META: Option<u32> = Some(1); | ||
pub const NUM_COLUMNS: u32 = 2; | ||
} | ||
|
||
/// Configuration for the availability store. | ||
pub struct Config { | ||
/// Cache size in bytes. If `None` default is used. | ||
pub cache_size: Option<usize>, | ||
/// Path to the database. | ||
pub path: PathBuf, | ||
} | ||
|
||
/// Some data to keep available. | ||
pub struct Data { | ||
/// The relay chain parent hash this should be localized to. | ||
pub relay_parent: Hash, | ||
/// The parachain index for this candidate. | ||
pub parachain_id: ParaId, | ||
/// Unique candidate receipt hash. | ||
pub candidate_hash: Hash, | ||
/// Block data. | ||
pub block_data: BlockData, | ||
/// Extrinsic data. | ||
pub extrinsic: Option<Extrinsic>, | ||
} | ||
|
||
fn extract_io_err(err: ::kvdb::Error) -> io::Error { | ||
match err { | ||
::kvdb::Error(::kvdb::ErrorKind::Io(io_err), _) => io_err, | ||
::kvdb::Error(::kvdb::ErrorKind::Msg(msg), _) => io::Error::new( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These errors are gone and replaced (in |
||
io::ErrorKind::Other, | ||
msg, | ||
), | ||
x => io::Error::new( | ||
io::ErrorKind::Other, | ||
format!("Unexpected error variant: {:?}", x), // only necessary because of nonexaustive match. | ||
) | ||
} | ||
} | ||
|
||
fn block_data_key(relay_parent: &Hash, candidate_hash: &Hash) -> Vec<u8> { | ||
(relay_parent, candidate_hash, 0i8).encode() | ||
} | ||
|
||
fn extrinsic_key(relay_parent: &Hash, candidate_hash: &Hash) -> Vec<u8> { | ||
(relay_parent, candidate_hash, 1i8).encode() | ||
} | ||
|
||
/// Handle to the availability store. | ||
#[derive(Clone)] | ||
pub struct Store { | ||
inner: Arc<KeyValueDB>, | ||
} | ||
|
||
impl Store { | ||
/// Create a new `Store` with given config on disk. | ||
pub fn new(config: Config) -> io::Result<Self> { | ||
let mut db_config = DatabaseConfig::with_columns(Some(columns::NUM_COLUMNS)); | ||
db_config.memory_budget = config.cache_size; | ||
db_config.wal = true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This option is also gone (always true now). |
||
|
||
let path = config.path.to_str().ok_or_else(|| io::Error::new( | ||
io::ErrorKind::Other, | ||
format!("Bad database path: {:?}", config.path), | ||
))?; | ||
|
||
let db = Database::open(&db_config, &path).map_err(extract_io_err)?; | ||
|
||
Ok(Store { | ||
inner: Arc::new(db), | ||
}) | ||
} | ||
|
||
/// Create a new `Store` in-memory. Useful for tests. | ||
pub fn new_in_memory() -> Self { | ||
Store { | ||
inner: Arc::new(::kvdb_memorydb::create(::columns::NUM_COLUMNS)), | ||
} | ||
} | ||
|
||
/// Make some data available provisionally. | ||
pub fn make_available(&self, data: Data) -> io::Result<()> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's wrong with |
||
let mut tx = DBTransaction::new(); | ||
|
||
// note the meta key. | ||
let mut v = match self.inner.get(columns::META, &*data.relay_parent) { | ||
Ok(Some(raw)) => Vec::decode(&mut &raw[..]).expect("all stored data serialized correctly; qed"), | ||
Ok(None) => Vec::new(), | ||
Err(e) => { | ||
warn!(target: "availability", "Error reading from availability store: {:?}", e); | ||
Vec::new() | ||
} | ||
}; | ||
|
||
v.push(data.candidate_hash); | ||
tx.put_vec(columns::META, &data.relay_parent[..], v.encode()); | ||
|
||
tx.put_vec( | ||
columns::DATA, | ||
block_data_key(&data.relay_parent, &data.candidate_hash).as_slice(), | ||
data.block_data.encode() | ||
); | ||
|
||
if let Some(_extrinsic) = data.extrinsic { | ||
tx.put_vec( | ||
columns::DATA, | ||
extrinsic_key(&data.relay_parent, &data.candidate_hash).as_slice(), | ||
vec![], | ||
); | ||
} | ||
|
||
self.inner.write(tx).map_err(extract_io_err) | ||
} | ||
|
||
/// Note that a set of candidates have been included in a finalized block with given hash and parent hash. | ||
pub fn candidates_finalized(&self, parent: Hash, finalized_candidates: HashSet<Hash>) -> io::Result<()> { | ||
let mut tx = DBTransaction::new(); | ||
|
||
let v = match self.inner.get(columns::META, &parent[..]) { | ||
Ok(Some(raw)) => Vec::decode(&mut &raw[..]).expect("all stored data serialized correctly; qed"), | ||
Ok(None) => Vec::new(), | ||
Err(e) => { | ||
warn!(target: "availability", "Error reading from availability store: {:?}", e); | ||
Vec::new() | ||
} | ||
}; | ||
tx.delete(columns::META, &parent[..]); | ||
|
||
for candidate_hash in v { | ||
if !finalized_candidates.contains(&candidate_hash) { | ||
tx.delete(columns::DATA, block_data_key(&parent, &candidate_hash).as_slice()); | ||
tx.delete(columns::DATA, extrinsic_key(&parent, &candidate_hash).as_slice()); | ||
} | ||
} | ||
|
||
self.inner.write(tx).map_err(extract_io_err) | ||
} | ||
|
||
/// Query block data. | ||
pub fn block_data(&self, relay_parent: Hash, candidate_hash: Hash) -> Option<BlockData> { | ||
let encoded_key = block_data_key(&relay_parent, &candidate_hash); | ||
match self.inner.get(columns::DATA, &encoded_key[..]) { | ||
Ok(Some(raw)) => Some( | ||
BlockData::decode(&mut &raw[..]).expect("all stored data serialized correctly; qed") | ||
), | ||
Ok(None) => None, | ||
Err(e) => { | ||
warn!(target: "availability", "Error reading from availability store: {:?}", e); | ||
None | ||
} | ||
} | ||
} | ||
|
||
/// Query extrinsic data. | ||
pub fn extrinsic(&self, relay_parent: Hash, candidate_hash: Hash) -> Option<Extrinsic> { | ||
let encoded_key = extrinsic_key(&relay_parent, &candidate_hash); | ||
match self.inner.get(columns::DATA, &encoded_key[..]) { | ||
Ok(Some(_raw)) => Some(Extrinsic), | ||
Ok(None) => None, | ||
Err(e) => { | ||
warn!(target: "availability", "Error reading from availability store: {:?}", e); | ||
None | ||
} | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn finalization_removes_unneeded() { | ||
let relay_parent = [1; 32].into(); | ||
|
||
let para_id_1 = 5.into(); | ||
let para_id_2 = 6.into(); | ||
|
||
let candidate_1 = [2; 32].into(); | ||
let candidate_2 = [3; 32].into(); | ||
|
||
let block_data_1 = BlockData(vec![1, 2, 3]); | ||
let block_data_2 = BlockData(vec![4, 5, 6]); | ||
|
||
let store = Store::new_in_memory(); | ||
store.make_available(Data { | ||
relay_parent, | ||
parachain_id: para_id_1, | ||
candidate_hash: candidate_1, | ||
block_data: block_data_1.clone(), | ||
extrinsic: Some(Extrinsic), | ||
}).unwrap(); | ||
|
||
store.make_available(Data { | ||
relay_parent, | ||
parachain_id: para_id_2, | ||
candidate_hash: candidate_2, | ||
block_data: block_data_2.clone(), | ||
extrinsic: Some(Extrinsic), | ||
}).unwrap(); | ||
|
||
assert_eq!(store.block_data(relay_parent, candidate_1).unwrap(), block_data_1); | ||
assert_eq!(store.block_data(relay_parent, candidate_2).unwrap(), block_data_2); | ||
|
||
assert!(store.extrinsic(relay_parent, candidate_1).is_some()); | ||
assert!(store.extrinsic(relay_parent, candidate_2).is_some()); | ||
|
||
store.candidates_finalized(relay_parent, [candidate_1].iter().cloned().collect()).unwrap(); | ||
|
||
assert_eq!(store.block_data(relay_parent, candidate_1).unwrap(), block_data_1); | ||
assert!(store.block_data(relay_parent, candidate_2).is_none()); | ||
|
||
assert!(store.extrinsic(relay_parent, candidate_1).is_some()); | ||
assert!(store.extrinsic(relay_parent, candidate_2).is_none()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
kvdb*
crates are inparity-common
now.