Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 81 additions & 44 deletions smite-ir/src/operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use std::fmt::Write;
use bitcoin::{opcodes::all as opcodes, script::Builder, script::PushBytes};
use rand::{Rng, RngExt};
use serde::{Deserialize, Serialize};
use smite::bolt::ShortChannelId;
use smite::bolt::{FeatureBit, Features, ShortChannelId};

use super::VariableType;

Expand Down Expand Up @@ -520,56 +520,93 @@ impl ChannelTypeVariant {

/// The feature bits (even/required) contained in this channel type.
#[must_use]
pub fn bits(self) -> &'static [usize] {
// BOLT 9 feature bits:
// 12 = option_static_remotekey
// 22 = option_anchors
// 40 = zero_fee_commitments
// 46 = option_scid_alias
// 50 = option_zeroconf
// 80 = option_simple_taproot
// 180 = option_simple_taproot_staging
// 2022 = option_script_enforced_lease
pub fn bits(self) -> &'static [FeatureBit] {
use Features as F;
match self {
Self::StaticRemoteKey => &[12],
Self::StaticRemoteKeyScidAlias => &[12, 46],
Self::StaticRemoteKeyZeroConf => &[12, 50],
Self::StaticRemoteKeyScidAliasZeroConf => &[12, 46, 50],
Self::Anchors => &[12, 22],
Self::AnchorsScidAlias => &[12, 22, 46],
Self::AnchorsZeroConf => &[12, 22, 50],
Self::AnchorsScidAliasZeroConf => &[12, 22, 46, 50],
Self::ZeroFeeCommitments => &[40],
Self::ZeroFeeCommitmentsScidAlias => &[40, 46],
Self::ZeroFeeCommitmentsZeroConf => &[40, 50],
Self::ZeroFeeCommitmentsScidAliasZeroConf => &[40, 46, 50],
Self::SimpleTaproot => &[80],
Self::SimpleTaprootScidAlias => &[80, 46],
Self::SimpleTaprootZeroConf => &[80, 50],
Self::SimpleTaprootScidAliasZeroConf => &[80, 46, 50],
Self::SimpleTaprootStaging => &[180],
Self::SimpleTaprootStagingScidAlias => &[180, 46],
Self::SimpleTaprootStagingZeroConf => &[180, 50],
Self::SimpleTaprootStagingScidAliasZeroConf => &[180, 46, 50],
Self::ScriptEnforcedLease => &[12, 22, 2022],
Self::ScriptEnforcedLeaseScidAlias => &[12, 22, 2022, 46],
Self::ScriptEnforcedLeaseZeroConf => &[12, 22, 2022, 50],
Self::ScriptEnforcedLeaseScidAliasZeroConf => &[12, 22, 2022, 46, 50],
Self::StaticRemoteKey => &[F::OPTION_STATIC_REMOTEKEY],
Self::StaticRemoteKeyScidAlias => &[F::OPTION_STATIC_REMOTEKEY, F::OPTION_SCID_ALIAS],
Self::StaticRemoteKeyZeroConf => &[F::OPTION_STATIC_REMOTEKEY, F::OPTION_ZEROCONF],
Self::StaticRemoteKeyScidAliasZeroConf => &[
F::OPTION_STATIC_REMOTEKEY,
F::OPTION_SCID_ALIAS,
F::OPTION_ZEROCONF,
],
Self::Anchors => &[F::OPTION_STATIC_REMOTEKEY, F::OPTION_ANCHORS],
Self::AnchorsScidAlias => &[
F::OPTION_STATIC_REMOTEKEY,
F::OPTION_ANCHORS,
F::OPTION_SCID_ALIAS,
],
Self::AnchorsZeroConf => &[
F::OPTION_STATIC_REMOTEKEY,
F::OPTION_ANCHORS,
F::OPTION_ZEROCONF,
],
Self::AnchorsScidAliasZeroConf => &[
F::OPTION_STATIC_REMOTEKEY,
F::OPTION_ANCHORS,
F::OPTION_SCID_ALIAS,
F::OPTION_ZEROCONF,
],
Self::ZeroFeeCommitments => &[F::ZERO_FEE_COMMITMENTS],
Self::ZeroFeeCommitmentsScidAlias => &[F::ZERO_FEE_COMMITMENTS, F::OPTION_SCID_ALIAS],
Self::ZeroFeeCommitmentsZeroConf => &[F::ZERO_FEE_COMMITMENTS, F::OPTION_ZEROCONF],
Self::ZeroFeeCommitmentsScidAliasZeroConf => &[
F::ZERO_FEE_COMMITMENTS,
F::OPTION_SCID_ALIAS,
F::OPTION_ZEROCONF,
],
Self::SimpleTaproot => &[F::OPTION_SIMPLE_TAPROOT],
Self::SimpleTaprootScidAlias => &[F::OPTION_SIMPLE_TAPROOT, F::OPTION_SCID_ALIAS],
Self::SimpleTaprootZeroConf => &[F::OPTION_SIMPLE_TAPROOT, F::OPTION_ZEROCONF],
Self::SimpleTaprootScidAliasZeroConf => &[
F::OPTION_SIMPLE_TAPROOT,
F::OPTION_SCID_ALIAS,
F::OPTION_ZEROCONF,
],
Self::SimpleTaprootStaging => &[F::OPTION_SIMPLE_TAPROOT_STAGING],
Self::SimpleTaprootStagingScidAlias => {
&[F::OPTION_SIMPLE_TAPROOT_STAGING, F::OPTION_SCID_ALIAS]
}
Self::SimpleTaprootStagingZeroConf => {
&[F::OPTION_SIMPLE_TAPROOT_STAGING, F::OPTION_ZEROCONF]
}
Self::SimpleTaprootStagingScidAliasZeroConf => &[
F::OPTION_SIMPLE_TAPROOT_STAGING,
F::OPTION_SCID_ALIAS,
F::OPTION_ZEROCONF,
],
Self::ScriptEnforcedLease => &[
F::OPTION_STATIC_REMOTEKEY,
F::OPTION_ANCHORS,
F::OPTION_SCRIPT_ENFORCED_LEASE,
],
Self::ScriptEnforcedLeaseScidAlias => &[
F::OPTION_STATIC_REMOTEKEY,
F::OPTION_ANCHORS,
F::OPTION_SCRIPT_ENFORCED_LEASE,
F::OPTION_SCID_ALIAS,
],
Self::ScriptEnforcedLeaseZeroConf => &[
F::OPTION_STATIC_REMOTEKEY,
F::OPTION_ANCHORS,
F::OPTION_SCRIPT_ENFORCED_LEASE,
F::OPTION_ZEROCONF,
],
Self::ScriptEnforcedLeaseScidAliasZeroConf => &[
F::OPTION_STATIC_REMOTEKEY,
F::OPTION_ANCHORS,
F::OPTION_SCRIPT_ENFORCED_LEASE,
F::OPTION_SCID_ALIAS,
F::OPTION_ZEROCONF,
],
}
}

/// Encodes the channel type as a BOLT feature bitmap (big-endian bytes).
#[must_use]
#[allow(clippy::missing_panics_doc)] // bits() is always non-empty
pub fn encode(self) -> Vec<u8> {
let bits = self.bits();
let max_bit = *bits.iter().max().expect("non-empty bits");
let num_bytes = max_bit / 8 + 1;
let mut out = vec![0u8; num_bytes];
for &bit in bits {
out[num_bytes - 1 - bit / 8] |= 1 << (bit % 8);
}
out
Features::from_bits(self.bits()).into_bytes()
}
}

Expand Down
6 changes: 3 additions & 3 deletions smite-scenarios/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use bitcoin::{OutPoint, ScriptBuf, Txid};
use smite::bitcoin::{BitcoinCli, TxBlockPosition, Utxo};
use smite::bolt::{
AcceptChannel, AnnouncementSignatures, ChannelAnnouncement, ChannelId, ChannelReady,
ChannelReadyTlvs, ChannelUpdate, FundingCreated, FundingSigned, Message, NodeAnnouncement,
OpenChannel, OpenChannelTlvs, Pong, ShortChannelId, Shutdown, msg_type,
ChannelReadyTlvs, ChannelUpdate, Features, FundingCreated, FundingSigned, Message,
NodeAnnouncement, OpenChannel, OpenChannelTlvs, Pong, ShortChannelId, Shutdown, msg_type,
};
use smite::channel_tx::{
ChannelConfig, ChannelPartyConfig, ChannelState, FundingTransaction, HolderIdentity, Side,
Expand Down Expand Up @@ -932,7 +932,7 @@ fn build_funding_created(
let config = ChannelConfig {
funding_outpoint,
funding_satoshis: open_channel.funding_satoshis,
channel_type: open_channel.tlvs.channel_type.clone().unwrap_or_default(),
channel_type: Features::from(open_channel.tlvs.channel_type.clone().unwrap_or_default()),
opener,
acceptor,
minimum_depth: accept_channel.minimum_depth,
Expand Down
61 changes: 23 additions & 38 deletions smite-scenarios/src/scenarios/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use std::time::Duration;

use smite::bolt::{Init, InitTlvs, Message};
use smite::bolt::{FeatureBit, Features, Init, InitTlvs, Message};
use smite::noise::NoiseConnection;
use smite::scenarios::ScenarioError;

Expand Down Expand Up @@ -30,49 +30,34 @@ pub trait SnapshotSetup<T: Target> {
fn setup(target: &T) -> Result<(NoiseConnection, ProgramContext), ScenarioError>;
}

/// Clears a feature bit from a feature vector.
///
/// Feature vectors are encoded as big-endian byte arrays where bit N lives in
/// byte `features[len - 1 - N/8]` at position `N % 8`.
fn clear_feature_bit(features: &mut [u8], bit: usize) {
let byte_index = features.len().checked_sub(1 + bit / 8);
if let Some(i) = byte_index {
features[i] &= !(1 << (bit % 8));
}
}

/// Gossip-related feature bits (BOLT 9): `gossip_queries` (6/7),
/// `gossip_queries_ex` (10/11). Stripped so the target doesn't send
/// `gossip_timestamp_filter` or other gossip noise during execution.
const GOSSIP_FEATURE_BITS: &[usize] = &[6, 7, 10, 11];

/// Feature bits that force a dual-funded flow when both peers support them:
/// `option_dual_fund` (28/29). Eclair in particular will not allow
/// single-funded flows if either of these feature bits is set, so we strip them
/// when fuzzing the single-funded flow.
const DUAL_FUNDING_FEATURE_BITS: &[usize] = &[28, 29];

/// Peer storage feature bits: `option_provide_storage` (42/43). When enabled,
/// peers may send `peer_storage` and `peer_storage_retrieval` messages at
/// arbitrary times. Disabling these bits eliminates peer storage noise.
const PEER_STORAGE_FEATURE_BITS: &[usize] = &[42, 43];
/// Features stripped from our echoed `init` so the target stays on the single
/// funded flow and doesn't emit unrelated noise:
/// - `gossip_queries` (6/7), `gossip_queries_ex` (10/11): Stripped so the
/// target doesn't send `gossip_timestamp_filter` or other gossip noise during
/// execution.
/// - `option_dual_fund` (28/29): Eclair in particular will not allow
/// single-funded flows if either of these feature bits is set.
/// - `option_provide_storage` (42/43): When enabled, peers may send
/// `peer_storage` and `peer_storage_retrieval` messages at arbitrary times.
const STRIPPED_FEATURES: &[FeatureBit] = &[
Features::GOSSIP_QUERIES,
Features::GOSSIP_QUERIES_EX,
Features::OPTION_DUAL_FUND,
Features::OPTION_PROVIDE_STORAGE,
];

/// Creates an `init` that echoes the received features with bits stripped that
/// would steer the target away from the single-funded `open_channel` flow.
fn init_for_single_funded(received: &Init) -> Init {
let mut globalfeatures = received.globalfeatures.clone();
let mut features = received.features.clone();
for &bit in GOSSIP_FEATURE_BITS
.iter()
.chain(DUAL_FUNDING_FEATURE_BITS)
.chain(PEER_STORAGE_FEATURE_BITS)
{
clear_feature_bit(&mut globalfeatures, bit);
clear_feature_bit(&mut features, bit);
let mut globalfeatures = Features::from(received.globalfeatures.clone());
let mut features = Features::from(received.features.clone());
for &bit in STRIPPED_FEATURES {
globalfeatures.clear_feature(bit);
features.clear_feature(bit);
}
Init {
globalfeatures,
features,
globalfeatures: globalfeatures.into_bytes(),
features: features.into_bytes(),
tlvs: InitTlvs::default(),
}
}
Expand Down
2 changes: 2 additions & 0 deletions smite/src/bolt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ mod closing_complete;
mod closing_sig;
mod commitment_signed;
mod error;
mod features;
mod funding_created;
mod funding_signed;
mod gossip_timestamp_filter;
Expand Down Expand Up @@ -52,6 +53,7 @@ pub use closing_complete::{ClosingComplete, ClosingTlvs};
pub use closing_sig::ClosingSig;
pub use commitment_signed::{CommitmentSigned, CommitmentSignedTlvs};
pub use error::Error;
pub use features::{FeatureBit, Features};
pub use funding_created::FundingCreated;
pub use funding_signed::FundingSigned;
pub use gossip_timestamp_filter::GossipTimestampFilter;
Expand Down
Loading