rework block validation to check input|output|kernel sizes consistently
with tx validation
This commit is contained in:
+17
-6
@@ -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;
|
||||
@@ -107,9 +116,11 @@ 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
|
||||
let weight =
|
||||
input_len * BLOCK_INPUT_WEIGHT +
|
||||
output_len * BLOCK_OUTPUT_WEIGHT +
|
||||
kernel_len * BLOCK_KERNEL_WEIGHT;
|
||||
weight > MAX_BLOCK_WEIGHT
|
||||
}
|
||||
|
||||
/// Fork every 250,000 blocks for first 2 years, simple number and just a
|
||||
|
||||
+33
-1
@@ -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,6 +744,21 @@ 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()) {
|
||||
return Err(Error::WeightExceeded);
|
||||
|
||||
Reference in New Issue
Block a user