Skip to content

Commit

Permalink
Merge pull request #338 from quake/quake/shutdown-script-size
Browse files Browse the repository at this point in the history
fix: resolve shutdown script size bug
  • Loading branch information
quake authored Nov 24, 2024
2 parents 07174b5 + 5649889 commit bb8a4b1
Show file tree
Hide file tree
Showing 7 changed files with 408 additions and 303 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
tests/nodes/.ports
/coverage-report
/*.info
.vscode
346 changes: 183 additions & 163 deletions src/fiber/channel.rs

Large diffs are not rendered by default.

32 changes: 11 additions & 21 deletions src/fiber/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,19 @@ use std::{fs, path::PathBuf, str::FromStr};
use tentacle::secio::{PublicKey, SecioKeyPair};

pub const CKB_SHANNONS: u64 = 100_000_000; // 1 CKB = 10 ^ 8 shannons
pub const DEFAULT_MIN_INBOUND_LIQUIDITY: u64 = 100 * CKB_SHANNONS; // 100 CKB for minimal inbound liquidity
pub const DEFAULT_MIN_SHUTDOWN_FEE: u64 = CKB_SHANNONS; // 1 CKB prepared for shutdown transaction fee
pub const MIN_OCCUPIED_CAPACITY: u64 = 61 * CKB_SHANNONS; // 61 CKB for occupied capacity
pub const MIN_UDT_OCCUPIED_CAPACITY: u64 = 142 * CKB_SHANNONS; // 142 CKB for UDT occupied capacity
pub const DEFAULT_MIN_SHUTDOWN_FEE: u64 = 1 * CKB_SHANNONS; // 1 CKB prepared for shutdown transaction fee

/// By default, listen to any tcp port allocated by the kernel.
pub const DEFAULT_LISTENING_ADDR: &str = "/ip4/0.0.0.0/tcp/0";

/// 62 CKB minimal channel amount, at any time a partner should keep at least
/// `DEFAULT_CHANNEL_MINIMAL_CKB_AMOUNT` CKB in the channel,
/// to make sure he can build a valid shutdown transaction and pay proper fee.
pub const DEFAULT_CHANNEL_MINIMAL_CKB_AMOUNT: u64 =
MIN_OCCUPIED_CAPACITY + DEFAULT_MIN_SHUTDOWN_FEE;
const MIN_OCCUPIED_CAPACITY: u64 = 61 * CKB_SHANNONS; // 61 CKB for occupied capacity

/// 143 CKB for minimal UDT amount
pub const DEFAULT_UDT_MINIMAL_CKB_AMOUNT: u64 =
MIN_UDT_OCCUPIED_CAPACITY + DEFAULT_MIN_SHUTDOWN_FEE;
/// Default ckb funding amount when auto accepting an open channel request.
pub const DEFAULT_AUTO_ACCEPT_CHANNEL_CKB_FUNDING_AMOUNT: u64 =
MIN_OCCUPIED_CAPACITY + DEFAULT_MIN_SHUTDOWN_FEE;

/// 162 CKB to open a channel which maybe automatically acceptted.
/// 100 CKB for minimal inbound liquidity, 61 CKB for occupied capacity, 1 CKB for shutdown fee
/// The other party may auto accept the channel if the amount is greater than this.
pub const DEFAULT_CHANNEL_MIN_AUTO_CKB_AMOUNT: u64 =
DEFAULT_MIN_INBOUND_LIQUIDITY + MIN_OCCUPIED_CAPACITY + DEFAULT_MIN_SHUTDOWN_FEE;
/// Default minimum ckb funding amount for auto accepting an open channel request.
pub const DEFAULT_OPEN_CHANNEL_AUTO_ACCEPT_MIN_CKB_FUNDING_AMOUNT: u64 = 100 * CKB_SHANNONS;

/// The expiry delta to forward a tlc, in milliseconds, default to 1 day.
pub const DEFAULT_TLC_EXPIRY_DELTA: u64 = 24 * 60 * 60 * 1000;
Expand Down Expand Up @@ -113,12 +103,12 @@ pub struct FiberConfig {
#[arg(name = "FIBER_SCRIPTS", long = "fiber-scripts", env, value_parser, num_args = 0.., value_delimiter = ',')]
pub scripts: Vec<FiberScript>,

/// minimum ckb funding amount for auto accepting an open channel requests, aunit: shannons [default: 16200000000 shannons]
/// minimum ckb funding amount for auto accepting an open channel requests, unit: shannons [default: 10000000000 shannons]
#[arg(
name = "FIBER_OPEN_CHANNEL_AUTO_ACCEPT_MIN_CKB_FUNDING_AMOUNT",
long = "fiber-open-channel-auto-accept-min-ckb-funding-amount",
env,
help = "minimum ckb funding amount for auto accepting an open channel requests, unit: shannons [default: 16200000000 shannons]"
help = "minimum ckb funding amount for auto accepting an open channel requests, unit: shannons [default: 10000000000 shannons]"
)]
pub open_channel_auto_accept_min_ckb_funding_amount: Option<u64>,
/// whether to accept open channel requests with ckb funding amount automatically, unit: shannons [default: 6200000000 shannons], if this is set to zero, it means to disable auto accept
Expand Down Expand Up @@ -318,12 +308,12 @@ impl FiberConfig {

pub fn open_channel_auto_accept_min_ckb_funding_amount(&self) -> u64 {
self.open_channel_auto_accept_min_ckb_funding_amount
.unwrap_or(DEFAULT_CHANNEL_MIN_AUTO_CKB_AMOUNT)
.unwrap_or(DEFAULT_OPEN_CHANNEL_AUTO_ACCEPT_MIN_CKB_FUNDING_AMOUNT)
}

pub fn auto_accept_channel_ckb_funding_amount(&self) -> u64 {
self.auto_accept_channel_ckb_funding_amount
.unwrap_or(DEFAULT_CHANNEL_MINIMAL_CKB_AMOUNT)
.unwrap_or(DEFAULT_AUTO_ACCEPT_CHANNEL_CKB_FUNDING_AMOUNT)
}

pub fn tlc_expiry_delta(&self) -> u64 {
Expand Down
9 changes: 0 additions & 9 deletions src/fiber/fee.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use super::channel::FUNDING_CELL_WITNESS_LEN;
use super::config::{DEFAULT_CHANNEL_MINIMAL_CKB_AMOUNT, DEFAULT_UDT_MINIMAL_CKB_AMOUNT};
use crate::ckb::contracts::{get_cell_deps, get_script_by_contract, Contract};
use ckb_types::core::TransactionBuilder;
use ckb_types::packed::{Bytes, Script};
Expand All @@ -12,14 +11,6 @@ use ckb_types::{
use molecule::prelude::Entity;
use tracing::debug;

pub(crate) fn default_minimal_ckb_amount(is_udt: bool) -> u64 {
if is_udt {
DEFAULT_UDT_MINIMAL_CKB_AMOUNT
} else {
DEFAULT_CHANNEL_MINIMAL_CKB_AMOUNT
}
}

fn commitment_tx_size(udt_type_script: &Option<Script>) -> usize {
// when there is pending htlcs, the commitment lock args will be 56 bytes, otherwise 46 bytes.
// to simplify the calculation, we use hardcoded 56 bytes here.
Expand Down
162 changes: 66 additions & 96 deletions src/fiber/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,15 @@ use tokio_util::task::TaskTracker;
use tracing::{debug, error, info, trace, warn};

use super::channel::{
AcceptChannelParameter, ChannelActor, ChannelActorMessage, ChannelActorStateStore,
ChannelCommand, ChannelCommandWithId, ChannelEvent, ChannelInitializationParameter,
ChannelState, ChannelSubscribers, OpenChannelParameter, ProcessingChannelError,
ProcessingChannelResult, PublicChannelInfo, ShuttingDownFlags, DEFAULT_COMMITMENT_FEE_RATE,
DEFAULT_FEE_RATE,
get_funding_and_reserved_amount, occupied_capacity, AcceptChannelParameter, ChannelActor,
ChannelActorMessage, ChannelActorStateStore, ChannelCommand, ChannelCommandWithId,
ChannelEvent, ChannelInitializationParameter, ChannelState, ChannelSubscribers,
OpenChannelParameter, ProcessingChannelError, ProcessingChannelResult, PublicChannelInfo,
ShuttingDownFlags, DEFAULT_COMMITMENT_FEE_RATE, DEFAULT_FEE_RATE, MAX_COMMITMENT_DELAY_EPOCHS,
MIN_COMMITMENT_DELAY_EPOCHS, SYS_MAX_TLC_NUMBER_IN_FLIGHT,
};
use super::config::AnnouncedNodeName;
use super::fee::{calculate_commitment_tx_fee, default_minimal_ckb_amount};
use super::fee::calculate_commitment_tx_fee;
use super::graph::{NetworkGraph, NetworkGraphStateStore, SessionRoute};
use super::graph_syncer::{GraphSyncer, GraphSyncerMessage};
use super::key::blake2b_hash_with_salt;
Expand Down Expand Up @@ -669,8 +670,8 @@ where
is_udt_type_auto_accept(udt_type_script, open_channel.funding_amount)
} else {
state.auto_accept_channel_ckb_funding_amount > 0
&& open_channel.all_ckb_amount()
>= state.open_channel_auto_accept_min_ckb_funding_amount
&& open_channel.funding_amount
>= state.open_channel_auto_accept_min_ckb_funding_amount as u128
};
if auto_accept {
let accept_channel = AcceptChannelCommand {
Expand Down Expand Up @@ -1122,12 +1123,6 @@ where
) => {
assert_ne!(new, old, "new and old channel id must be different");
if let Some(session) = state.get_peer_session(&peer_id) {
state.check_accept_channel_ckb_parameters(
local_reserved_ckb_amount,
remote_reserved_ckb_amount,
funding_fee_rate,
&udt_funding_script,
)?;
if let Some(channel) = state.channels.remove(&old) {
debug!("Channel accepted: {:?} -> {:?}", old, new);
state.channels.insert(new, channel);
Expand Down Expand Up @@ -3020,9 +3015,8 @@ where
));
}
}
// NOTE: here we only check the amount is valid, we will also check more in the `pre_start` from channel creation
let (_funding_amount, _reserved_ckb_amount) =
self.get_funding_and_reserved_amount(funding_amount, &funding_udt_type_script)?;
let shutdown_script =
shutdown_script.unwrap_or_else(|| self.default_shutdown_script.clone());

let seed = self.generate_channel_seed();
let (tx, rx) = oneshot::channel::<Hash256>();
Expand All @@ -3045,8 +3039,7 @@ where
tlc_fee_proportional_millionths.unwrap_or(self.tlc_fee_proportional_millionths),
)),
funding_udt_type_script,
shutdown_script: shutdown_script
.unwrap_or_else(|| self.default_shutdown_script.clone()),
shutdown_script,
channel_id_sender: tx,
commitment_fee_rate,
commitment_delay_epoch,
Expand Down Expand Up @@ -3089,8 +3082,11 @@ where
&peer_id
)))?;

let (funding_amount, reserved_ckb_amount) = self.get_funding_and_reserved_amount(
let shutdown_script =
shutdown_script.unwrap_or_else(|| self.default_shutdown_script.clone());
let (funding_amount, reserved_ckb_amount) = get_funding_and_reserved_amount(
funding_amount,
&shutdown_script,
&open_channel.funding_udt_type_script,
)?;

Expand Down Expand Up @@ -3123,8 +3119,7 @@ where
)),
seed,
open_channel,
shutdown_script: shutdown_script
.unwrap_or_else(|| self.default_shutdown_script.clone()),
shutdown_script,
channel_id_sender: Some(tx),
}),
network.clone().get_cell(),
Expand Down Expand Up @@ -3221,104 +3216,79 @@ where
self.peer_pubkey_map.get(peer_id).cloned()
}

fn get_funding_and_reserved_amount(
&self,
funding_amount: u128,
udt_type_script: &Option<Script>,
) -> Result<(u128, u64), ProcessingChannelError> {
let reserved_ckb_amount = default_minimal_ckb_amount(udt_type_script.is_some());
if udt_type_script.is_none() {
if funding_amount < reserved_ckb_amount.into() {
return Err(ProcessingChannelError::InvalidParameter(format!(
"The funding amount should be greater than the reserved amount: {}",
reserved_ckb_amount
)));
}
if funding_amount >= u64::MAX as u128 {
return Err(ProcessingChannelError::InvalidParameter(format!(
"The funding amount should be less than {:?}",
u64::MAX
)));
}
}
let funding_amount = if udt_type_script.is_some() {
funding_amount
} else {
funding_amount - reserved_ckb_amount as u128
};
Ok((funding_amount, reserved_ckb_amount))
}

fn check_accept_channel_ckb_parameters(
&self,
remote_reserved_ckb_amount: u64,
local_reserved_ckb_amount: u64,
funding_fee_rate: u64,
udt_type_script: &Option<Script>,
) -> crate::Result<()> {
let reserved_ckb_amount = default_minimal_ckb_amount(udt_type_script.is_some());
if remote_reserved_ckb_amount < reserved_ckb_amount
|| local_reserved_ckb_amount < reserved_ckb_amount
{
return Err(Error::InvalidParameter(format!(
"Reserved CKB amount is less than the minimal amount: {}",
reserved_ckb_amount
)));
}

if funding_fee_rate < DEFAULT_FEE_RATE {
return Err(Error::InvalidParameter(format!(
"Funding fee rate is less than {}",
DEFAULT_FEE_RATE
)));
}
Ok(())
}

fn check_open_ckb_parameters(
// TODO: this fn is duplicated with ChannelActorState::check_open_channel_parameters, but is not easy to refactor, just keep it for now.
fn check_open_channel_parameters(
&self,
open_channel: &OpenChannel,
) -> Result<(), ProcessingChannelError> {
let reserved_ckb_amount = open_channel.reserved_ckb_amount;
let udt_type_script = &open_channel.funding_udt_type_script;

let minimal_reserved_ckb_amount = default_minimal_ckb_amount(udt_type_script.is_some());
if reserved_ckb_amount < minimal_reserved_ckb_amount {
// reserved_ckb_amount
let occupied_capacity =
occupied_capacity(&open_channel.shutdown_script, udt_type_script)?.as_u64();
if open_channel.reserved_ckb_amount < occupied_capacity {
return Err(ProcessingChannelError::InvalidParameter(format!(
"Remote reserved CKB amount {} is less than the minimal amount: {}",
reserved_ckb_amount, minimal_reserved_ckb_amount,
"Reserved CKB amount {} is less than {}",
open_channel.reserved_ckb_amount, occupied_capacity,
)));
}

// funding_fee_rate
if open_channel.funding_fee_rate < DEFAULT_FEE_RATE {
return Err(ProcessingChannelError::InvalidParameter(format!(
"Funding fee rate is less than {}",
DEFAULT_FEE_RATE,
)));
}

// commitment_fee_rate
if open_channel.commitment_fee_rate < DEFAULT_COMMITMENT_FEE_RATE {
return Err(ProcessingChannelError::InvalidParameter(format!(
"Commitment fee rate is less than {}",
DEFAULT_COMMITMENT_FEE_RATE,
)));
}
let commitment_fee =
calculate_commitment_tx_fee(open_channel.commitment_fee_rate, udt_type_script);
let reserved_fee = open_channel.reserved_ckb_amount - occupied_capacity;
if commitment_fee * 2 > reserved_fee {
return Err(ProcessingChannelError::InvalidParameter(format!(
"Commitment fee {} which caculated by commitment fee rate {} is larger than half of reserved fee {}",
commitment_fee, open_channel.commitment_fee_rate, reserved_fee
)));
}

let commitment_fee = calculate_commitment_tx_fee(
open_channel.commitment_fee_rate,
&open_channel.funding_udt_type_script,
);
// commitment_delay_epoch
let epoch =
EpochNumberWithFraction::from_full_value_unchecked(open_channel.commitment_delay_epoch);
if !epoch.is_well_formed() {
return Err(ProcessingChannelError::InvalidParameter(format!(
"Commitment delay epoch {} is not a valid value",
open_channel.commitment_delay_epoch,
)));
}

let expected_minimal_reserved_ckb_amount = commitment_fee * 2;
debug!(
"expected_minimal_reserved_ckb_amount: {}, reserved_ckb_amount: {}",
expected_minimal_reserved_ckb_amount, reserved_ckb_amount
);
if reserved_ckb_amount < expected_minimal_reserved_ckb_amount {
let min = EpochNumberWithFraction::new(MIN_COMMITMENT_DELAY_EPOCHS, 0, 1);
if epoch < min {
return Err(ProcessingChannelError::InvalidParameter(format!(
"Commitment delay epoch {} is less than the minimal value {}",
epoch, min
)));
}

let max = EpochNumberWithFraction::new(MAX_COMMITMENT_DELAY_EPOCHS, 0, 1);
if epoch > max {
return Err(ProcessingChannelError::InvalidParameter(format!(
"Commitment delay epoch {} is greater than the maximal value {}",
epoch, max
)));
}

// max_tlc_number_in_flight
if open_channel.max_tlc_number_in_flight > SYS_MAX_TLC_NUMBER_IN_FLIGHT {
return Err(ProcessingChannelError::InvalidParameter(format!(
"Commitment fee rate is: {}, expect more CKB amount as reserved ckb amount expected to larger than {}, \
or you can set a lower commitment fee rate",
open_channel.commitment_fee_rate, expected_minimal_reserved_ckb_amount
"Max TLC number in flight {} is greater than the system maximal value {}",
open_channel.max_tlc_number_in_flight, SYS_MAX_TLC_NUMBER_IN_FLIGHT
)));
}

Expand Down Expand Up @@ -3707,7 +3677,7 @@ where
peer_id: PeerId,
open_channel: OpenChannel,
) -> ProcessingChannelResult {
self.check_open_ckb_parameters(&open_channel)?;
self.check_open_channel_parameters(&open_channel)?;

if let Some(udt_type_script) = &open_channel.funding_udt_type_script {
if !check_udt_script(udt_type_script) {
Expand Down
Loading

0 comments on commit bb8a4b1

Please sign in to comment.