Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(validators): check input metadata matches spending output #3761

Merged
merged 5 commits into from
Jan 28, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
12 changes: 0 additions & 12 deletions base_layer/core/src/base_node/proto/mmr_tree.proto

This file was deleted.

50 changes: 0 additions & 50 deletions base_layer/core/src/base_node/proto/mmr_tree.rs

This file was deleted.

2 changes: 0 additions & 2 deletions base_layer/core/src/base_node/proto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
mod chain_metadata;
pub mod wallet_rpc;

#[cfg(feature = "base_node")]
mod mmr_tree;
#[cfg(feature = "base_node")]
mod request;
#[cfg(feature = "base_node")]
Expand Down
5 changes: 0 additions & 5 deletions base_layer/core/src/base_node/proto/request.proto
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
syntax = "proto3";

import "block.proto";
import "mmr_tree.proto";
import "types.proto";

package tari.base_node;
Expand Down Expand Up @@ -42,10 +41,6 @@ message FetchHeadersAfter {
bytes stopping_hash = 2;
}

message FetchMmrNodeCount {
MmrTree tree = 1;
uint64 height = 2;
}

message NewBlockTemplateRequest{
uint64 algo = 1;
Expand Down
3 changes: 1 addition & 2 deletions base_layer/core/src/base_node/proto/rpc.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ syntax = "proto3";

import "transaction.proto";
import "block.proto";
import "mmr_tree.proto";

package tari.base_node;

Expand Down Expand Up @@ -86,4 +85,4 @@ message SyncUtxosByBlockResponse {
repeated tari.types.TransactionOutput outputs = 1;
uint64 height = 2;
bytes header_hash = 3;
}
}
1 change: 0 additions & 1 deletion base_layer/core/src/base_node/proto/wallet_rpc.proto
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
syntax = "proto3";

import "google/protobuf/wrappers.proto";
import "block.proto";
import "chain_metadata.proto";
import "types.proto";
import "transaction.proto";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ impl<B: BlockchainBackend + 'static> BlockSynchronizer<B> {
Err(BlockSyncError::ValidationError(err)) => {
match &err {
ValidationError::BlockHeaderError(_) => {},
ValidationError::BlockError(BlockValidationError::MismatchedMmrRoots) |
ValidationError::BlockError(BlockValidationError::MismatchedMmrRoots { .. }) |
ValidationError::BadBlockFound { .. } |
ValidationError::BlockError(BlockValidationError::MismatchedMmrSize { .. }) => {
let num_cleared = self.db.clear_all_pending_headers().await?;
Expand Down
4 changes: 2 additions & 2 deletions base_layer/core/src/blocks/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ pub enum BlockValidationError {
InvalidInput,
#[error("Contains kernels or inputs that are not yet spendable")]
MaturityError,
#[error("Mismatched MMR roots")]
MismatchedMmrRoots,
#[error("Mismatched {kind} MMR roots")]
MismatchedMmrRoots { kind: String },
#[error("MMR size for {mmr_tree} does not match. Expected: {expected}, received: {actual}")]
MismatchedMmrSize {
mmr_tree: String,
Expand Down
3 changes: 3 additions & 0 deletions base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2020,6 +2020,9 @@ impl BlockchainBackend for LMDBDatabase {
// lmdb_len(&txn, &self.utxo)
unimplemented!("Need to get rangeproof mmr size")
},
MmrTree::Input => {
unimplemented!("Need to get input mmr size")
},
}
}

Expand Down
22 changes: 4 additions & 18 deletions base_layer/core/src/chain_storage/mmr_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,39 +20,25 @@
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use std::{
convert::TryFrom,
fmt::{Display, Error, Formatter},
};
use std::fmt::{Display, Error, Formatter};

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Copy)]
pub enum MmrTree {
Utxo,
Input,
Kernel,
Witness,
}

impl Display for MmrTree {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
match self {
MmrTree::Witness => f.write_str("Witness"),
MmrTree::Utxo => f.write_str("UTXO"),
MmrTree::Input => f.write_str("Input"),
MmrTree::Kernel => f.write_str("Kernel"),
}
}
}

impl TryFrom<i32> for MmrTree {
type Error = String;

fn try_from(v: i32) -> Result<Self, Self::Error> {
match v {
0 => Ok(MmrTree::Utxo),
1 => Ok(MmrTree::Kernel),
2 => Ok(MmrTree::Witness),
_ => Err("Invalid MmrTree".into()),
MmrTree::Witness => f.write_str("Witness"),
}
}
}
2 changes: 1 addition & 1 deletion base_layer/core/src/chain_storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ mod db_transaction;
pub use db_transaction::{DbKey, DbTransaction, DbValue, WriteOperation};

mod mmr_tree;
pub use mmr_tree::*;
pub use mmr_tree::MmrTree;

mod error;
pub use error::{ChainStorageError, Optional, OrNotFound};
Expand Down
27 changes: 27 additions & 0 deletions base_layer/core/src/common/hash_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,30 @@ impl<H: Update> Write for HashWriter<H> {
Ok(())
}
}

#[cfg(test)]
mod test {
use rand::{rngs::OsRng, RngCore};
use tari_common_types::types::HashDigest;

use super::*;

#[test]
fn it_updates_the_digest_state() {
let mut writer = HashWriter::new(HashDigest::new());
let mut data = [0u8; 1024];
OsRng.fill_bytes(&mut data);

// Even if data is streamed in chunks, the preimage and therefore the resulting hash are the same
writer.write_all(&data[0..256]).unwrap();
writer.write_all(&data[256..500]).unwrap();
writer.write_all(&data[500..1024]).unwrap();
let hash = writer.finalize();
let empty: [u8; 32] = Update::chain(HashDigest::new(), [0u8; 1024]).finalize_fixed().into();
assert_ne!(hash, empty);

let mut writer = HashWriter::new(HashDigest::new());
writer.write_all(&data).unwrap();
assert_eq!(writer.finalize(), hash);
}
}
32 changes: 32 additions & 0 deletions base_layer/core/src/consensus/consensus_encoding/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use std::{
convert::TryFrom,
io,
io::{Error, Read, Write},
ops::Deref,
Expand Down Expand Up @@ -56,6 +57,17 @@ impl<const MAX: usize> From<MaxSizeBytes<MAX>> for Vec<u8> {
}
}

impl<const MAX: usize> TryFrom<Vec<u8>> for MaxSizeBytes<MAX> {
type Error = Vec<u8>;

fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
if value.len() > MAX {
return Err(value);
}
Ok(MaxSizeBytes { inner: value })
}
}

impl<const MAX: usize> ConsensusDecoding for MaxSizeBytes<MAX> {
fn consensus_decode<R: Read>(reader: &mut R) -> Result<Self, io::Error> {
let len = reader.read_varint()?;
Expand Down Expand Up @@ -121,3 +133,23 @@ impl<const N: usize> ConsensusDecoding for [u8; N] {
Ok(buf)
}
}

#[cfg(test)]
mod test {
use rand::{rngs::OsRng, RngCore};

use super::*;
use crate::consensus::{check_consensus_encoding_correctness, ToConsensusBytes};

#[test]
fn it_encodes_and_decodes_correctly() {
let mut subject = [0u8; 1024];
OsRng.fill_bytes(&mut subject);
check_consensus_encoding_correctness(subject).unwrap();

// Get vec encoding with length byte
let encoded = subject.to_vec().to_consensus_bytes();
let decoded = MaxSizeBytes::<1024>::consensus_decode(&mut encoded.as_slice()).unwrap();
assert_eq!(*decoded, *subject.as_slice());
}
}
77 changes: 64 additions & 13 deletions base_layer/core/src/consensus/consensus_encoding/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,8 @@ impl ConsensusDecoding for Commitment {

impl ConsensusEncoding for Signature {
fn consensus_encode<W: io::Write>(&self, writer: &mut W) -> Result<usize, io::Error> {
let pub_nonce = self.get_public_nonce().as_bytes();
let mut written = pub_nonce.len();
writer.write_all(pub_nonce)?;
let sig = self.get_signature().as_bytes();
written += sig.len();
writer.write_all(sig)?;
let mut written = self.get_public_nonce().consensus_encode(writer)?;
written += self.get_signature().consensus_encode(writer)?;
Ok(written)
}
}
Expand All @@ -126,13 +122,8 @@ impl ConsensusEncodingSized for Signature {

impl ConsensusDecoding for Signature {
fn consensus_decode<R: Read>(reader: &mut R) -> Result<Self, io::Error> {
let mut buf = [0u8; 32];
reader.read_exact(&mut buf)?;
let pub_nonce =
PublicKey::from_bytes(&buf[..]).map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?;
let mut buf = [0u8; 32];
reader.read_exact(&mut buf)?;
let sig = PrivateKey::from_bytes(&buf[..]).map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?;
let pub_nonce = PublicKey::consensus_decode(reader)?;
let sig = PrivateKey::consensus_decode(reader)?;
Ok(Signature::new(pub_nonce, sig))
}
}
Expand Down Expand Up @@ -184,3 +175,63 @@ impl ConsensusDecoding for ComSignature {
Ok(ComSignature::new(nonce, u, v))
}
}

#[cfg(test)]
mod test {
use rand::{rngs::OsRng, RngCore};
use tari_crypto::range_proof::RangeProofService;

use super::*;
use crate::{consensus::check_consensus_encoding_correctness, transactions::CryptoFactories};

mod keys {
use super::*;

#[test]
fn it_encodes_and_decodes_correctly() {
let (_, subject) = PublicKey::random_keypair(&mut OsRng);
check_consensus_encoding_correctness(subject).unwrap();
let (subject, _) = PublicKey::random_keypair(&mut OsRng);
check_consensus_encoding_correctness(subject).unwrap();
}
}

mod commitment {
use super::*;

#[test]
fn it_encodes_and_decodes_correctly() {
let (_, p) = PublicKey::random_keypair(&mut OsRng);
let subject = Commitment::from_public_key(&p);
check_consensus_encoding_correctness(subject).unwrap();
}
}

mod signature {
use super::*;

#[test]
fn it_encodes_and_decodes_correctly() {
let (k, p) = PublicKey::random_keypair(&mut OsRng);
let subject = Signature::new(p, k);
check_consensus_encoding_correctness(subject).unwrap();
}
}

mod range_proof {
use super::*;

#[test]
fn it_encodes_and_decodes_correctly() {
let k = PrivateKey::random(&mut OsRng);
let subject = RangeProof::from_bytes(
&CryptoFactories::default()
.range_proof
.construct_proof(&k, OsRng.next_u64())
.unwrap(),
)
.unwrap();
check_consensus_encoding_correctness(subject).unwrap();
}
}
}
Loading