Skip to content

*_to_*_bits_toaltstack boundary guards: LE under-push for num_bits <= 1; BE over-push for num_bits = 0 #3

Description

@WindOctober

Summary

  • limb_to_le_bits_toaltstack pushes to altstack only when num_bits ≥ 2. Callers that pop num_bits items underflow for num_bits = 1.
  • limb_to_be_bits_toaltstack unconditionally pushes even when num_bits = 0, leaving an unexpected extra item on altstack.

Both violate the contract “push exactly num_bits bits to altstack,” breaking stack discipline at the edges.

Proposed Fix

LE:

pub fn limb_to_le_bits_toaltstack(num_bits: usize) -> Script {
    if num_bits >= 2 {
        script! {
            { limb_to_le_bits_common(num_bits) }
            for _ in 0..num_bits { OP_TOALTSTACK }
        }
    } else if num_bits == 1 {
        script! {
            OP_TOALTSTACK
        }
    } else {
        script! {}
    }
}

BE:

pub fn limb_to_be_bits_toaltstack(num_bits: usize) -> Script {
    if num_bits >= 2 {
        script! {
            { limb_to_be_bits_common(num_bits) }
            OP_TOALTSTACK
            OP_TOALTSTACK
        }
    } else if num_bits == 1 {
        script! {
            OP_TOALTSTACK
        }
    } else {
        script! {}
    }
}

Acknowledgements: This issue was identified using Pomelo (https://eprint.iacr.org/2024/1768) by the UCSB/Nubit team. We also acknowledge BitVM#360 for first flagging potential problems in the limb_to_le_bits* family.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions