mirror of
https://github.com/jwinterm/grin-wallet.git
synced 2026-11-13 19:13:54 +01:00
validate contract participant count
This commit is contained in:
@@ -39,7 +39,6 @@ Separating side effects until the 'save_step' part would make these functions mu
|
||||
|
||||
- Reserve funds across open late-locked contracts, or make the possible signing failure
|
||||
clearer to callers.
|
||||
- Keep contracts at two participants until the known multi-party attack is addressed.
|
||||
- For a payjoin, `--use-inputs any` picks an input for us; naming the commitment would be
|
||||
clearer, for the API as much as the CLI.
|
||||
- `new` has no `--no-setup`.
|
||||
|
||||
@@ -38,6 +38,7 @@ where
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
contract::utils::verify_num_participants(setup_args.num_participants)?;
|
||||
// Compute state for 'new'
|
||||
let (slate, context) = compute(w, keychain_mask, setup_args, slate_id)?;
|
||||
|
||||
|
||||
@@ -35,6 +35,13 @@ where
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
contract::utils::verify_num_participants(slate.num_participants)?;
|
||||
if slate.num_participants != setup_args.num_participants {
|
||||
return Err(Error::GenericError(format!(
|
||||
"Inconsistent num_participants. Slate num_participants:{}, Setup num_participants: {}",
|
||||
slate.num_participants, setup_args.num_participants
|
||||
)));
|
||||
}
|
||||
contract::utils::verify_not_signed(w, slate.id)?;
|
||||
// Compute state for 'setup'
|
||||
let (slate, context) = compute(w, keychain_mask, slate, setup_args)?;
|
||||
|
||||
@@ -58,6 +58,7 @@ where
|
||||
C: NodeClient,
|
||||
K: Keychain,
|
||||
{
|
||||
contract::utils::verify_num_participants(slate.num_participants)?;
|
||||
// Compute if we will add outputs at this step. Only a missing context means we
|
||||
// still need to add them; propagate any other error.
|
||||
let existing_context = match w.get_private_context(keychain_mask, slate.id.as_bytes()) {
|
||||
|
||||
@@ -75,13 +75,7 @@ where
|
||||
"Cannot view a slate with an Unknown state".to_string(),
|
||||
));
|
||||
}
|
||||
// Mirror the contract setup bound so a tampered slate can't surface a bogus count.
|
||||
if slate.num_participants < 1 || slate.num_participants > 2 {
|
||||
return Err(Error::GenericError(format!(
|
||||
"Unsupported num_participants: {} (expected 1 or 2)",
|
||||
slate.num_participants
|
||||
)));
|
||||
}
|
||||
contract::utils::verify_num_participants(slate.num_participants)?;
|
||||
let context = match w.get_private_context(keychain_mask, slate.id.as_bytes()) {
|
||||
Ok(context) => Some(context),
|
||||
Err(Error::NotFoundErr(_)) => None,
|
||||
|
||||
@@ -79,17 +79,7 @@ where
|
||||
K: Keychain,
|
||||
{
|
||||
debug!("Creating a new contract context");
|
||||
// Validate num_participants before any fee math (kernel cost is divided by it, so 0
|
||||
// would be a divide-by-zero). This is the common setup path for every party (the
|
||||
// signer's value comes from the slate), so it also guards against a tampered slate.
|
||||
// Only 1 (self-spend) and 2 (standard) are implemented today; lift this when
|
||||
// multi-party contracts are supported.
|
||||
if setup_args.num_participants < 1 || setup_args.num_participants > 2 {
|
||||
return Err(Error::GenericError(format!(
|
||||
"Unsupported num_participants: {} (expected 1 or 2)",
|
||||
setup_args.num_participants
|
||||
)));
|
||||
}
|
||||
contract_utils::verify_num_participants(setup_args.num_participants)?;
|
||||
// sender should always refresh outputs
|
||||
updater::refresh_outputs(w, keychain_mask, parent_key_id, false)?;
|
||||
|
||||
|
||||
@@ -346,6 +346,7 @@ pub fn my_fee_contribution(
|
||||
n_kernels: usize,
|
||||
num_participants: u8,
|
||||
) -> Result<FeeFields, Error> {
|
||||
verify_num_participants(num_participants)?;
|
||||
// Add our fee costs for our inputs and a single output
|
||||
let mut fee = tx_fee(n_inputs, n_outputs, 0);
|
||||
// Add out fee costs for kernel. We pay 1/num_participants of a kernel cost
|
||||
@@ -362,6 +363,18 @@ pub fn my_fee_contribution(
|
||||
Ok(my_fee_fields)
|
||||
}
|
||||
|
||||
/// Contracts need at least one participant for fee splitting
|
||||
/// More than two remain disabled because of the known multi-party attack
|
||||
pub(super) fn verify_num_participants(num_participants: u8) -> Result<(), Error> {
|
||||
if !(1..=2).contains(&num_participants) {
|
||||
return Err(Error::GenericError(format!(
|
||||
"Unsupported num_participants: {} (expected 1 or 2)",
|
||||
num_participants
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Whether a transaction log entry shows that this wallet signed the slate.
|
||||
pub(super) fn is_signed_tx(tx: &TxLogEntry, slate_id: Uuid) -> bool {
|
||||
tx.tx_slate_id == Some(slate_id) && tx.kernel_excess.is_some()
|
||||
@@ -469,4 +482,22 @@ mod tests {
|
||||
// ... and applying the same split, the participants together cover the kernel cost.
|
||||
assert!(2 * my_share >= kernel_cost);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn participant_count() {
|
||||
assert!(verify_num_participants(1).is_ok());
|
||||
assert!(verify_num_participants(2).is_ok());
|
||||
for count in [0, 3] {
|
||||
let err = verify_num_participants(count).unwrap_err();
|
||||
assert!(matches!(
|
||||
err,
|
||||
Error::GenericError(ref msg)
|
||||
if msg == &format!(
|
||||
"Unsupported num_participants: {} (expected 1 or 2)",
|
||||
count
|
||||
)
|
||||
));
|
||||
assert!(my_fee_contribution(1, 1, 1, count).is_err());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user