Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support fiber multiple hops payment in cross chain transaction #511

Open
wants to merge 94 commits into
base: develop
Choose a base branch
from

Conversation

contrun
Copy link
Collaborator

@contrun contrun commented Feb 17, 2025

No description provided.

@contrun contrun force-pushed the support-fiber-multiple-hops-payment-in-cross-chain-transaction branch 3 times, most recently from 3f98938 to 21a1d8c Compare February 17, 2025 10:54
@contrun contrun force-pushed the support-fiber-multiple-hops-payment-in-cross-chain-transaction branch from a63dc44 to 21a1d8c Compare February 17, 2025 11:56
@contrun contrun force-pushed the support-fiber-multiple-hops-payment-in-cross-chain-transaction branch from 21a1d8c to ee59e1f Compare February 17, 2025 14:07
@contrun contrun force-pushed the support-fiber-multiple-hops-payment-in-cross-chain-transaction branch from 1ed77fd to 969f732 Compare February 17, 2025 14:59
@@ -23,7 +23,8 @@ rpc:

cch:
ignore_startup_failure: true
wrapped_btc_type_script_args: "0x32e555f3ff8e135cece1351a6a2971518392c1e30375c1e006ad0ce8eac07947"
# will be generated by udt-init
wrapped_btc_type_script: 0x55000000100000003000000031000000e1e354d6d643ad42724d40967e334984534e0367405c5ae42a9d7d63d77df419022000000032e555f3ff8e135cece1351a6a2971518392c1e30375c1e006ad0ce8eac07947
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we may use ckb json type Script here, it's easy to read than a hex string:

Suggested change
wrapped_btc_type_script: 0x55000000100000003000000031000000e1e354d6d643ad42724d40967e334984534e0367405c5ae42a9d7d63d77df419022000000032e555f3ff8e135cece1351a6a2971518392c1e30375c1e006ad0ce8eac07947
wrapped_btc_type_script:
code_hash: 0xe1e354d6d643ad42724d40967e334984534e0367405c5ae42a9d7d63d77df419
hash_type: Data1
args: 0x....

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrapped_btc_type_script can also be specified with environment variable CCH_WRAPPED_BTC_TYPE_SCRIPT. It is not very easy to write nested structures with environment variable. Can we both do a hex decoding and a ckb_json_types::Script decoding? That makes both 0x55000000100000003000000031000000e1e354d6d643ad42724d40967e334984534e0367405c5ae42a9d7d63d77df419022000000032e555f3ff8e135cece1351a6a2971518392c1e30375c1e006ad0ce8eac07947 and code_hash: 0xe1e354d6d643ad42724d40967e334984534e0367405c5ae42a9d7d63d77df419 hash_type: Data1 args: 0x.... legitimate.

#[derive(Clone, Debug)]
pub struct Store {
pub struct GenericStore<IH: InvoiceUpdateHook, PH: PaymentUpdateHook> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need to make a GenericStore abstraction of the store here, it violates the principle of composition over inheritance, you can just use a store + hook struct wherever you need a store callback.

Copy link
Collaborator Author

@contrun contrun Feb 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean that the hooks can be passed to every one that needs them (instead of bundling them with the store)? There are many places in current code base which alters the status of an invoice, for example, https://github.com/contrun/fiber/blob/c53f6dfebba73ae7f9b3b75622abd7e8132eb3b6/src/rpc/invoice.rs#L340-L348 https://github.com/contrun/fiber/blob/c53f6dfebba73ae7f9b3b75622abd7e8132eb3b6/src/fiber/channel.rs#L1058-L1062 https://github.com/contrun/fiber/blob/c53f6dfebba73ae7f9b3b75622abd7e8132eb3b6/src/store/store.rs#L444-L447 , all the code listed above must have a reference to the InvoiceUpdateHook. So it seems to me define a

Struct StoreWithHook<IH: InvoiceUpdateHook> {
   store: Store,
   hook: IH,
}

and pass this StoreWithHook to above code would make life easier. That is essentially GenericStore<IH: InvoiceUpdateHook>.

The thing is that Hook and Store can't really work work independently. Hook (calling it StoreUpdateHook is better) should be part of Store because the hooks run when store is updated. We can't compose these two inter-dependent component.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keep the original store implementation unchanged:

impl InvoiceStore for Store {
    fn insert_invoice(...) {
        ...
    }
}

and add a compsited store implementation:

struct InvoiceStoreWithHook {
    store: Store,
    invoice_hook: ConcreteInvoiceHook,
}

impl InvoiceStore for InvoiceStoreWithHook {
    fn insert_invoice(...) {
        self.store.insert_invoice(...);
        self.invoice_hook.on_invoice_updated(...);
    }
}

no need to introduce GenericStore struct and InvoiceUpdateHook trait

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In https://github.com/contrun/fiber/tree/support-fiber-multiple-hops-payment-in-cross-chain-transaction-pass-hooks, I tried to use this approach (as I understood it). Can you confirm this align well with the approach you described? My code in that branch is very complicated, I don't know if that is the simplest way to accomplish what's on your mind. The complexity of my implementation is due to the following points:

  1. Hooks has to be initialized in the main function (we need to have a singleton to receive all the updates and push them to the subscribers).
  2. There are many modules which use InvoiceStore, so all of they have to call InvoiceStoreWithHooks::new, which is a function to initialize a InvoiceStore with a hook. This means we have to pass the invoice hooks created in the main function down to many modules.
  3. Channel actors use ChannelActorStateStore along with InvoiceStore. So we have to implement ChannelActorStateStore for the InvoiceStoreWithHooks.
  4. The same thing needs to be done for NetworkGraphStateStore (yeah, confusingly, this trait updates payment session), because we need to inject hooks for payments.

I didn't have a complete implementation yet. If my current code is what's on your mind. I can finish injecting payment hooks and removing the GenericStore.

@contrun contrun force-pushed the support-fiber-multiple-hops-payment-in-cross-chain-transaction branch from 0a2be2d to 548259b Compare February 18, 2025 06:22
@@ -272,6 +345,120 @@ impl NetworkNodeConfigBuilder {
}
}

#[allow(clippy::too_many_arguments)]
pub(crate) async fn establish_udt_channel_between_nodes(
node_a: &mut NetworkNode,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we merge this with establish_channel_between_nodes? seems only have a extra udt_script.

Copy link
Collaborator Author

@contrun contrun Feb 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't do this because there are some many places in the code base calling establish_channel_between_nodes. This PR is so large that we may encounter a few conflicts when this PR is ready to merge (this happened to the function create_n_nodes_and_channels_with_index_amounts). When this PR is ready to merge, I will do what you said.

|| status == CkbInvoiceStatus::Received;
let is_settled =
self.store.get_invoice_preimage(&payment_hash).is_some();
if is_active && !is_settled {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the is_settled could be removed, since if is_some return true the code will goto Some(preimage) branch and will never reach here.

// TLCs in the list applied_add_tlcs wouldn't be processed again.
// For the unsettled active hold invoice TLCs, we should process them indefinitely
// until they expire or are settled.
state.tlc_state.applied_add_tlcs.remove(&add_tlc.tlc_id);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

applied_add_tlcs is introduced to resolve some concurrency issue, I'm not sure is it ok to remove it from here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so. Because we are actually adding and removing the tlc from exactly the same function apply_add_tlc_operation_with_peeled_onion_packet.

.insert_payment_preimage(payment_hash, preimage)
.map_err(|_| {
ProcessingChannelError::InternalError("insert preimage failed".to_string())
})?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems ProcessingChannelError::FinalIncorrectPaymentHash does not return to caller. we should return the error at line 1019?

))
};

match call_t!(network_actor, message, RPC_TIMEOUT_MS).map_err(|ractor_error| {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe can reuse macro handle_actor_call like rpc/channel.rs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants