Merge pull request #39 from paritytech/nv4

Commitments tree storing
This commit is contained in:
Nikolay Volf 2019-01-18 15:37:13 +03:00 committed by GitHub
commit 04052c6e43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 143 additions and 10 deletions

View File

@ -24,7 +24,7 @@ use storage::{
BlockRef, Error, BlockHeaderProvider, BlockProvider, BlockOrigin, TransactionMeta, IndexedBlockProvider,
TransactionMetaProvider, TransactionProvider, TransactionOutputProvider, BlockChain, Store,
SideChainOrigin, ForkChain, Forkable, CanonStore, ConfigStore, BestBlock, NullifierTracker, Nullifier,
EpochTag,
EpochTag, RegularTreeState, TreeStateProvider,
};
const KEY_BEST_BLOCK_NUMBER: &'static str = "best_block_number";
@ -205,20 +205,41 @@ impl<T> BlockChainDatabase<T> where T: KeyValueDatabase {
return Ok(())
}
let parent_hash = block.header.raw.previous_header_hash.clone();
if !self.contains_block(parent_hash.clone().into()) && !parent_hash.is_zero() {
let parent_hash = block.header.raw.previous_header_hash;
if !self.contains_block(parent_hash.into()) && !parent_hash.is_zero() {
return Err(Error::UnknownParent);
}
let mut tree_state = if parent_hash.is_zero() {
RegularTreeState::new()
} else {
self.tree_at_block(&parent_hash)
.expect(&format!("Corrupted database - no root for block {}", parent_hash))
};
let mut update = DBTransaction::new();
update.insert(KeyValue::BlockHeader(block.hash().clone(), block.header.raw));
let tx_hashes = block.transactions.iter().map(|tx| tx.hash.clone()).collect::<Vec<_>>();
update.insert(KeyValue::BlockTransactions(block.header.hash.clone(), List::from(tx_hashes)));
update.insert(KeyValue::BlockHeader(*block.hash(), block.header.raw));
let tx_hashes = block.transactions.iter().map(|tx| tx.hash).collect::<Vec<_>>();
update.insert(KeyValue::BlockTransactions(block.header.hash, List::from(tx_hashes)));
for tx in block.transactions.into_iter() {
if let Some(ref js) = tx.raw.join_split {
for js_descriptor in js.descriptions.iter() {
for commitment in &js_descriptor.commitments[..] {
tree_state.append(H256::from(&commitment[..]))
.expect("Appending to a full commitment tree in the block insertion")
}
}
}
update.insert(KeyValue::Transaction(tx.hash, tx.raw));
}
let tree_root = tree_state.root();
update.insert(KeyValue::BlockRoot(block.header.hash, tree_root));
update.insert(KeyValue::TreeState(tree_root, tree_state));
self.db.write(update).map_err(Error::DatabaseError)
}
@ -576,6 +597,16 @@ impl<T> NullifierTracker for BlockChainDatabase<T> where T: KeyValueDatabase {
}
}
impl<T> TreeStateProvider for BlockChainDatabase<T> where T: KeyValueDatabase {
fn tree_at(&self, root: &H256) -> Option<RegularTreeState> {
self.get(Key::TreeRoot(*root)).and_then(Value::as_tree_state)
}
fn block_root(&self, block_hash: &H256) -> Option<H256> {
self.get(Key::BlockRoot(*block_hash)).and_then(Value::as_block_root)
}
}
impl<T> BlockChain for BlockChainDatabase<T> where T: KeyValueDatabase {
fn insert(&self, block: IndexedBlock) -> Result<(), Error> {
BlockChainDatabase::insert(self, block)

View File

@ -7,12 +7,13 @@ use bytes::Bytes;
use ser::List;
use chain::{Transaction as ChainTransaction, BlockHeader};
use kv::{Transaction, Key, KeyState, Operation, Value, KeyValueDatabase, KeyValue};
use storage::{TransactionMeta, EpochTag};
use storage::{TransactionMeta, EpochTag, RegularTreeState, Nullifier};
#[derive(Default, Debug)]
struct InnerDatabase {
meta: HashMap<&'static str, KeyState<Bytes>>,
block_hash: HashMap<u32, KeyState<H256>>,
block_root: HashMap<H256, KeyState<H256>>,
block_header: HashMap<H256, KeyState<BlockHeader>>,
block_transactions: HashMap<H256, KeyState<List<H256>>>,
transaction: HashMap<H256, KeyState<ChainTransaction>>,
@ -21,6 +22,7 @@ struct InnerDatabase {
configuration: HashMap<&'static str, KeyState<Bytes>>,
sprout_nullifiers: HashMap<H256, KeyState<()>>,
sapling_nullifiers: HashMap<H256, KeyState<()>>,
tree_state: HashMap<H256, KeyState<RegularTreeState>>,
}
#[derive(Default, Debug)]
@ -55,6 +57,28 @@ impl MemoryDatabase {
let configuration = replace(&mut db.configuration, HashMap::default()).into_iter()
.flat_map(|(key, state)| state.into_operation(key, KeyValue::Configuration, Key::Configuration));
let sprout_nullifiers = replace(&mut db.sprout_nullifiers, HashMap::default()).into_iter()
.flat_map(|(key, state)|
state.into_operation(key,
|k, _| KeyValue::Nullifier(Nullifier::new(EpochTag::Sprout, k)),
|h| Key::Nullifier(Nullifier::new(EpochTag::Sprout, h))
)
);
let sapling_nullifiers = replace(&mut db.sapling_nullifiers, HashMap::default()).into_iter()
.flat_map(|(key, state)|
state.into_operation(key,
|k, _| KeyValue::Nullifier(Nullifier::new(EpochTag::Sapling, k)),
|h| Key::Nullifier(Nullifier::new(EpochTag::Sapling, h))
)
);
let tree_state = replace(&mut db.tree_state, HashMap::default()).into_iter()
.flat_map(|(key, state)| state.into_operation(key, KeyValue::TreeState, Key::TreeRoot));
let block_root = replace(&mut db.block_root, HashMap::default()).into_iter()
.flat_map(|(key, state)| state.into_operation(key, KeyValue::BlockRoot, Key::BlockRoot));
Transaction {
operations: meta
.chain(block_hash)
@ -64,6 +88,10 @@ impl MemoryDatabase {
.chain(transaction_meta)
.chain(block_number)
.chain(configuration)
.chain(tree_state)
.chain(block_root)
.chain(sprout_nullifiers)
.chain(sapling_nullifiers)
.collect()
}
}
@ -87,6 +115,8 @@ impl KeyValueDatabase for MemoryDatabase {
EpochTag::Sprout => { db.sprout_nullifiers.insert(*key.hash(), KeyState::Insert(())); },
EpochTag::Sapling => { db.sapling_nullifiers.insert(*key.hash(), KeyState::Insert(())); },
},
KeyValue::TreeState(key, value) => { db.tree_state.insert(key, KeyState::Insert(value)); },
KeyValue::BlockRoot(key, value) => { db.block_root.insert(key, KeyState::Insert(value)); },
},
Operation::Delete(delete) => match delete {
Key::Meta(key) => { db.meta.insert(key, KeyState::Delete); }
@ -101,6 +131,8 @@ impl KeyValueDatabase for MemoryDatabase {
EpochTag::Sprout => { db.sprout_nullifiers.insert(*key.hash(), KeyState::Delete); },
EpochTag::Sapling => { db.sapling_nullifiers.insert(*key.hash(), KeyState::Delete); },
},
Key::TreeRoot(key) => { db.tree_state.insert(key, KeyState::Delete); },
Key::BlockRoot(key) => { db.block_root.insert(key, KeyState::Delete); },
},
}
}
@ -121,7 +153,9 @@ impl KeyValueDatabase for MemoryDatabase {
Key::Nullifier(ref key) => match key.tag() {
EpochTag::Sprout => db.sprout_nullifiers.get(key.hash()).cloned().unwrap_or_default().map(|_| Value::Empty),
EpochTag::Sapling => db.sapling_nullifiers.get(key.hash()).cloned().unwrap_or_default().map(|_| Value::Empty),
}
},
Key::TreeRoot(ref key) => db.tree_state.get(key).cloned().unwrap_or_default().map(Value::TreeState),
Key::BlockRoot(ref key) => db.block_root.get(key).cloned().unwrap_or_default().map(Value::TreeRoot),
};
Ok(result)

View File

@ -2,7 +2,7 @@ use bytes::Bytes;
use hash::H256;
use ser::{serialize, List, deserialize};
use chain::{Transaction as ChainTransaction, BlockHeader};
use storage::{TransactionMeta, Nullifier, EpochTag};
use storage::{TransactionMeta, Nullifier, EpochTag, RegularTreeState};
pub const COL_COUNT: u32 = 16;
pub const COL_META: u32 = 0;
@ -14,7 +14,9 @@ pub const COL_TRANSACTIONS_META: u32 = 5;
pub const COL_BLOCK_NUMBERS: u32 = 6;
pub const COL_SPROUT_NULLIFIERS: u32 = 7;
pub const COL_SAPLING_NULLIFIERS: u32 = 8;
pub const COL_CONFIGURATION: u32 = 9;
pub const COL_TREESTATES: u32 = 9;
pub const COL_BLOCK_ROOTS: u32 = 10;
pub const COL_CONFIGURATION: u32 = 11;
#[derive(Debug)]
pub enum Operation {
@ -33,6 +35,8 @@ pub enum KeyValue {
BlockNumber(H256, u32),
Configuration(&'static str, Bytes),
Nullifier(Nullifier),
TreeState(H256, RegularTreeState),
BlockRoot(H256, H256),
}
#[derive(Debug)]
@ -46,6 +50,8 @@ pub enum Key {
BlockNumber(H256),
Configuration(&'static str),
Nullifier(Nullifier),
TreeRoot(H256),
BlockRoot(H256),
}
#[derive(Debug, Clone)]
@ -59,6 +65,8 @@ pub enum Value {
BlockNumber(u32),
Configuration(Bytes),
Empty,
TreeState(RegularTreeState),
TreeRoot(H256),
}
impl Value {
@ -73,6 +81,8 @@ impl Value {
Key::BlockNumber(_) => deserialize(bytes).map(Value::BlockNumber),
Key::Configuration(_) => deserialize(bytes).map(Value::Configuration),
Key::Nullifier(_) => Ok(Value::Empty),
Key::TreeRoot(_) => deserialize(bytes).map(Value::TreeState),
Key::BlockRoot(_) => deserialize(bytes).map(Value::TreeRoot),
}.map_err(|e| format!("{:?}", e))
}
@ -131,6 +141,20 @@ impl Value {
_ => None,
}
}
pub fn as_tree_state(self) -> Option<RegularTreeState> {
match self {
Value::TreeState(tree) => Some(tree),
_ => None,
}
}
pub fn as_block_root(self) -> Option<H256> {
match self {
Value::TreeRoot(v) => Some(v),
_ => None,
}
}
}
#[derive(Debug, Clone)]
@ -237,6 +261,8 @@ impl<'a> From<&'a KeyValue> for RawKeyValue {
EpochTag::Sapling => (COL_SAPLING_NULLIFIERS, serialize(key.hash()), Bytes::new()),
},
KeyValue::BlockNumber(ref key, ref value) => (COL_BLOCK_NUMBERS, serialize(key), serialize(value)),
KeyValue::TreeState(ref key, ref value) => (COL_TREESTATES, serialize(key), serialize(value)),
KeyValue::BlockRoot(ref key, ref value) => (COL_BLOCK_ROOTS, serialize(key), serialize(value)),
KeyValue::Configuration(ref key, ref value) => (COL_CONFIGURATION, serialize(key), serialize(value)),
};
@ -275,7 +301,9 @@ impl<'a> From<&'a Key> for RawKey {
EpochTag::Sprout => (COL_SPROUT_NULLIFIERS, serialize(key.hash())),
EpochTag::Sapling => (COL_SAPLING_NULLIFIERS, serialize(key.hash())),
},
Key::TreeRoot(ref key) => (COL_TREESTATES, serialize(key)),
Key::BlockNumber(ref key) => (COL_BLOCK_NUMBERS, serialize(key)),
Key::BlockRoot(ref key) => (COL_BLOCK_ROOTS, serialize(key)),
Key::Configuration(ref key) => (COL_CONFIGURATION, serialize(key)),
};

View File

@ -27,6 +27,7 @@ mod transaction_meta;
mod transaction_provider;
mod nullifier;
mod tree_state;
mod tree_state_provider;
pub use primitives::{hash, bytes};
@ -44,6 +45,7 @@ pub use transaction_meta::TransactionMeta;
pub use transaction_provider::{TransactionProvider, TransactionOutputProvider, TransactionMetaProvider};
pub use nullifier::{Nullifier, NullifierTracker};
pub use tree_state::{TreeState, H32 as H32TreeDim, Dim as TreeDim, RegularTreeState};
pub use tree_state_provider::TreeStateProvider;
/// Epoch tag.
///

View File

@ -76,12 +76,14 @@ pub trait Dim {
const HEIGHT: usize;
}
#[derive(Clone, Debug, PartialEq)]
pub struct H32;
impl Dim for H32 {
const HEIGHT: usize = 32;
}
#[derive(Clone, Debug, PartialEq)]
pub struct TreeState<D: Dim> {
_phantom: ::std::marker::PhantomData<D>,
left: Option<H256>,
@ -145,6 +147,10 @@ impl<D: Dim> TreeState<D> {
root
}
pub fn empty_root() -> H256 {
EMPTY_ROOTS[D::HEIGHT]
}
}
pub type RegularTreeState = TreeState<H32>;
@ -206,6 +212,25 @@ mod tests {
);
}
#[test]
fn empty_32_root() {
assert_eq!(
RegularTreeState::new().root(),
H256::from("ac58cd1388fec290d398f1944b564449a63c815880566bd1d189f7839e3b0c8c"),
)
}
#[test]
fn appended_1_32_root() {
let mut tree = RegularTreeState::new();
tree.append(H256::from("bab6e8992959caf0ca94847c36b4e648a7f88a9b9c6a62ea387cf1fb9badfd62"))
.expect("failed to append to the tree");
assert_eq!(
tree.root(),
H256::from("af3a29c548af2d8314544875fe0a59555bfda3c81ea78da54bd02f89cce68acb")
);
}
#[test]
fn single_elem_in_double_tree() {
let mut tree = TreeState::<H2>::new();

View File

@ -0,0 +1,13 @@
use hash::H256;
use bytes::Bytes;
use RegularTreeState;
pub trait TreeStateProvider {
fn tree_at(&self, root: &H256) -> Option<RegularTreeState>;
fn block_root(&self, block_hash: &H256) -> Option<H256>;
fn tree_at_block(&self, block_hash: &H256) -> Option<RegularTreeState> {
self.block_root(block_hash).and_then(|h| self.tree_at(&h))
}
}