Skip to content

Commit aca2a48

Browse files
1440000bytesAsh-L2L
authored andcommitted
Reject blocks and transactions with the wrong number of authorizations
1 parent 1d318c1 commit aca2a48

3 files changed

Lines changed: 45 additions & 0 deletions

File tree

lib/state/block.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,21 @@ pub fn validate(
8484
};
8585
return Err(err);
8686
}
87+
// The authorization and address checks below pair authorizations with
88+
// spent UTXOs positionally via `zip`, which silently ignores any trailing
89+
// inputs when there are fewer authorizations than inputs requiring one.
90+
// Require an exact count so that every input requiring authorization is
91+
// covered by a provided authorization.
92+
let n_authorizations_required: usize = filled_txs
93+
.iter()
94+
.map(|t| t.spent_utxos_requiring_auth().len())
95+
.sum();
96+
if body.authorizations.len() != n_authorizations_required {
97+
return Err(Error::WrongNumberOfAuthorizations {
98+
expected: n_authorizations_required,
99+
received: body.authorizations.len(),
100+
});
101+
}
87102
let spent_utxos = filled_txs
88103
.iter()
89104
.flat_map(|t| t.spent_utxos_requiring_auth().into_iter());
@@ -168,6 +183,21 @@ pub fn prevalidate(
168183
};
169184
return Err(err);
170185
}
186+
// The authorization and address checks below pair authorizations with
187+
// spent UTXOs positionally via `zip`, which silently ignores any trailing
188+
// inputs when there are fewer authorizations than inputs requiring one.
189+
// Require an exact count so that every input requiring authorization is
190+
// covered by a provided authorization.
191+
let n_authorizations_required: usize = filled_transactions
192+
.iter()
193+
.map(|t| t.spent_utxos_requiring_auth().len())
194+
.sum();
195+
if body.authorizations.len() != n_authorizations_required {
196+
return Err(Error::WrongNumberOfAuthorizations {
197+
expected: n_authorizations_required,
198+
received: body.authorizations.len(),
199+
});
200+
}
171201
let spent_utxos = filled_transactions
172202
.iter()
173203
.flat_map(|t| t.spent_utxos_requiring_auth().into_iter());

lib/state/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,10 @@ pub enum Error {
265265
UtxoDoubleSpent,
266266
#[error(transparent)]
267267
WithdrawalBundle(#[from] WithdrawalBundleError),
268+
#[error(
269+
"wrong number of authorizations: expected {expected}, but received {received}"
270+
)]
271+
WrongNumberOfAuthorizations { expected: usize, received: usize },
268272
#[error("wrong public key for address")]
269273
WrongPubKeyForAddress,
270274
}

lib/state/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,17 @@ impl State {
484484
) -> Result<bitcoin::Amount, Error> {
485485
let filled_transaction =
486486
self.fill_transaction(rotxn, &transaction.transaction)?;
487+
// Pairing authorizations with spent UTXOs via `zip` silently ignores
488+
// trailing inputs when there are too few authorizations. Require an
489+
// exact count so that every input requiring authorization is covered.
490+
let n_authorizations_required =
491+
filled_transaction.spent_utxos_requiring_auth().len();
492+
if transaction.authorizations.len() != n_authorizations_required {
493+
return Err(Error::WrongNumberOfAuthorizations {
494+
expected: n_authorizations_required,
495+
received: transaction.authorizations.len(),
496+
});
497+
}
487498
for (authorization, spent_utxo) in transaction
488499
.authorizations
489500
.iter()

0 commit comments

Comments
 (0)