Merge pull request #1336 from antiochp/validate_block_size

Validate block size
This commit is contained in:
hashmap 2018-08-10 16:51:33 +02:00 committed by GitHub
commit b10609bbb1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 52 additions and 13 deletions

View file

@ -90,11 +90,20 @@ pub const MAX_BLOCK_WEIGHT: usize = 80_000;
/// Reused consistently for various max lengths below.
/// Max transaction is effectively a full block of data.
/// Soft fork down when too high.
/// Likely we will need to tweak these all individually, but using a single constant for now.
const MAX_INP_OUT_KERN_LEN: usize = 300_000;
/// Maximum inputs for a block (issue#261)
/// Hundreds of inputs + 1 output might be slow to validate (issue#258)
pub const MAX_BLOCK_INPUTS: usize = MAX_INP_OUT_KERN_LEN; // soft fork down when too_high
/// Maximum inputs for a block.
pub const MAX_BLOCK_INPUTS: usize = MAX_INP_OUT_KERN_LEN;
/// Maximum outputs for a block (max tx + single output for coinbase).
/// This is just a starting point - need to discuss this further.
pub const MAX_BLOCK_OUTPUTS: usize = MAX_INP_OUT_KERN_LEN + 1;
/// Maximum kernels for a block (max tx + single output for coinbase).
/// This is just a starting point - need to discuss this further.
pub const MAX_BLOCK_KERNELS: usize = MAX_INP_OUT_KERN_LEN + 1;
/// Maximum inputs in a transaction.
pub const MAX_TX_INPUTS: usize = MAX_INP_OUT_KERN_LEN;
@ -105,13 +114,6 @@ pub const MAX_TX_OUTPUTS: usize = MAX_INP_OUT_KERN_LEN;
/// Maximum kernels in a transaction.
pub const MAX_TX_KERNELS: usize = MAX_INP_OUT_KERN_LEN;
/// Whether a block exceeds the maximum acceptable weight
pub fn exceeds_weight(input_len: usize, output_len: usize, kernel_len: usize) -> bool {
input_len * BLOCK_INPUT_WEIGHT
+ output_len * BLOCK_OUTPUT_WEIGHT
+ kernel_len * BLOCK_KERNEL_WEIGHT > MAX_BLOCK_WEIGHT || input_len > MAX_BLOCK_INPUTS
}
/// Fork every 250,000 blocks for first 2 years, simple number and just a
/// little less than 6 months.
pub const HARD_FORK_INTERVAL: u64 = 250_000;

View file

@ -21,7 +21,7 @@ use std::collections::HashSet;
use std::fmt;
use std::iter::FromIterator;
use consensus::{self, exceeds_weight, reward, VerifySortOrder, REWARD};
use consensus::{self, reward, VerifySortOrder, REWARD};
use core::committed::{self, Committed};
use core::hash::{Hash, HashWriter, Hashed, ZERO_HASH};
use core::id::ShortIdentifiable;
@ -45,7 +45,13 @@ pub enum Error {
InvalidTotalKernelSum,
/// Same as above but for the coinbase part of a block, including reward
CoinbaseSumMismatch,
/// Too many inputs, outputs or kernels in the block
/// Restrict number of block inputs.
TooManyInputs,
/// Restrict number of block outputs.
TooManyOutputs,
/// Retrict number of block kernels.
TooManyKernels,
/// Block weight (based on inputs|outputs|kernels) exceeded.
WeightExceeded,
/// Kernel not valid due to lock_height exceeding block header height
KernelLockHeight(u64),
@ -399,6 +405,13 @@ impl Readable for Block {
let outputs = read_and_verify_sorted(reader, output_len)?;
let kernels = read_and_verify_sorted(reader, kernel_len)?;
// TODO - we do not verify the input|output|kernel counts here.
// I think should call block.validate() as part of a call to read()
// but block.validate() as it stands currently requires the previous sums etc.
// So there is no easy way to do this in isolation.
// Maybe we need two variations of validate() where one handles the validation
// rules that *can* be done in isolation.
Ok(Block {
header: header,
inputs: inputs,
@ -694,7 +707,11 @@ impl Block {
prev_kernel_offset: &BlindingFactor,
prev_kernel_sum: &Commitment,
) -> Result<(Commitment), Error> {
// Verify we do not exceed the max number of inputs|outputs|kernels
// and that the "weight" based on these does not exceed the max permitted weight.
self.verify_size()?;
self.verify_weight()?;
self.verify_sorted()?;
self.verify_cut_through()?;
self.verify_coinbase()?;
@ -727,8 +744,28 @@ impl Block {
Ok(kernel_sum)
}
// Verify the tx is not too big in terms of
// number of inputs|outputs|kernels.
fn verify_size(&self) -> Result<(), Error> {
if self.inputs.len() > consensus::MAX_BLOCK_INPUTS {
return Err(Error::TooManyInputs);
}
if self.outputs.len() > consensus::MAX_BLOCK_OUTPUTS {
return Err(Error::TooManyOutputs);
}
if self.kernels.len() > consensus::MAX_BLOCK_KERNELS {
return Err(Error::TooManyKernels);
}
Ok(())
}
fn verify_weight(&self) -> Result<(), Error> {
if exceeds_weight(self.inputs.len(), self.outputs.len(), self.kernels.len()) {
let weight =
self.inputs.len() * consensus::BLOCK_INPUT_WEIGHT +
self.outputs.len() * consensus::BLOCK_OUTPUT_WEIGHT +
self.kernels.len() * consensus::BLOCK_KERNEL_WEIGHT;
if weight > consensus::MAX_BLOCK_WEIGHT {
return Err(Error::WeightExceeded);
}
Ok(())