2016-10-22 21:35:48 +03:00
|
|
|
// Copyright 2016 The Grin Developers
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2016-10-21 03:06:12 +03:00
|
|
|
//! Implements storage primitives required by the chain
|
|
|
|
|
2017-06-19 18:59:56 +03:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
2017-04-20 21:26:53 +03:00
|
|
|
use secp::pedersen::Commitment;
|
|
|
|
|
2016-10-21 03:06:12 +03:00
|
|
|
use types::*;
|
2017-02-08 00:50:01 +03:00
|
|
|
use core::core::hash::{Hash, Hashed};
|
2017-04-20 21:26:53 +03:00
|
|
|
use core::core::{Block, BlockHeader, Output};
|
2017-06-19 18:59:56 +03:00
|
|
|
use core::consensus::TargetError;
|
|
|
|
use core::core::target::Difficulty;
|
2017-02-09 22:41:46 +03:00
|
|
|
use grin_store::{self, Error, to_key, u64_to_key, option_to_not_found};
|
2016-10-21 03:06:12 +03:00
|
|
|
|
2016-11-30 05:45:39 +03:00
|
|
|
const STORE_SUBPATH: &'static str = "chain";
|
2016-10-21 03:06:12 +03:00
|
|
|
|
2016-11-17 04:03:23 +03:00
|
|
|
const BLOCK_HEADER_PREFIX: u8 = 'h' as u8;
|
|
|
|
const BLOCK_PREFIX: u8 = 'b' as u8;
|
2016-10-21 03:06:12 +03:00
|
|
|
const HEAD_PREFIX: u8 = 'H' as u8;
|
2017-02-08 00:50:01 +03:00
|
|
|
const HEADER_HEAD_PREFIX: u8 = 'I' as u8;
|
|
|
|
const HEADER_HEIGHT_PREFIX: u8 = '8' as u8;
|
2017-04-20 21:26:53 +03:00
|
|
|
const OUTPUT_COMMIT_PREFIX: u8 = 'o' as u8;
|
2017-08-29 19:32:45 +03:00
|
|
|
const HEADER_BY_OUTPUT_PREFIX: u8 = 'p' as u8;
|
2017-09-28 02:46:32 +03:00
|
|
|
const COMMIT_POS_PREFIX: u8 = 'c' as u8;
|
|
|
|
const KERNEL_POS_PREFIX: u8 = 'k' as u8;
|
2016-10-21 03:06:12 +03:00
|
|
|
|
|
|
|
/// An implementation of the ChainStore trait backed by a simple key-value
|
|
|
|
/// store.
|
|
|
|
pub struct ChainKVStore {
|
|
|
|
db: grin_store::Store,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ChainKVStore {
|
2017-08-10 03:54:10 +03:00
|
|
|
/// Create new chain store
|
2016-11-30 05:45:39 +03:00
|
|
|
pub fn new(root_path: String) -> Result<ChainKVStore, Error> {
|
2017-02-09 22:41:46 +03:00
|
|
|
let db = grin_store::Store::open(format!("{}/{}", root_path, STORE_SUBPATH).as_str())?;
|
2016-10-21 03:06:12 +03:00
|
|
|
Ok(ChainKVStore { db: db })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ChainStore for ChainKVStore {
|
|
|
|
fn head(&self) -> Result<Tip, Error> {
|
2017-06-01 01:47:52 +03:00
|
|
|
option_to_not_found(self.db.get_ser(&vec![HEAD_PREFIX]))
|
2016-10-21 03:06:12 +03:00
|
|
|
}
|
|
|
|
|
2016-11-30 05:45:39 +03:00
|
|
|
fn head_header(&self) -> Result<BlockHeader, Error> {
|
2017-08-29 19:32:45 +03:00
|
|
|
self.get_block_header(&try!(self.head()).last_block_h)
|
2016-11-30 05:45:39 +03:00
|
|
|
}
|
2016-11-27 23:31:15 +03:00
|
|
|
|
2017-02-08 00:50:01 +03:00
|
|
|
fn save_head(&self, t: &Tip) -> Result<(), Error> {
|
2016-11-17 04:03:23 +03:00
|
|
|
self.db
|
2017-02-08 00:50:01 +03:00
|
|
|
.batch()
|
2017-06-01 01:47:52 +03:00
|
|
|
.put_ser(&vec![HEAD_PREFIX], t)?
|
|
|
|
.put_ser(&vec![HEADER_HEAD_PREFIX], t)?
|
2017-02-08 00:50:01 +03:00
|
|
|
.write()
|
2016-11-17 04:03:23 +03:00
|
|
|
}
|
|
|
|
|
2017-04-28 07:59:53 +03:00
|
|
|
fn save_body_head(&self, t: &Tip) -> Result<(), Error> {
|
2017-06-01 01:47:52 +03:00
|
|
|
self.db.put_ser(&vec![HEAD_PREFIX], t)
|
2017-04-28 07:59:53 +03:00
|
|
|
}
|
|
|
|
|
2017-02-08 00:50:01 +03:00
|
|
|
fn get_header_head(&self) -> Result<Tip, Error> {
|
2017-06-01 01:47:52 +03:00
|
|
|
option_to_not_found(self.db.get_ser(&vec![HEADER_HEAD_PREFIX]))
|
2017-02-08 00:50:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn save_header_head(&self, t: &Tip) -> Result<(), Error> {
|
2017-06-01 01:47:52 +03:00
|
|
|
self.db.put_ser(&vec![HEADER_HEAD_PREFIX], t)
|
2017-02-08 00:50:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get_block(&self, h: &Hash) -> Result<Block, Error> {
|
2017-06-01 01:47:52 +03:00
|
|
|
option_to_not_found(self.db.get_ser(&to_key(BLOCK_PREFIX, &mut h.to_vec())))
|
2017-02-08 00:50:01 +03:00
|
|
|
}
|
|
|
|
|
2016-11-17 04:03:23 +03:00
|
|
|
fn get_block_header(&self, h: &Hash) -> Result<BlockHeader, Error> {
|
2017-08-29 19:32:45 +03:00
|
|
|
option_to_not_found(self.db.get_ser(&to_key(BLOCK_HEADER_PREFIX, &mut h.to_vec())))
|
2016-10-21 03:06:12 +03:00
|
|
|
}
|
|
|
|
|
2017-04-28 07:59:53 +03:00
|
|
|
fn check_block_exists(&self, h: &Hash) -> Result<bool, Error> {
|
|
|
|
self.db.exists(&to_key(BLOCK_PREFIX, &mut h.to_vec()))
|
|
|
|
}
|
|
|
|
|
2017-02-08 00:50:01 +03:00
|
|
|
fn save_block(&self, b: &Block) -> Result<(), Error> {
|
2017-04-20 21:26:53 +03:00
|
|
|
// saving the block and its header
|
|
|
|
let mut batch = self.db
|
2017-02-08 00:50:01 +03:00
|
|
|
.batch()
|
2017-06-01 01:47:52 +03:00
|
|
|
.put_ser(&to_key(BLOCK_PREFIX, &mut b.hash().to_vec())[..], b)?
|
2017-08-29 19:32:45 +03:00
|
|
|
.put_ser(&to_key(BLOCK_HEADER_PREFIX, &mut b.hash().to_vec())[..], &b.header)?;
|
2017-04-20 21:26:53 +03:00
|
|
|
|
|
|
|
// saving the full output under its hash, as well as a commitment to hash index
|
|
|
|
for out in &b.outputs {
|
2017-08-29 19:32:45 +03:00
|
|
|
batch = batch
|
|
|
|
.put_ser(&to_key(OUTPUT_COMMIT_PREFIX, &mut out.commitment().as_ref().to_vec())[..], out)?
|
|
|
|
.put_ser(&to_key(HEADER_BY_OUTPUT_PREFIX, &mut out.commitment().as_ref().to_vec())[..], &b.hash())?;
|
2017-04-20 21:26:53 +03:00
|
|
|
}
|
|
|
|
batch.write()
|
2017-02-08 00:50:01 +03:00
|
|
|
}
|
|
|
|
|
2017-08-29 19:32:45 +03:00
|
|
|
// lookup the block header hash by output commitment
|
|
|
|
// lookup the block header based on this hash
|
|
|
|
// to check the chain is correct compare this block header to
|
|
|
|
// the block header currently indexed at the relevant block height (tbd if actually necessary)
|
|
|
|
//
|
|
|
|
// NOTE: This index is not exhaustive.
|
|
|
|
// This node may not have seen this full block, so may not have populated the index.
|
|
|
|
// Block headers older than some threshold (2 months?) will not necessarily be included
|
|
|
|
// in this index.
|
|
|
|
//
|
|
|
|
fn get_block_header_by_output_commit(&self, commit: &Commitment) -> Result<BlockHeader, Error> {
|
|
|
|
let block_hash = self.db.get_ser(&to_key(
|
|
|
|
HEADER_BY_OUTPUT_PREFIX,
|
|
|
|
&mut commit.as_ref().to_vec(),
|
|
|
|
))?;
|
|
|
|
|
|
|
|
match block_hash {
|
|
|
|
Some(hash) => {
|
|
|
|
let block_header = self.get_block_header(&hash)?;
|
|
|
|
let header_at_height = self.get_header_by_height(block_header.height)?;
|
|
|
|
if block_header.hash() == header_at_height.hash() {
|
|
|
|
Ok(block_header)
|
|
|
|
} else {
|
|
|
|
Err(Error::NotFoundErr)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
None => Err(Error::NotFoundErr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-08 00:50:01 +03:00
|
|
|
fn save_block_header(&self, bh: &BlockHeader) -> Result<(), Error> {
|
2017-08-29 19:32:45 +03:00
|
|
|
self.db.put_ser(&to_key(BLOCK_HEADER_PREFIX, &mut bh.hash().to_vec())[..], bh)
|
2017-02-08 00:50:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get_header_by_height(&self, height: u64) -> Result<BlockHeader, Error> {
|
2017-06-01 01:47:52 +03:00
|
|
|
option_to_not_found(self.db.get_ser(&u64_to_key(HEADER_HEIGHT_PREFIX, height)))
|
2017-02-08 00:50:01 +03:00
|
|
|
}
|
|
|
|
|
2017-05-29 06:21:29 +03:00
|
|
|
fn get_output_by_commit(&self, commit: &Commitment) -> Result<Output, Error> {
|
2017-07-28 00:13:34 +03:00
|
|
|
option_to_not_found(self.db.get_ser(&to_key(
|
|
|
|
OUTPUT_COMMIT_PREFIX,
|
|
|
|
&mut commit.as_ref().to_vec(),
|
|
|
|
)))
|
2017-04-20 21:26:53 +03:00
|
|
|
}
|
|
|
|
|
2017-09-28 02:46:32 +03:00
|
|
|
fn save_output_pos(&self, commit: &Commitment, pos: u64) -> Result<(), Error> {
|
|
|
|
self.db.put_ser(&to_key(COMMIT_POS_PREFIX, &mut commit.as_ref().to_vec())[..], &pos)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_output_pos(&self, commit: &Commitment) -> Result<u64, Error> {
|
|
|
|
option_to_not_found(self.db.get_ser(&to_key(COMMIT_POS_PREFIX, &mut commit.as_ref().to_vec())))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn save_kernel_pos(&self, excess: &Commitment, pos: u64) -> Result<(), Error> {
|
|
|
|
self.db.put_ser(&to_key(KERNEL_POS_PREFIX, &mut excess.as_ref().to_vec())[..], &pos)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_kernel_pos(&self, excess: &Commitment) -> Result<u64, Error> {
|
|
|
|
option_to_not_found(self.db.get_ser(&to_key(KERNEL_POS_PREFIX, &mut excess.as_ref().to_vec())))
|
2017-04-20 21:26:53 +03:00
|
|
|
}
|
|
|
|
|
2017-08-29 19:32:45 +03:00
|
|
|
/// Maintain consistency of the "header_by_height" index by traversing back through the
|
|
|
|
/// current chain and updating "header_by_height" until we reach a block_header
|
|
|
|
/// that is consistent with its height (everything prior to this will be consistent)
|
2017-02-08 00:50:01 +03:00
|
|
|
fn setup_height(&self, bh: &BlockHeader) -> Result<(), Error> {
|
2017-09-28 02:46:32 +03:00
|
|
|
self.db.put_ser(&u64_to_key(HEADER_HEIGHT_PREFIX, bh.height), bh)?;
|
|
|
|
if bh.height == 0 {
|
|
|
|
return Ok(());
|
|
|
|
}
|
2017-02-08 00:50:01 +03:00
|
|
|
|
|
|
|
let mut prev_h = bh.previous;
|
|
|
|
let mut prev_height = bh.height - 1;
|
|
|
|
while prev_height > 0 {
|
|
|
|
let prev = self.get_header_by_height(prev_height)?;
|
|
|
|
if prev.hash() != prev_h {
|
|
|
|
let real_prev = self.get_block_header(&prev_h)?;
|
2017-07-28 00:13:34 +03:00
|
|
|
self.db.put_ser(
|
|
|
|
&u64_to_key(HEADER_HEIGHT_PREFIX, real_prev.height),
|
|
|
|
&real_prev,
|
2017-08-10 03:54:10 +03:00
|
|
|
).unwrap();
|
2017-02-08 00:50:01 +03:00
|
|
|
prev_h = real_prev.previous;
|
|
|
|
prev_height = real_prev.height - 1;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
2017-06-19 18:59:56 +03:00
|
|
|
|
|
|
|
/// An iterator on blocks, from latest to earliest, specialized to return
|
|
|
|
/// information pertaining to block difficulty calculation (timestamp and
|
|
|
|
/// previous difficulties). Mostly used by the consensus next difficulty
|
|
|
|
/// calculation.
|
|
|
|
pub struct DifficultyIter {
|
|
|
|
next: Hash,
|
|
|
|
store: Arc<ChainStore>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DifficultyIter {
|
2017-07-04 02:46:25 +03:00
|
|
|
/// Build a new iterator using the provided chain store and starting from
|
|
|
|
/// the provided block hash.
|
2017-06-19 18:59:56 +03:00
|
|
|
pub fn from(start: Hash, store: Arc<ChainStore>) -> DifficultyIter {
|
|
|
|
DifficultyIter {
|
|
|
|
next: start,
|
|
|
|
store: store,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Iterator for DifficultyIter {
|
2017-08-03 19:57:55 +03:00
|
|
|
type Item = Result<(u64, Difficulty), TargetError>;
|
2017-06-19 18:59:56 +03:00
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
let bhe = self.store.get_block_header(&self.next);
|
|
|
|
match bhe {
|
|
|
|
Err(e) => Some(Err(TargetError(e.to_string()))),
|
|
|
|
Ok(bh) => {
|
|
|
|
if bh.height == 0 {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
self.next = bh.previous;
|
2017-08-03 19:57:55 +03:00
|
|
|
Some(Ok((bh.timestamp.to_timespec().sec as u64, bh.difficulty)))
|
2017-06-19 18:59:56 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|