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
3 changes: 3 additions & 0 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1894,6 +1894,9 @@ components:
push_msat:
type: integer
example: 1394000
push_asset_amount:
type: integer
example: 100
asset_amount:
type: integer
example: 333
Expand Down
30 changes: 28 additions & 2 deletions src/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,7 @@
pub(crate) push_msat: u64,
pub(crate) asset_amount: Option<u64>,
pub(crate) asset_id: Option<String>,
pub(crate) push_asset_amount: Option<u64>,
pub(crate) public: bool,
pub(crate) with_anchors: bool,
pub(crate) fee_base_msat: Option<u32>,
Expand Down Expand Up @@ -1174,7 +1175,7 @@
}

async fn check_locked(
&self,

Check warning on line 1178 in src/routes.rs

View workflow job for this annotation

GitHub Actions / build (nightly, linux)

hiding a lifetime that's elided elsewhere is confusing

Check warning on line 1178 in src/routes.rs

View workflow job for this annotation

GitHub Actions / build (nightly, macos)

hiding a lifetime that's elided elsewhere is confusing

Check warning on line 1178 in src/routes.rs

View workflow job for this annotation

GitHub Actions / build (nightly, windows)

hiding a lifetime that's elided elsewhere is confusing
) -> Result<TokioMutexGuard<Option<Arc<UnlockedAppState>>>, APIError> {
self.check_changing_state()?;
let unlocked_app_state = self.get_unlocked_app_state().await;
Expand All @@ -1186,7 +1187,7 @@
}

async fn check_unlocked(
&self,

Check warning on line 1190 in src/routes.rs

View workflow job for this annotation

GitHub Actions / build (nightly, linux)

hiding a lifetime that's elided elsewhere is confusing

Check warning on line 1190 in src/routes.rs

View workflow job for this annotation

GitHub Actions / build (nightly, macos)

hiding a lifetime that's elided elsewhere is confusing

Check warning on line 1190 in src/routes.rs

View workflow job for this annotation

GitHub Actions / build (nightly, windows)

hiding a lifetime that's elided elsewhere is confusing
) -> Result<TokioMutexGuard<Option<Arc<UnlockedAppState>>>, APIError> {
self.check_changing_state()?;
let unlocked_app_state = self.get_unlocked_app_state().await;
Expand Down Expand Up @@ -2916,6 +2917,24 @@
)));
}

// Validate push_asset_amount
if let Some(push_asset_amount) = payload.push_asset_amount {
// push_asset_amount can only be used with RGB channels
if colored_info.is_none() {
return Err(APIError::InvalidAmount(s!(
"push_asset_amount can only be used with RGB channels (asset_id must be specified)"
)));
}

if let Some((_, asset_amount)) = &colored_info {
if push_asset_amount > *asset_amount {
return Err(APIError::InvalidAmount(s!(
"push_asset_amount cannot be higher than asset_amount"
)));
}
}
}

if colored_info.is_some() && !payload.with_anchors {
return Err(APIError::AnchorsRequired);
}
Expand Down Expand Up @@ -3073,11 +3092,18 @@
tracing::info!("EVENT: initiated channel with peer {}", peer_pubkey);

if let Some((contract_id, asset_amount)) = &colored_info {
// Calculate local and remote amounts based on push_asset_amount
let (local_amount, remote_amount) = if let Some(push_amount) = payload.push_asset_amount {
(*asset_amount - push_amount, push_amount)
} else {
(*asset_amount, 0)
};

let rgb_info = RgbInfo {
contract_id: *contract_id,
schema: schema.unwrap(),
local_rgb_amount: *asset_amount,
remote_rgb_amount: 0,
local_rgb_amount: local_amount,
remote_rgb_amount: remote_amount,
};
write_rgb_channel_info(
&get_rgb_channel_info_path(
Expand Down
1 change: 1 addition & 0 deletions src/test/close_coop_vanilla.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ async fn without_anchors() {
None,
None,
None,
None,
false,
)
.await;
Expand Down
1 change: 1 addition & 0 deletions src/test/getchannelid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ async fn getchannelid_success() {
Some(&asset_id),
None,
None,
None,
Some(&temporary_channel_id),
true,
)
Expand Down
4 changes: 4 additions & 0 deletions src/test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,7 @@ async fn open_channel(
None,
None,
None,
None,
true,
)
.await
Expand All @@ -1053,6 +1054,7 @@ async fn open_channel_with_custom_data(
push_msat: Option<u64>,
asset_amount: Option<u64>,
asset_id: Option<&str>,
push_asset_amount: Option<u64>,
fee_base_msat: Option<u32>,
fee_proportional_millionths: Option<u32>,
temporary_channel_id: Option<&str>,
Expand All @@ -1074,6 +1076,7 @@ async fn open_channel_with_custom_data(
push_msat: push_msat.unwrap_or(0),
asset_amount,
asset_id: asset_id.map(|a| a.to_string()),
push_asset_amount,
public: true,
with_anchors,
fee_base_msat,
Expand Down Expand Up @@ -1685,6 +1688,7 @@ mod open_after_double_send;
mod openchannel_fail;
mod openchannel_optional_addr;
mod payment;
mod push_rgb_assets;
mod refuse_high_fees;
mod restart;
mod send_receive;
Expand Down
14 changes: 14 additions & 0 deletions src/test/openchannel_fail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ async fn open_fail() {
push_msat: 3_500_000,
asset_amount: Some(100),
asset_id: Some(asset_id.clone()),
push_asset_amount: None,
public: true,
with_anchors: true,
fee_base_msat: None,
Expand Down Expand Up @@ -63,6 +64,7 @@ async fn open_fail() {
push_msat: 3_500_000,
asset_amount: Some(100),
asset_id: Some(s!("rgb:EIkAVQvq-WbAb5JG-CYxbUER-oqDNwne-ZNxBDID-p0cpf9U")),
push_asset_amount: None,
public: true,
with_anchors: true,
fee_base_msat: None,
Expand Down Expand Up @@ -90,6 +92,7 @@ async fn open_fail() {
push_msat: 3_500_000,
asset_amount: Some(0),
asset_id: Some(asset_id.clone()),
push_asset_amount: None,
public: true,
with_anchors: true,
fee_base_msat: None,
Expand Down Expand Up @@ -122,6 +125,7 @@ async fn open_fail() {
push_msat: 3_500_000,
asset_amount: Some(100),
asset_id: Some(s!("bad asset ID")),
push_asset_amount: None,
public: true,
with_anchors: true,
fee_base_msat: None,
Expand Down Expand Up @@ -154,6 +158,7 @@ async fn open_fail() {
push_msat: 3_500_000,
asset_amount: None,
asset_id: None,
push_asset_amount: None,
public: true,
with_anchors: true,
fee_base_msat: None,
Expand Down Expand Up @@ -186,6 +191,7 @@ async fn open_fail() {
push_msat: 3_500_000,
asset_amount: Some(100),
asset_id: Some(asset_id.clone()),
push_asset_amount: None,
public: true,
with_anchors: true,
fee_base_msat: None,
Expand Down Expand Up @@ -220,6 +226,7 @@ async fn open_fail() {
push_msat: 100_000_000,
asset_amount: Some(100),
asset_id: Some(asset_id.clone()),
push_asset_amount: None,
public: true,
with_anchors: true,
fee_base_msat: None,
Expand Down Expand Up @@ -252,6 +259,7 @@ async fn open_fail() {
push_msat: 100_000_001,
asset_amount: Some(100),
asset_id: Some(asset_id.clone()),
push_asset_amount: None,
public: true,
with_anchors: true,
fee_base_msat: None,
Expand Down Expand Up @@ -284,6 +292,7 @@ async fn open_fail() {
push_msat: 3_500_000,
asset_amount: Some(100),
asset_id: Some(asset_id.clone()),
push_asset_amount: None,
public: true,
with_anchors: false,
fee_base_msat: None,
Expand Down Expand Up @@ -318,6 +327,7 @@ async fn open_fail() {
push_msat: 0,
asset_amount: None,
asset_id: None,
push_asset_amount: None,
public: true,
with_anchors: true,
fee_base_msat: None,
Expand Down Expand Up @@ -350,6 +360,7 @@ async fn open_fail() {
push_msat: 3_500_000,
asset_amount: Some(2000),
asset_id: Some(asset_id.clone()),
push_asset_amount: None,
public: true,
with_anchors: true,
fee_base_msat: None,
Expand Down Expand Up @@ -382,6 +393,7 @@ async fn open_fail() {
push_msat: 3_500_000,
asset_amount: Some(100),
asset_id: Some(asset_id.clone()),
push_asset_amount: None,
public: true,
with_anchors: true,
fee_base_msat: None,
Expand Down Expand Up @@ -414,6 +426,7 @@ async fn open_fail() {
push_msat: 3_500_000,
asset_amount: Some(100),
asset_id: Some(asset_id.clone()),
push_asset_amount: None,
public: true,
with_anchors: true,
fee_base_msat: None,
Expand All @@ -434,6 +447,7 @@ async fn open_fail() {
push_msat: 3_500_000,
asset_amount: Some(100),
asset_id: Some(asset_id),
push_asset_amount: None,
public: true,
with_anchors: true,
fee_base_msat: None,
Expand Down
2 changes: 2 additions & 0 deletions src/test/openchannel_optional_addr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ async fn openchannel_optional_addr_forward() {
push_msat: 3_500_000,
asset_amount: Some(600),
asset_id: Some(asset_id.clone()),
push_asset_amount: None,
public: true,
with_anchors: true,
fee_base_msat: None,
Expand Down Expand Up @@ -113,6 +114,7 @@ async fn openchannel_optional_addr_reverse() {
push_msat: 3_500_000,
asset_amount: Some(600),
asset_id: Some(asset_id.clone()),
push_asset_amount: None,
public: true,
with_anchors: true,
fee_base_msat: None,
Expand Down
140 changes: 140 additions & 0 deletions src/test/push_rgb_assets.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
use super::*;
const TEST_DIR_BASE: &str = "tmp/push_rgb_assets/";

#[serial_test::serial]
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
#[traced_test]
async fn test_push_rgb_assets() {
initialize();

let test_dir_base = format!("{TEST_DIR_BASE}test_push_rgb_assets/");
let test_dir_node1 = format!("{test_dir_base}node1");
let test_dir_node2 = format!("{test_dir_base}node2");

Check warning on line 12 in src/test/push_rgb_assets.rs

View workflow job for this annotation

GitHub Actions / format

Diff in /home/runner/work/rgb-lightning-node/rgb-lightning-node/src/test/push_rgb_assets.rs
let (node1_addr, _) = start_node(&test_dir_node1, NODE1_PEER_PORT, false).await;
let (node2_addr, _) = start_node(&test_dir_node2, NODE2_PEER_PORT, false).await;

let node2_pubkey = node_info(node2_addr).await.pubkey;

fund_and_create_utxos(node1_addr, None).await;
fund_and_create_utxos(node2_addr, None).await;

Check warning on line 19 in src/test/push_rgb_assets.rs

View workflow job for this annotation

GitHub Actions / format

Diff in /home/runner/work/rgb-lightning-node/rgb-lightning-node/src/test/push_rgb_assets.rs

let asset_id = issue_asset_nia(node1_addr).await.asset_id;
connect_peer(node1_addr, &node2_pubkey, &format!("127.0.0.1:{NODE2_PEER_PORT}"),).await;

// Open channel with asset push: 600 total, push 250 to counterparty
let channel = open_channel_with_custom_data(
node1_addr,
&node2_pubkey,
Some(NODE2_PEER_PORT),

Check warning on line 28 in src/test/push_rgb_assets.rs

View workflow job for this annotation

GitHub Actions / format

Diff in /home/runner/work/rgb-lightning-node/rgb-lightning-node/src/test/push_rgb_assets.rs
None,
None,
Some(600),
Some(&asset_id),
Some(250),
None,
None,
None,
true,
)
.await;

let channels_1 = list_channels(node1_addr).await;
let channels_2 = list_channels(node2_addr).await;
assert_eq!(channels_1.len(), 1);
assert_eq!(channels_2.len(), 1);

Check warning on line 45 in src/test/push_rgb_assets.rs

View workflow job for this annotation

GitHub Actions / format

Diff in /home/runner/work/rgb-lightning-node/rgb-lightning-node/src/test/push_rgb_assets.rs
// Check balances after channel opening with push
assert_eq!(asset_balance_spendable(node1_addr, &asset_id).await, 400);

let node1_channels = list_channels(node1_addr).await;
let node1_channel = node1_channels.iter().find(|c| c.channel_id == channel.channel_id).unwrap();
assert_eq!(node1_channel.asset_local_amount, Some(350));
assert_eq!(node1_channel.asset_remote_amount, Some(250));

let node2_channels = list_channels(node2_addr).await;
let node2_channel = node2_channels.iter().find(|c| c.channel_id == channel.channel_id).unwrap();
assert_eq!(node2_channel.asset_local_amount, Some(250));
assert_eq!(node2_channel.asset_remote_amount, Some(350));

}

#[serial_test::serial]
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
#[traced_test]
async fn test_push_rgb_assets_validation() {
initialize();

let test_dir_base = format!("{TEST_DIR_BASE}test_push_rgb_assets_validation/");
let test_dir_node1 = format!("{test_dir_base}node1");
let test_dir_node2 = format!("{test_dir_base}node2");
let (node1_addr, _) = start_node(&test_dir_node1, NODE1_PEER_PORT, false).await;
let (node2_addr, _) = start_node(&test_dir_node2, NODE2_PEER_PORT, false).await;

Check warning on line 72 in src/test/push_rgb_assets.rs

View workflow job for this annotation

GitHub Actions / format

Diff in /home/runner/work/rgb-lightning-node/rgb-lightning-node/src/test/push_rgb_assets.rs
let node2_pubkey = node_info(node2_addr).await.pubkey;

// Fund node1 with on-chain funds
fund_and_create_utxos(node1_addr, None).await;

// Issue an RGB asset on node1
let asset = issue_asset_nia(node1_addr).await;
let asset_id = asset.asset_id;

// Connect peers
connect_peer(
node1_addr,
&node2_pubkey,
&format!("127.0.0.1:{NODE2_PEER_PORT}"),

Check warning on line 86 in src/test/push_rgb_assets.rs

View workflow job for this annotation

GitHub Actions / format

Diff in /home/runner/work/rgb-lightning-node/rgb-lightning-node/src/test/push_rgb_assets.rs
)
.await;

// Test 1: Try to use push_asset_amount without RGB channel (should fail)
let payload = OpenChannelRequest {
peer_pubkey_and_opt_addr: format!("{}@127.0.0.1:{}", node2_pubkey, NODE2_PEER_PORT),

Check failure on line 92 in src/test/push_rgb_assets.rs

View workflow job for this annotation

GitHub Actions / lint

variables can be used directly in the `format!` string
capacity_sat: 100_000,
push_msat: 0,
asset_amount: None,
asset_id: None,
push_asset_amount: Some(100), // This should fail
public: true,
with_anchors: true,
fee_base_msat: None,
fee_proportional_millionths: None,
temporary_channel_id: None,
};

let res = reqwest::Client::new()
.post(format!("http://{}/openchannel", node1_addr))

Check failure on line 106 in src/test/push_rgb_assets.rs

View workflow job for this annotation

GitHub Actions / lint

variables can be used directly in the `format!` string
.json(&payload)
.send()

Check warning on line 108 in src/test/push_rgb_assets.rs

View workflow job for this annotation

GitHub Actions / format

Diff in /home/runner/work/rgb-lightning-node/rgb-lightning-node/src/test/push_rgb_assets.rs
.await
.unwrap();

assert_eq!(res.status(), 400); // Should fail with bad request


// Test 2: Try to push more than asset_amount (should fail)
let payload = OpenChannelRequest {
peer_pubkey_and_opt_addr: format!("{}@127.0.0.1:{}", node2_pubkey, NODE2_PEER_PORT),

Check failure on line 117 in src/test/push_rgb_assets.rs

View workflow job for this annotation

GitHub Actions / lint

variables can be used directly in the `format!` string
capacity_sat: 100_000,
push_msat: 0,
asset_amount: Some(500),
asset_id: Some(asset_id.clone()),
push_asset_amount: Some(600), // More than asset_amount, should fail
public: true,
with_anchors: true,
fee_base_msat: None,
fee_proportional_millionths: None,
temporary_channel_id: None,
};

let res = reqwest::Client::new()
.post(format!("http://{}/openchannel", node1_addr))

Check failure on line 131 in src/test/push_rgb_assets.rs

View workflow job for this annotation

GitHub Actions / lint

variables can be used directly in the `format!` string
.json(&payload)
.send()

Check warning on line 133 in src/test/push_rgb_assets.rs

View workflow job for this annotation

GitHub Actions / format

Diff in /home/runner/work/rgb-lightning-node/rgb-lightning-node/src/test/push_rgb_assets.rs
.await
.unwrap();

assert_eq!(res.status(), 400); // Should fail with bad request

println!("✓ RGB asset push validation working correctly!");
}

Check warning on line 140 in src/test/push_rgb_assets.rs

View workflow job for this annotation

GitHub Actions / format

Diff in /home/runner/work/rgb-lightning-node/rgb-lightning-node/src/test/push_rgb_assets.rs
1 change: 1 addition & 0 deletions src/test/refuse_high_fees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ async fn refuse_high_fees() {
None,
Some(300),
Some(&asset_id),
None,
Some(2_000_000),
None,
None,
Expand Down
Loading