[WIP] Partial Transition from Writeable/Readable to Codecs (#51)
* Sample Signatures for put_enc and get_dec
* Implement put_enc and get_dec
* Implement ChainCodec in grin_chain
* Truncate src only on complete Blocks
* Truncate src only on complete Tip + Check Len
* Move BlockHeader Encoding to BlockHeaderCodec
* Define put_enc for store::Batch
* Replace BlockCodec and BlockHeaderCodec with generic BlockCodec<T>
* Implement Default for BlockCodec Manually
* Replace get_ser/put_ser with get_enc/get_dec for chain::ChainKVStore
* Remove Writeable/Readable for chain::Tip
* Add Tokio-io and Bytes to grin_p2p
* Additional Setup for Message enum + Msg{Encode,Decode} traits
* base msg ping pong encoding and test
* fill out msg-codec tests
* Implement Hand Encoding/Decoding
* msg-encode shake
* msg-encode getpeeraddr
* codec peer-addrs message, SockAddr struct wierdness
* header message codec
* msg encoding finished prelim
* Implement PeerCodec Encoding/Decoding
* Set PeerStore to use PeerCodec for Encoding/Decoding
* Add a DecIterator
* Prune PeerStore
* Replace Decoding and Encoding in handle_payload
* Prune Writeable/Readable methods in store::Store
* Remove Incomplete Frame Testing ( Not Nessesary right now )
* separate block and tx codec tests
* Refactor {Tx,Block}Codec Tests
This commit is contained in:
committed by
Ignotus Peverell
parent
a402f39633
commit
de4ebdde71
+64
-217
@@ -19,6 +19,7 @@ use bytes::{BytesMut, BigEndian, BufMut, Buf, IntoBuf};
|
||||
use num_bigint::BigUint;
|
||||
use time::Timespec;
|
||||
use time;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use core::core::{Input, Output, Proof, TxKernel, Block, BlockHeader};
|
||||
use core::core::hash::Hash;
|
||||
@@ -38,34 +39,83 @@ macro_rules! try_opt_dec {
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BlockCodec;
|
||||
/// Internal Convenience Trait
|
||||
pub trait BlockEncode: Sized {
|
||||
fn block_encode(&self, dst: &mut BytesMut) -> Result<(), io::Error>;
|
||||
}
|
||||
|
||||
impl codec::Encoder for BlockCodec {
|
||||
type Item = Block;
|
||||
/// Internal Convenience Trait
|
||||
pub trait BlockDecode: Sized {
|
||||
fn block_decode(src: &mut BytesMut) -> Result<Option<Self>, io::Error>;
|
||||
}
|
||||
|
||||
/// Decodes and encodes `Block`s and their subtypes
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BlockCodec<T: BlockDecode + BlockEncode> {
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Default for BlockCodec<T>
|
||||
where T: BlockDecode + BlockEncode
|
||||
{
|
||||
fn default() -> Self {
|
||||
BlockCodec { phantom: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> codec::Encoder for BlockCodec<T>
|
||||
where T: BlockDecode + BlockEncode
|
||||
{
|
||||
type Item = T;
|
||||
type Error = io::Error;
|
||||
fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> {
|
||||
T::block_encode(&item, dst)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> codec::Decoder for BlockCodec<T>
|
||||
where T: BlockDecode + BlockEncode
|
||||
{
|
||||
type Item = T;
|
||||
type Error = io::Error;
|
||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
// Create Temporary Buffer
|
||||
let ref mut temp = src.clone();
|
||||
let res = try_opt_dec!(T::block_decode(temp)?);
|
||||
|
||||
// If succesfull truncate src by bytes read from src;
|
||||
let diff = src.len() - temp.len();
|
||||
src.split_to(diff);
|
||||
|
||||
// Return Item
|
||||
Ok(Some(res))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl BlockEncode for Block {
|
||||
fn block_encode(&self, dst: &mut BytesMut) -> Result<(), io::Error> {
|
||||
// Put Header
|
||||
item.header.block_encode(dst)?;
|
||||
self.header.block_encode(dst)?;
|
||||
|
||||
// Put Lengths of Inputs, Outputs and Kernels in 3 u64's
|
||||
dst.reserve(24);
|
||||
dst.put_u64::<BigEndian>(item.inputs.len() as u64);
|
||||
dst.put_u64::<BigEndian>(item.outputs.len() as u64);
|
||||
dst.put_u64::<BigEndian>(item.kernels.len() as u64);
|
||||
dst.put_u64::<BigEndian>(self.inputs.len() as u64);
|
||||
dst.put_u64::<BigEndian>(self.outputs.len() as u64);
|
||||
dst.put_u64::<BigEndian>(self.kernels.len() as u64);
|
||||
|
||||
// Put Inputs
|
||||
for inp in &item.inputs {
|
||||
for inp in &self.inputs {
|
||||
inp.block_encode(dst)?;
|
||||
}
|
||||
|
||||
// Put Outputs
|
||||
for outp in &item.outputs {
|
||||
for outp in &self.outputs {
|
||||
outp.block_encode(dst)?;
|
||||
}
|
||||
|
||||
// Put TxKernels
|
||||
for proof in &item.kernels {
|
||||
for proof in &self.kernels {
|
||||
proof.block_encode(dst)?;
|
||||
}
|
||||
|
||||
@@ -73,10 +123,9 @@ impl codec::Encoder for BlockCodec {
|
||||
}
|
||||
}
|
||||
|
||||
impl codec::Decoder for BlockCodec {
|
||||
type Item = Block;
|
||||
type Error = io::Error;
|
||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
impl BlockDecode for Block {
|
||||
fn block_decode(src: &mut BytesMut) -> Result<Option<Self>, io::Error> {
|
||||
|
||||
// Get Header
|
||||
let header = try_opt_dec!(BlockHeader::block_decode(src)?);
|
||||
|
||||
@@ -129,16 +178,6 @@ impl codec::Encoder for BlockHasher {
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal Convenience Trait
|
||||
trait BlockEncode: Sized {
|
||||
fn block_encode(&self, dst: &mut BytesMut) -> Result<(), io::Error>;
|
||||
}
|
||||
|
||||
/// Internal Convenience Trait
|
||||
trait BlockDecode: Sized {
|
||||
fn block_decode(src: &mut BytesMut) -> Result<Option<Self>, io::Error>;
|
||||
}
|
||||
|
||||
impl BlockEncode for BlockHeader {
|
||||
fn block_encode(&self, dst: &mut BytesMut) -> Result<(), io::Error> {
|
||||
partial_block_encode(self, dst)?;
|
||||
@@ -462,195 +501,3 @@ impl BlockDecode for Proof {
|
||||
Ok(Some(Proof(proof_data)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_have_block_codec_roundtrip() {
|
||||
use tokio_io::codec::{Encoder, Decoder};
|
||||
|
||||
let input = Input(Commitment([1; PEDERSEN_COMMITMENT_SIZE]));
|
||||
|
||||
let output = Output {
|
||||
features: OutputFeatures::empty(),
|
||||
commit: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
|
||||
proof: RangeProof {
|
||||
proof: [1; 5134],
|
||||
plen: 5134,
|
||||
},
|
||||
};
|
||||
|
||||
let kernel = TxKernel {
|
||||
features: KernelFeatures::empty(),
|
||||
excess: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
|
||||
excess_sig: vec![1; 10],
|
||||
fee: 100,
|
||||
};
|
||||
|
||||
let block = Block {
|
||||
header: BlockHeader::default(),
|
||||
inputs: vec![input],
|
||||
outputs: vec![output],
|
||||
kernels: vec![kernel],
|
||||
};
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
let mut codec = BlockCodec {};
|
||||
codec.encode(block.clone(), &mut buf).expect("Error During Block Encoding");
|
||||
|
||||
let d_block =
|
||||
codec.decode(&mut buf).expect("Error During Block Decoding").expect("Unfinished Block");
|
||||
|
||||
assert_eq!(block.header.height, d_block.header.height);
|
||||
assert_eq!(block.header.previous, d_block.header.previous);
|
||||
assert_eq!(block.header.timestamp, d_block.header.timestamp);
|
||||
assert_eq!(block.header.cuckoo_len, d_block.header.cuckoo_len);
|
||||
assert_eq!(block.header.utxo_merkle, d_block.header.utxo_merkle);
|
||||
assert_eq!(block.header.tx_merkle, d_block.header.tx_merkle);
|
||||
assert_eq!(block.header.features, d_block.header.features);
|
||||
assert_eq!(block.header.nonce, d_block.header.nonce);
|
||||
assert_eq!(block.header.pow, d_block.header.pow);
|
||||
assert_eq!(block.header.difficulty, d_block.header.difficulty);
|
||||
assert_eq!(block.header.total_difficulty,
|
||||
d_block.header.total_difficulty);
|
||||
|
||||
assert_eq!(block.inputs[0].commitment(), d_block.inputs[0].commitment());
|
||||
|
||||
assert_eq!(block.outputs[0].features, d_block.outputs[0].features);
|
||||
assert_eq!(block.outputs[0].proof().as_ref(),
|
||||
d_block.outputs[0].proof().as_ref());
|
||||
assert_eq!(block.outputs[0].commitment(),
|
||||
d_block.outputs[0].commitment());
|
||||
|
||||
assert_eq!(block.kernels[0].features, d_block.kernels[0].features);
|
||||
assert_eq!(block.kernels[0].excess, d_block.kernels[0].excess);
|
||||
assert_eq!(block.kernels[0].excess_sig, d_block.kernels[0].excess_sig);
|
||||
assert_eq!(block.kernels[0].fee, d_block.kernels[0].fee);
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_encode_and_decode_blockheader() {
|
||||
|
||||
let block_header = BlockHeader::default();
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
block_header.block_encode(&mut buf);
|
||||
|
||||
let d_block_header = BlockHeader::block_decode(&mut buf).unwrap().unwrap();
|
||||
|
||||
assert_eq!(block_header.height, d_block_header.height);
|
||||
assert_eq!(block_header.previous, d_block_header.previous);
|
||||
assert_eq!(block_header.timestamp, d_block_header.timestamp);
|
||||
assert_eq!(block_header.cuckoo_len, d_block_header.cuckoo_len);
|
||||
assert_eq!(block_header.utxo_merkle, d_block_header.utxo_merkle);
|
||||
assert_eq!(block_header.tx_merkle, d_block_header.tx_merkle);
|
||||
assert_eq!(block_header.features, d_block_header.features);
|
||||
assert_eq!(block_header.nonce, d_block_header.nonce);
|
||||
assert_eq!(block_header.pow, d_block_header.pow);
|
||||
assert_eq!(block_header.difficulty, d_block_header.difficulty);
|
||||
assert_eq!(block_header.total_difficulty,
|
||||
d_block_header.total_difficulty);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_encode_and_decode_input() {
|
||||
let input = Input(Commitment([1; PEDERSEN_COMMITMENT_SIZE]));
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
input.block_encode(&mut buf);
|
||||
|
||||
assert_eq!([1; PEDERSEN_COMMITMENT_SIZE].as_ref(), buf);
|
||||
assert_eq!(input.commitment(),
|
||||
Input::block_decode(&mut buf)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.commitment());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_encode_and_decode_output() {
|
||||
let output = Output {
|
||||
features: OutputFeatures::empty(),
|
||||
commit: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
|
||||
proof: RangeProof {
|
||||
proof: [1; 5134],
|
||||
plen: 5134,
|
||||
},
|
||||
};
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
output.block_encode(&mut buf);
|
||||
|
||||
let d_output = Output::block_decode(&mut buf).unwrap().unwrap();
|
||||
|
||||
assert_eq!(output.features, d_output.features);
|
||||
assert_eq!(output.proof().as_ref(), d_output.proof().as_ref());
|
||||
assert_eq!(output.commitment(), d_output.commitment());
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_encode_and_decode_txkernel() {
|
||||
|
||||
let kernel = TxKernel {
|
||||
features: KernelFeatures::empty(),
|
||||
excess: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
|
||||
excess_sig: vec![1; 10],
|
||||
fee: 100,
|
||||
};
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
kernel.block_encode(&mut buf);
|
||||
|
||||
let d_kernel = TxKernel::block_decode(&mut buf).unwrap().unwrap();
|
||||
|
||||
assert_eq!(kernel.features, d_kernel.features);
|
||||
assert_eq!(kernel.excess, d_kernel.excess);
|
||||
assert_eq!(kernel.excess_sig, d_kernel.excess_sig);
|
||||
assert_eq!(kernel.fee, d_kernel.fee);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_encode_and_decode_difficulty() {
|
||||
|
||||
let difficulty = Difficulty::from_num(1000);
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
difficulty.block_encode(&mut buf);
|
||||
|
||||
let d_difficulty = Difficulty::block_decode(&mut buf).unwrap().unwrap();
|
||||
|
||||
assert_eq!(difficulty, d_difficulty);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_encode_and_decode_hash() {
|
||||
|
||||
let hash = Hash([1u8; 32]);
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
hash.block_encode(&mut buf);
|
||||
|
||||
let d_hash = Hash::block_decode(&mut buf).unwrap().unwrap();
|
||||
|
||||
assert_eq!(hash, d_hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_encode_and_decode_proof() {
|
||||
|
||||
let proof = Proof::zero();
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
proof.block_encode(&mut buf);
|
||||
|
||||
let d_proof = Proof::block_decode(&mut buf).unwrap().unwrap();
|
||||
|
||||
assert_eq!(proof, d_proof);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
// 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.
|
||||
|
||||
use std::io;
|
||||
|
||||
use tokio_io::*;
|
||||
use bytes::{BytesMut, BigEndian, BufMut, Buf, IntoBuf};
|
||||
use num_bigint::BigUint;
|
||||
use time::Timespec;
|
||||
use time;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use core::core::{Input, Output, Proof, TxKernel, Block, BlockHeader};
|
||||
use core::core::hash::Hash;
|
||||
use core::core::target::Difficulty;
|
||||
use core::core::transaction::{OutputFeatures, KernelFeatures};
|
||||
use core::core::block::BlockFeatures;
|
||||
use core::consensus::PROOFSIZE;
|
||||
|
||||
use secp::pedersen::{RangeProof, Commitment};
|
||||
use secp::constants::PEDERSEN_COMMITMENT_SIZE;
|
||||
|
||||
use super::block::*;
|
||||
|
||||
#[test]
|
||||
fn should_have_block_codec_roundtrip() {
|
||||
use tokio_io::codec::{Encoder, Decoder};
|
||||
|
||||
let input = Input(Commitment([1; PEDERSEN_COMMITMENT_SIZE]));
|
||||
|
||||
let output = Output {
|
||||
features: OutputFeatures::empty(),
|
||||
commit: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
|
||||
proof: RangeProof {
|
||||
proof: [1; 5134],
|
||||
plen: 5134,
|
||||
},
|
||||
};
|
||||
|
||||
let kernel = TxKernel {
|
||||
features: KernelFeatures::empty(),
|
||||
excess: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
|
||||
excess_sig: vec![1; 10],
|
||||
fee: 100,
|
||||
};
|
||||
|
||||
let block = Block {
|
||||
header: BlockHeader::default(),
|
||||
inputs: vec![input],
|
||||
outputs: vec![output],
|
||||
kernels: vec![kernel],
|
||||
};
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
let mut codec = BlockCodec::default();
|
||||
codec
|
||||
.encode(block.clone(), &mut buf)
|
||||
.expect("Error During Block Encoding");
|
||||
|
||||
let d_block = codec
|
||||
.decode(&mut buf)
|
||||
.expect("Error During Block Decoding")
|
||||
.expect("Unfinished Block");
|
||||
|
||||
// Check if all bytes are read
|
||||
assert_eq!(buf.len(), 0);
|
||||
assert_eq!(block, d_block);
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_have_block_header_codec_roundtrip() {
|
||||
use tokio_io::codec::{Encoder, Decoder};
|
||||
|
||||
let mut codec = BlockCodec::default();
|
||||
let block_header = BlockHeader::default();
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
codec.encode(block_header.clone(), &mut buf);
|
||||
|
||||
let d_block_header = codec.decode(&mut buf).unwrap().unwrap();
|
||||
|
||||
assert_eq!(block_header, d_block_header);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_encode_and_decode_input() {
|
||||
let input = Input(Commitment([1; PEDERSEN_COMMITMENT_SIZE]));
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
input.block_encode(&mut buf);
|
||||
|
||||
let d_input = Input::block_decode(&mut buf).unwrap().unwrap();
|
||||
|
||||
assert_eq!(input,d_input)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_encode_and_decode_output() {
|
||||
let output = Output {
|
||||
features: OutputFeatures::empty(),
|
||||
commit: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
|
||||
proof: RangeProof {
|
||||
proof: [1; 5134],
|
||||
plen: 5134,
|
||||
},
|
||||
};
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
output.block_encode(&mut buf);
|
||||
|
||||
let d_output = Output::block_decode(&mut buf).unwrap().unwrap();
|
||||
|
||||
assert_eq!(output, d_output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_encode_and_decode_txkernel() {
|
||||
|
||||
let kernel = TxKernel {
|
||||
features: KernelFeatures::empty(),
|
||||
excess: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
|
||||
excess_sig: vec![1; 10],
|
||||
fee: 100,
|
||||
};
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
kernel.block_encode(&mut buf);
|
||||
|
||||
let d_kernel = TxKernel::block_decode(&mut buf).unwrap().unwrap();
|
||||
|
||||
assert_eq!(kernel, d_kernel);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_encode_and_decode_difficulty() {
|
||||
|
||||
let difficulty = Difficulty::from_num(1000);
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
difficulty.block_encode(&mut buf);
|
||||
|
||||
let d_difficulty = Difficulty::block_decode(&mut buf).unwrap().unwrap();
|
||||
|
||||
assert_eq!(difficulty, d_difficulty);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_encode_and_decode_hash() {
|
||||
|
||||
let hash = Hash([1u8; 32]);
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
hash.block_encode(&mut buf);
|
||||
|
||||
let d_hash = Hash::block_decode(&mut buf).unwrap().unwrap();
|
||||
|
||||
assert_eq!(hash, d_hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_encode_and_decode_proof() {
|
||||
|
||||
let proof = Proof::zero();
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
proof.block_encode(&mut buf);
|
||||
|
||||
let d_proof = Proof::block_decode(&mut buf).unwrap().unwrap();
|
||||
|
||||
assert_eq!(proof, d_proof);
|
||||
}
|
||||
@@ -12,9 +12,15 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Codecs for Blocks and Transactions
|
||||
|
||||
pub mod block;
|
||||
pub mod tx;
|
||||
|
||||
pub use self::block::{BlockCodec, BlockHasher};
|
||||
pub use self::tx::TxCodec;
|
||||
#[cfg(test)]
|
||||
mod block_test;
|
||||
#[cfg(test)]
|
||||
mod tx_test;
|
||||
|
||||
pub use self::block::{BlockCodec, BlockHasher };
|
||||
pub use self::tx::TxCodec;
|
||||
|
||||
+5
-85
@@ -31,7 +31,8 @@ macro_rules! try_opt_dec {
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// Decodes and encodes `Transaction`s
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct TxCodec;
|
||||
|
||||
impl codec::Encoder for TxCodec {
|
||||
@@ -121,12 +122,12 @@ impl codec::Decoder for TxCodec {
|
||||
}
|
||||
|
||||
/// Internal Convenience Trait
|
||||
trait TxEncode: Sized {
|
||||
pub trait TxEncode: Sized {
|
||||
fn tx_encode(&self, dst: &mut BytesMut) -> Result<(), io::Error>;
|
||||
}
|
||||
|
||||
/// Internal Convenience Trait
|
||||
trait TxDecode: Sized {
|
||||
pub trait TxDecode: Sized {
|
||||
fn tx_decode(src: &mut BytesMut) -> Result<Option<Self>, io::Error>;
|
||||
}
|
||||
|
||||
@@ -187,85 +188,4 @@ impl TxDecode for Input {
|
||||
|
||||
Ok(Some(Input(Commitment(c))))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_have_tx_codec_roundtrip() {
|
||||
use tokio_io::codec::{Encoder, Decoder};
|
||||
|
||||
let input = Input(Commitment([1; PEDERSEN_COMMITMENT_SIZE]));
|
||||
|
||||
let output = Output {
|
||||
features: OutputFeatures::empty(),
|
||||
commit: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
|
||||
proof: RangeProof {
|
||||
proof: [1; 5134],
|
||||
plen: 5134,
|
||||
},
|
||||
};
|
||||
|
||||
let tx = Transaction {
|
||||
inputs: vec![input],
|
||||
outputs: vec![output],
|
||||
fee: 0,
|
||||
excess_sig: vec![0; 10],
|
||||
};
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
let mut codec = TxCodec {};
|
||||
codec.encode(tx.clone(), &mut buf).expect("Error During Transaction Encoding");
|
||||
|
||||
let d_tx = codec.decode(&mut buf)
|
||||
.expect("Error During Transaction Decoding")
|
||||
.expect("Unfinished Transaction");
|
||||
|
||||
assert_eq!(tx.inputs[0].commitment(), d_tx.inputs[0].commitment());
|
||||
assert_eq!(tx.outputs[0].features, d_tx.outputs[0].features);
|
||||
assert_eq!(tx.fee, d_tx.fee);
|
||||
assert_eq!(tx.excess_sig, d_tx.excess_sig);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_encode_and_decode_output() {
|
||||
let output = Output {
|
||||
features: OutputFeatures::empty(),
|
||||
commit: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
|
||||
proof: RangeProof {
|
||||
proof: [1; 5134],
|
||||
plen: 5134,
|
||||
},
|
||||
};
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
output.tx_encode(&mut buf);
|
||||
|
||||
let d_output = Output::tx_decode(&mut buf).unwrap().unwrap();
|
||||
|
||||
assert_eq!(output.features, d_output.features);
|
||||
assert_eq!(output.proof().as_ref(), d_output.proof().as_ref());
|
||||
assert_eq!(output.commitment(), d_output.commitment());
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_encode_and_decode_input() {
|
||||
let input = Input(Commitment([1; PEDERSEN_COMMITMENT_SIZE]));
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
input.tx_encode(&mut buf);
|
||||
|
||||
assert_eq!([1; PEDERSEN_COMMITMENT_SIZE].as_ref(), buf);
|
||||
assert_eq!(input.commitment(),
|
||||
Input::tx_decode(&mut buf)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.commitment());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// 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.
|
||||
|
||||
use std::io;
|
||||
|
||||
use tokio_io::*;
|
||||
use bytes::{BytesMut, BigEndian, BufMut, Buf, IntoBuf};
|
||||
|
||||
use core::core::{Input, Output, Transaction};
|
||||
use core::core::transaction::OutputFeatures;
|
||||
|
||||
use secp::pedersen::{RangeProof, Commitment};
|
||||
use secp::constants::PEDERSEN_COMMITMENT_SIZE;
|
||||
|
||||
use super::tx::*;
|
||||
|
||||
#[test]
|
||||
fn should_have_tx_codec_roundtrip() {
|
||||
use tokio_io::codec::{Encoder, Decoder};
|
||||
|
||||
let input = Input(Commitment([1; PEDERSEN_COMMITMENT_SIZE]));
|
||||
|
||||
let output = Output {
|
||||
features: OutputFeatures::empty(),
|
||||
commit: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
|
||||
proof: RangeProof {
|
||||
proof: [1; 5134],
|
||||
plen: 5134,
|
||||
},
|
||||
};
|
||||
|
||||
let tx = Transaction {
|
||||
inputs: vec![input],
|
||||
outputs: vec![output],
|
||||
fee: 0,
|
||||
excess_sig: vec![0; 10],
|
||||
};
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
let mut codec = TxCodec {};
|
||||
codec
|
||||
.encode(tx.clone(), &mut buf)
|
||||
.expect("Error During Transaction Encoding");
|
||||
|
||||
let d_tx = codec
|
||||
.decode(&mut buf)
|
||||
.expect("Error During Transaction Decoding")
|
||||
.expect("Unfinished Transaction");
|
||||
|
||||
assert_eq!(tx, d_tx);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_encode_and_decode_output() {
|
||||
let output = Output {
|
||||
features: OutputFeatures::empty(),
|
||||
commit: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
|
||||
proof: RangeProof {
|
||||
proof: [1; 5134],
|
||||
plen: 5134,
|
||||
},
|
||||
};
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
output.tx_encode(&mut buf);
|
||||
|
||||
let d_output = Output::tx_decode(&mut buf).unwrap().unwrap();
|
||||
|
||||
assert_eq!(output, d_output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_encode_and_decode_input() {
|
||||
let input = Input(Commitment([1; PEDERSEN_COMMITMENT_SIZE]));
|
||||
|
||||
let mut buf = BytesMut::with_capacity(0);
|
||||
input.tx_encode(&mut buf);
|
||||
|
||||
let d_input = Input::tx_decode(&mut buf).unwrap().unwrap();
|
||||
|
||||
assert_eq!(input, d_input);
|
||||
}
|
||||
+55
-66
@@ -35,13 +35,16 @@ use std::fmt;
|
||||
use std::iter::Iterator;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::RwLock;
|
||||
use tokio_io::codec::{Encoder,Decoder};
|
||||
use bytes::BytesMut;
|
||||
use bytes::buf::{FromBuf, IntoBuf};
|
||||
|
||||
use byteorder::{WriteBytesExt, BigEndian};
|
||||
use rocksdb::{DB, WriteBatch, DBCompactionStyle, DBIterator, IteratorMode, Direction};
|
||||
|
||||
use core::ser;
|
||||
|
||||
mod codec;
|
||||
pub mod codec;
|
||||
use codec::{BlockCodec, BlockHasher, TxCodec};
|
||||
|
||||
/// Main error type for this crate.
|
||||
@@ -54,6 +57,8 @@ pub enum Error {
|
||||
RocksDbErr(String),
|
||||
/// Wraps a serialization error for Writeable or Readable
|
||||
SerErr(ser::Error),
|
||||
/// Wraps an Io Error
|
||||
Io(std::io::Error),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
@@ -62,6 +67,7 @@ impl fmt::Display for Error {
|
||||
&Error::NotFoundErr => write!(f, "Not Found"),
|
||||
&Error::RocksDbErr(ref s) => write!(f, "RocksDb Error: {}", s),
|
||||
&Error::SerErr(ref e) => write!(f, "Serialization Error: {}", e.to_string()),
|
||||
&Error::Io(ref e) => write!(f, "Codec Error: {}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,6 +78,12 @@ impl From<rocksdb::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for Error {
|
||||
fn from(e: std::io::Error) -> Error {
|
||||
Error::Io(e)
|
||||
}
|
||||
}
|
||||
|
||||
/// Thread-safe rocksdb wrapper
|
||||
pub struct Store {
|
||||
rdb: RwLock<DB>,
|
||||
@@ -98,13 +110,25 @@ impl Store {
|
||||
db.put(key, &value[..]).map_err(&From::from)
|
||||
}
|
||||
|
||||
/// Writes a single key and its `Writeable` value to the db. Encapsulates
|
||||
/// serialization.
|
||||
pub fn put_ser<W: ser::Writeable>(&self, key: &[u8], value: &W) -> Result<(), Error> {
|
||||
let ser_value = ser::ser_vec(value);
|
||||
match ser_value {
|
||||
Ok(data) => self.put(key, data),
|
||||
Err(err) => Err(Error::SerErr(err)),
|
||||
/// Writes a single key and a value using a given encoder.
|
||||
pub fn put_enc<E: Encoder>(&self, encoder: &mut E, key: &[u8], value: E::Item) -> Result<(), Error>
|
||||
where Error: From<E::Error> {
|
||||
|
||||
let mut data = BytesMut::with_capacity(0);
|
||||
encoder.encode(value, &mut data)?;
|
||||
self.put(key, data.to_vec())
|
||||
}
|
||||
|
||||
/// Gets a value from the db, provided its key and corresponding decoder
|
||||
pub fn get_dec<D: Decoder>(&self, decoder: &mut D, key: &[u8]) -> Result<Option<D::Item>, Error>
|
||||
where Error: From<D::Error> {
|
||||
|
||||
let data = self.get(key)?;
|
||||
if let Some(buf) = data {
|
||||
let mut buf = BytesMut::from_buf(buf);
|
||||
decoder.decode(&mut buf).map_err(From::from)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,30 +138,6 @@ impl Store {
|
||||
db.get(key).map(|r| r.map(|o| o.to_vec())).map_err(From::from)
|
||||
}
|
||||
|
||||
/// Gets a `Readable` value from the db, provided its key. Encapsulates
|
||||
/// serialization.
|
||||
pub fn get_ser<T: ser::Readable>(&self, key: &[u8]) -> Result<Option<T>, Error> {
|
||||
self.get_ser_limited(key, 0)
|
||||
}
|
||||
|
||||
/// Gets a `Readable` value from the db, provided its key, allowing to
|
||||
/// extract only partial data. The underlying Readable size must align
|
||||
/// accordingly. Encapsulates serialization.
|
||||
pub fn get_ser_limited<T: ser::Readable>(&self,
|
||||
key: &[u8],
|
||||
len: usize)
|
||||
-> Result<Option<T>, Error> {
|
||||
let data = try!(self.get(key));
|
||||
match data {
|
||||
Some(val) => {
|
||||
let mut lval = if len > 0 { &val[..len] } else { &val[..] };
|
||||
let r = try!(ser::deserialize(&mut lval).map_err(Error::SerErr));
|
||||
Ok(Some(r))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the provided key exists
|
||||
pub fn exists(&self, key: &[u8]) -> Result<bool, Error> {
|
||||
let db = self.rdb.read().unwrap();
|
||||
@@ -150,17 +150,15 @@ impl Store {
|
||||
db.delete(key).map_err(From::from)
|
||||
}
|
||||
|
||||
/// Produces an iterator of `Readable` types moving forward from the
|
||||
/// provided
|
||||
/// key.
|
||||
pub fn iter<T: ser::Readable>(&self, from: &[u8]) -> SerIterator<T> {
|
||||
/// Produces an iterator of items decoded by a decoder moving forward from the provided key.
|
||||
pub fn iter_dec<D: Decoder>(&self, codec: D, from: &[u8]) -> DecIterator<D> {
|
||||
let db = self.rdb.read().unwrap();
|
||||
SerIterator {
|
||||
DecIterator {
|
||||
iter: db.iterator(IteratorMode::From(from, Direction::Forward)),
|
||||
_marker: PhantomData,
|
||||
codec: codec
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Builds a new batch to be used with this store.
|
||||
pub fn batch(&self) -> Batch {
|
||||
Batch {
|
||||
@@ -182,17 +180,13 @@ pub struct Batch<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Batch<'a> {
|
||||
/// Writes a single key and its `Writeable` value to the batch. The write
|
||||
/// function must be called to "commit" the batch to storage.
|
||||
pub fn put_ser<W: ser::Writeable>(mut self, key: &[u8], value: &W) -> Result<Batch<'a>, Error> {
|
||||
let ser_value = ser::ser_vec(value);
|
||||
match ser_value {
|
||||
Ok(data) => {
|
||||
self.batch.put(key, &data[..])?;
|
||||
Ok(self)
|
||||
}
|
||||
Err(err) => Err(Error::SerErr(err)),
|
||||
}
|
||||
|
||||
/// Using a given encoder, Writes a single key and a value to the batch.
|
||||
pub fn put_enc<E: Encoder>(mut self, encoder: &mut E, key: &[u8], value: E::Item) -> Result<Batch<'a>, Error> where Error: From<E::Error> {
|
||||
let mut data = BytesMut::with_capacity(0);
|
||||
encoder.encode(value, &mut data)?;
|
||||
self.batch.put(key, &data)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Writes the batch to RocksDb.
|
||||
@@ -201,29 +195,24 @@ impl<'a> Batch<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator thad produces Readable instances back. Wraps the lower level
|
||||
/// DBIterator and deserializes the returned values.
|
||||
pub struct SerIterator<T>
|
||||
where T: ser::Readable
|
||||
{
|
||||
/// An iterator that produces items from a `DBIterator` instance with a given `Decoder`.
|
||||
/// Iterates and decodes returned values
|
||||
pub struct DecIterator<D> where D: Decoder {
|
||||
iter: DBIterator,
|
||||
_marker: PhantomData<T>,
|
||||
codec: D
|
||||
}
|
||||
|
||||
impl<T> Iterator for SerIterator<T>
|
||||
where T: ser::Readable
|
||||
{
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<T> {
|
||||
impl <D> Iterator for DecIterator<D> where D: Decoder {
|
||||
type Item = D::Item;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let next = self.iter.next();
|
||||
next.and_then(|r| {
|
||||
let (_, v) = r;
|
||||
ser::deserialize(&mut &v[..]).ok()
|
||||
})
|
||||
next.and_then(|(_, v)| {
|
||||
self.codec.decode(&mut BytesMut::from(v.as_ref())).ok()
|
||||
}).unwrap_or(None)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Build a db key from a prefix and a byte vector identifier.
|
||||
pub fn to_key(prefix: u8, k: &mut Vec<u8>) -> Vec<u8> {
|
||||
let mut res = Vec::with_capacity(k.len() + 2);
|
||||
|
||||
Reference in New Issue
Block a user