hyperlane-xyz / hyperlane-monorepo

The home for Hyperlane core contracts, sdk packages, and other infrastructure
https://hyperlane.xyz
Other
339 stars 378 forks source link

Sealevel Mailbox - query recipient ISMs #2209

Closed tkporter closed 1 year ago

tkporter commented 1 year ago

At the moment, the inbox_process function always uses the configured default ISM (found in the Inbox account). Ultimately, we want recipients to have the ability to default to whatever the Mailbox's default ISM is, or to have their own "custom" ISM be used. This is done by having some logic like this when inbox_process is called:

  1. Call the recipient's interchain_security_module() -> Option<Pubkey> function
  2. If None is given, use the default ISM for security (i.e. call verify on the default ISM)
  3. If Some is given, use this as the ISM for security (i.e. call verify on the program found at this Pubkey)

The one awkward bit here is the ISM that someone specifies will probably require certain accounts to be passed into the invocation. Not 100% sure what the best way of dealing with this is, but I think that can be figured out - there's probably a pattern out there that I'm not aware of? E.g. the accounts passed into the process function should be something like [inbox PDA, auth PDA, ISM, ...ISM addresses, recipient, ...recipient addresses]. So maybe the # of ISM addresses should be passed into the process instruction so that the # of ISM addresses can be figured out when crafting the invoke_signed call?

Anyways, concretely:

  1. Change the Mailbox program to query the recipient for what ISM to use
  2. Make required changes to agent / tests / other client code to ensure things still work end to end
Hyperlane-Cryptolutions commented 1 year ago

In the inbox_process function, add a call to the recipient's interchain_security_module() function. If the recipient's interchain_security_module() function returns None, then use the default ISM for security. If the recipient's interchain_security_module() function returns Some, then use the ISM returned by the function for security.

pub fn inbox_process( &mut self, message: &Message, ) -> Result<(), Error> { // Get the recipient's ISM. let ism = message.recipient.interchain_security_module()?;

// If the recipient didn't specify an ISM, use the default.
if ism.is_none() {
    ism = self.default_ism();
}

// Verify the message using the ISM.
message.verify(ism.unwrap())?;

// Process the message.
self.process_message(message)?;

Ok(())

}

tkporter commented 1 year ago

Yeah something like this works. Note what I said about the accounts that need to be passed into the ISM.verify call - this may need to be added to the InboxProcess instruction so that the correct accounts from the accounts: &[AccountInfo] slice are passed into the CPI

tkporter commented 1 year ago

Like I'd expect we need something like

// the data passed into an Instruction::InboxProcess
struct InboxProcessData {
  ism_account_count: u8,
  metadata: Vec<u8>,
  message: Vec<u8>,
}

...
...
fn inbox_process(
  program_id: &Pubkey,
  accounts: &[AccountInfo],
  process_data: InboxProcessData,
) {
  let accounts_iter = &mut accounts.iter();

  // ...
  let ism_account = next_account_info(accounts_iter);
  let ism_accounts = vec![];
  for i in 0..process_data.ism_account_count {
    ism_accounts.push(next_account_info(accounts_iter));
  }
  // Now make CPI to the ISM
}
tkporter commented 1 year ago

Actually, I just realized you can imply which accounts should be for the ISM by just iterating through next_account_info until you hit the recipient program

tkporter commented 1 year ago

done

tkporter commented 1 year ago

with #2295