Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d5cd4ca
fix: wasm file
seerscode Feb 1, 2019
a19a704
feat: add Ord to Hash
seerscode Jan 28, 2019
427ac59
feat: add children function to backend
seerscode Jan 29, 2019
79ae994
feat: add test for children hashes
seerscode Jan 29, 2019
a89f657
feat: add uncles function to client
seerscode Jan 29, 2019
a5dcc25
fix: improve uncles function adds few more tests
seerscode Feb 1, 2019
a2e00c2
chore: address review
seerscode Feb 1, 2019
dbcf786
fix: wasm file, typo, unused import
seerscode Feb 1, 2019
88b8745
fix: children datastructure to not keep children on memory
seerscode Feb 4, 2019
ef925d7
fix: types
seerscode Feb 4, 2019
b94a380
fix tests
seerscode Feb 4, 2019
aab6e23
fix: it should use the map already created
seerscode Feb 4, 2019
afa09cc
chore: add documentation
seerscode Feb 4, 2019
6e14788
chore: remove unused imports
seerscode Feb 4, 2019
b5c151a
fix tests
seerscode Feb 13, 2019
107984e
fix: Order of Hash
seerscode Feb 13, 2019
3fae6ca
fix: remove get_children function
seerscode Feb 13, 2019
6809938
fix: separate HashMap from db
seerscode Feb 14, 2019
f587d69
fix: remove hashmap
seerscode Feb 15, 2019
6b55176
feat: add tests for backend
seerscode Feb 18, 2019
3a0a58b
Merge branch 'master' into mar-client-provide-uncles
gavofyork Feb 21, 2019
adfb706
chore: free functions
seerscode Feb 25, 2019
bf5a3e8
feat: add remove children
seerscode Feb 25, 2019
96ce77f
fix: remove children when reverting
seerscode Feb 25, 2019
334fc5a
Merge branch 'mar-client-provide-uncles' of github.com:paritytech/sub…
seerscode Feb 25, 2019
b6fc5bf
fix: typo and spec version
seerscode Feb 25, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions core/client/db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ use executor::RuntimeInfo;
use state_machine::{CodeExecutor, DBValue};
use crate::utils::{Meta, db_err, meta_keys, open_database, read_db, block_id_to_lookup_key, read_meta};
use client::LeafSet;
use client::children;
use state_db::StateDb;
use crate::storage_cache::{CachingState, SharedCache, new_shared_cache};
use log::{trace, debug, warn};
Expand Down Expand Up @@ -249,6 +250,10 @@ impl<Block: BlockT> client::blockchain::Backend<Block> for BlockchainDb<Block> {
fn leaves(&self) -> Result<Vec<Block::Hash>, client::error::Error> {
Ok(self.leaves.read().hashes())
}

fn children(&self, parent_hash: Block::Hash) -> Result<Vec<Block::Hash>, client::error::Error> {
children::read_children(&*self.db, columns::META, meta_keys::CHILDREN_PREFIX, parent_hash)
}
}

/// Database transaction
Expand Down Expand Up @@ -857,6 +862,10 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
displaced_leaf
};

let mut children = children::read_children(&*self.storage.db, columns::META, meta_keys::CHILDREN_PREFIX, parent_hash)?;
children.push(hash);
children::write_children(&mut transaction, columns::META, meta_keys::CHILDREN_PREFIX, parent_hash, children);

meta_updates.push((hash, number, pending_block.leaf_state.is_best(), finalized));

Some((number, hash, enacted, retracted, displaced_leaf, is_best))
Expand Down Expand Up @@ -1080,6 +1089,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
let key = utils::number_and_hash_to_lookup_key(best.clone(), &hash);
transaction.put(columns::META, meta_keys::BEST_BLOCK, &key);
transaction.delete(columns::KEY_LOOKUP, removed.hash().as_ref());
children::remove_children(&mut transaction, columns::META, meta_keys::CHILDREN_PREFIX, hash);
self.storage.db.write(transaction).map_err(db_err)?;
self.blockchain.update_meta(hash, best, true, false);
self.blockchain.leaves.write().revert(removed.hash().clone(), removed.number().clone(), removed.parent_hash().clone());
Expand Down Expand Up @@ -1793,6 +1803,12 @@ mod tests {
test_client::trait_tests::test_leaves_for_backend(backend);
}

#[test]
fn test_children_with_complex_block_tree() {
let backend: Arc<Backend<test_client::runtime::Block>> = Arc::new(Backend::new_test(20, 20));
test_client::trait_tests::test_children_for_backend(backend);
}

#[test]
fn test_blockchain_query_by_number_gets_canonical() {
let backend: Arc<Backend<test_client::runtime::Block>> = Arc::new(Backend::new_test(20, 20));
Expand Down
2 changes: 2 additions & 0 deletions core/client/db/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ pub mod meta_keys {
pub const GENESIS_HASH: &[u8; 3] = b"gen";
/// Leaves prefix list key.
pub const LEAF_PREFIX: &[u8; 4] = b"leaf";
/// Children prefix list key.
pub const CHILDREN_PREFIX: &[u8; 8] = b"children";
}

/// Database metadata.
Expand Down
3 changes: 3 additions & 0 deletions core/client/src/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ pub trait Backend<Block: BlockT>: HeaderBackend<Block> {
/// in other words, that have no children, are chain heads.
/// Results must be ordered best (longest, heighest) chain first.
fn leaves(&self) -> Result<Vec<Block::Hash>>;

/// Return hashes of all blocks that are children of the block with `parent_hash`.
fn children(&self, parent_hash: Block::Hash) -> Result<Vec<Block::Hash>>;
}

/// Blockchain optional data cache.
Expand Down
121 changes: 121 additions & 0 deletions core/client/src/children.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.

// Substrate 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.

// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.

//! Functionality for reading and storing children hashes from db.

use kvdb::{KeyValueDB, DBTransaction};
use parity_codec::{Encode, Decode};
use crate::error;
use std::hash::Hash;


/// Returns the hashes of the children blocks of the block with `parent_hash`.
pub fn read_children<
K: Eq + Hash + Clone + Encode + Decode,
V: Eq + Hash + Clone + Encode + Decode,
>(db: &KeyValueDB, column: Option<u32>, prefix: &[u8], parent_hash: K) -> error::Result<Vec<V>> {
let mut buf = prefix.to_vec();
parent_hash.using_encoded(|s| buf.extend(s));

let raw_val_opt = match db.get(column, &buf[..]) {
Ok(raw_val_opt) => raw_val_opt,
Err(_) => return Err(error::ErrorKind::Backend("Error reading value from database".into()).into()),
};

let raw_val = match raw_val_opt {
Some(val) => val,
None => return Ok(Vec::new()),
};

let children: Vec<V> = match Decode::decode(&mut &raw_val[..]) {
Some(children) => children,
None => return Err(error::ErrorKind::Backend("Error decoding children".into()).into()),
};

Ok(children)
}

/// Insert the key-value pair (`parent_hash`, `children_hashes`) in the transaction.
/// Any existing value is overwritten upon write.
pub fn write_children<
K: Eq + Hash + Clone + Encode + Decode,
V: Eq + Hash + Clone + Encode + Decode,
>(
tx: &mut DBTransaction,
column: Option<u32>,
prefix: &[u8],
parent_hash: K,
children_hashes: V,
) {
let mut key = prefix.to_vec();
parent_hash.using_encoded(|s| key.extend(s));
tx.put_vec(column, &key[..], children_hashes.encode());
}

/// Prepare transaction to remove the children of `parent_hash`.
pub fn remove_children<
K: Eq + Hash + Clone + Encode + Decode,
>(
tx: &mut DBTransaction,
column: Option<u32>,
prefix: &[u8],
parent_hash: K,
) {
let mut key = prefix.to_vec();
parent_hash.using_encoded(|s| key.extend(s));
tx.delete(column, &key[..]);
}


#[cfg(test)]
mod tests {
use super::*;

#[test]
fn children_write_read_remove() {
const PREFIX: &[u8] = b"children";
let db = ::kvdb_memorydb::create(0);

let mut tx = DBTransaction::new();

let mut children1 = Vec::new();
children1.push(1_3);
children1.push(1_5);
write_children(&mut tx, None, PREFIX, 1_1, children1);

let mut children2 = Vec::new();
children2.push(1_4);
children2.push(1_6);
write_children(&mut tx, None, PREFIX, 1_2, children2);

db.write(tx.clone()).unwrap();

let r1: Vec<u32> = read_children(&db, None, PREFIX, 1_1).unwrap();
let r2: Vec<u32> = read_children(&db, None, PREFIX, 1_2).unwrap();

assert_eq!(r1, vec![1_3, 1_5]);
assert_eq!(r2, vec![1_4, 1_6]);

remove_children(&mut tx, None, PREFIX, 1_2);
db.write(tx).unwrap();

let r1: Vec<u32> = read_children(&db, None, PREFIX, 1_1).unwrap();
let r2: Vec<u32> = read_children(&db, None, PREFIX, 1_2).unwrap();

assert_eq!(r1, vec![1_3, 1_5]);
assert_eq!(r2.len(), 0);
}
}
144 changes: 143 additions & 1 deletion core/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ use executor::{RuntimeVersion, RuntimeInfo};
use crate::notifications::{StorageNotifications, StorageEventStream};
use crate::light::{call_executor::prove_execution, fetcher::ChangesProof};
use crate::cht;
use crate::error;
use crate::error::{self, ErrorKind};
use crate::in_mem;
use crate::block_builder::{self, api::BlockBuilder as BlockBuilderAPI};
use crate::genesis;
Expand Down Expand Up @@ -1230,6 +1230,37 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
Ok(None)
}

/// Gets the uncles of the block with `target_hash` going back `max_generation` ancestors.
pub fn uncles(&self, target_hash: Block::Hash, max_generation: NumberFor<Block>) -> error::Result<Vec<Block::Hash>> {
let load_header = |id: Block::Hash| -> error::Result<Block::Header> {
match self.backend.blockchain().header(BlockId::Hash(id))? {
Some(hdr) => Ok(hdr),
None => Err(ErrorKind::UnknownBlock(format!("Unknown block {:?}", id)).into()),
}
};

let genesis_hash = self.backend.blockchain().info()?.genesis_hash;
if genesis_hash == target_hash { return Ok(Vec::new()); }

let mut current_hash = target_hash;
let mut current = load_header(current_hash)?;
let mut ancestor_hash = *current.parent_hash();
let mut ancestor = load_header(ancestor_hash)?;
let mut uncles = Vec::new();

for _generation in 0..max_generation.as_() {
let children = self.backend.blockchain().children(ancestor_hash)?;
uncles.extend(children.into_iter().filter(|h| h != &current_hash));
current_hash = ancestor_hash;
if genesis_hash == current_hash { break; }
current = ancestor;
ancestor_hash = *current.parent_hash();
ancestor = load_header(ancestor_hash)?;
}

Ok(uncles)
}

fn changes_trie_config(&self) -> Result<Option<ChangesTrieConfiguration>, Error> {
Ok(self.backend.state_at(BlockId::Number(self.backend.blockchain().info()?.best_number))?
.storage(well_known_keys::CHANGES_TRIE_CONFIG)
Expand Down Expand Up @@ -1705,6 +1736,117 @@ pub(crate) mod tests {
assert_eq!(None, client.best_containing(uninserted_block.hash().clone(), None).unwrap());
}

#[test]
fn uncles_with_only_ancestors() {
// block tree:
// G -> A1 -> A2
let client = test_client::new();

// G -> A1
let a1 = client.new_block().unwrap().bake().unwrap();
client.import(BlockOrigin::Own, a1.clone()).unwrap();

// A1 -> A2
let a2 = client.new_block().unwrap().bake().unwrap();
client.import(BlockOrigin::Own, a2.clone()).unwrap();
let v: Vec<H256> = Vec::new();
assert_eq!(v, client.uncles(a2.hash(), 3).unwrap());
}

#[test]
fn uncles_with_multiple_forks() {
// block tree:
// G -> A1 -> A2 -> A3 -> A4 -> A5
// A1 -> B2 -> B3 -> B4
// B2 -> C3
// A1 -> D2
let client = test_client::new();

// G -> A1
let a1 = client.new_block().unwrap().bake().unwrap();
client.import(BlockOrigin::Own, a1.clone()).unwrap();

// A1 -> A2
let a2 = client.new_block_at(&BlockId::Hash(a1.hash())).unwrap().bake().unwrap();
client.import(BlockOrigin::Own, a2.clone()).unwrap();

// A2 -> A3
let a3 = client.new_block_at(&BlockId::Hash(a2.hash())).unwrap().bake().unwrap();
client.import(BlockOrigin::Own, a3.clone()).unwrap();

// A3 -> A4
let a4 = client.new_block_at(&BlockId::Hash(a3.hash())).unwrap().bake().unwrap();
client.import(BlockOrigin::Own, a4.clone()).unwrap();

// A4 -> A5
let a5 = client.new_block_at(&BlockId::Hash(a4.hash())).unwrap().bake().unwrap();
client.import(BlockOrigin::Own, a5.clone()).unwrap();

// A1 -> B2
let mut builder = client.new_block_at(&BlockId::Hash(a1.hash())).unwrap();
// this push is required as otherwise B2 has the same hash as A2 and won't get imported
builder.push_transfer(Transfer {
from: Keyring::Alice.to_raw_public().into(),
to: Keyring::Ferdie.to_raw_public().into(),
amount: 41,
nonce: 0,
}).unwrap();
let b2 = builder.bake().unwrap();
client.import(BlockOrigin::Own, b2.clone()).unwrap();

// B2 -> B3
let b3 = client.new_block_at(&BlockId::Hash(b2.hash())).unwrap().bake().unwrap();
client.import(BlockOrigin::Own, b3.clone()).unwrap();

// B3 -> B4
let b4 = client.new_block_at(&BlockId::Hash(b3.hash())).unwrap().bake().unwrap();
client.import(BlockOrigin::Own, b4.clone()).unwrap();

// // B2 -> C3
let mut builder = client.new_block_at(&BlockId::Hash(b2.hash())).unwrap();
// this push is required as otherwise C3 has the same hash as B3 and won't get imported
builder.push_transfer(Transfer {
from: Keyring::Alice.to_raw_public().into(),
to: Keyring::Ferdie.to_raw_public().into(),
amount: 1,
nonce: 1,
}).unwrap();
let c3 = builder.bake().unwrap();
client.import(BlockOrigin::Own, c3.clone()).unwrap();

// A1 -> D2
let mut builder = client.new_block_at(&BlockId::Hash(a1.hash())).unwrap();
// this push is required as otherwise D2 has the same hash as B2 and won't get imported
builder.push_transfer(Transfer {
from: Keyring::Alice.to_raw_public().into(),
to: Keyring::Ferdie.to_raw_public().into(),
amount: 1,
nonce: 0,
}).unwrap();
let d2 = builder.bake().unwrap();
client.import(BlockOrigin::Own, d2.clone()).unwrap();

let genesis_hash = client.info().unwrap().chain.genesis_hash;

let uncles1 = client.uncles(a4.hash(), 10).unwrap();
assert_eq!(vec![b2.hash(), d2.hash()], uncles1);

let uncles2 = client.uncles(a4.hash(), 0).unwrap();
assert_eq!(0, uncles2.len());

let uncles3 = client.uncles(a1.hash(), 10).unwrap();
assert_eq!(0, uncles3.len());

let uncles4 = client.uncles(genesis_hash, 10).unwrap();
assert_eq!(0, uncles4.len());

let uncles5 = client.uncles(d2.hash(), 10).unwrap();
assert_eq!(vec![a2.hash(), b2.hash()], uncles5);

let uncles6 = client.uncles(b3.hash(), 1).unwrap();
assert_eq!(vec![c3.hash()], uncles6);
}

#[test]
fn best_containing_with_single_chain_3_blocks() {
// block tree:
Expand Down
Loading