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

Feature: CPI Events API #2438

Merged
merged 41 commits into from
May 26, 2023
Merged

Feature: CPI Events API #2438

merged 41 commits into from
May 26, 2023

Conversation

ngundotra
Copy link
Contributor

Adds emit_cpi to allow programs to store logs in CPI data, the same way Metaplex's compressed NFTs do.

Closes #2408

@vercel
Copy link

vercel bot commented Mar 17, 2023

@ngundotra is attempting to deploy a commit to the coral-xyz Team on Vercel.

A member of the Team first needs to authorize it.

lang/src/lib.rs Outdated Show resolved Hide resolved
@ngundotra
Copy link
Contributor Author

Unfortunately I cannot move the _emit_cpi_data and emit_cpi macro into the lang/attribute crate because attribute crate is a proc_macro crate, and emit_cpi is a declarative macro, which are incompatible exports. ie attribute can only export proc_macro macros 🙃

@ngundotra
Copy link
Contributor Author

Rewrote emit_cpi as a proc_macro that takes a tuple (AccountInfo, <dyn anchor_lang::Event>)

lang/src/lib.rs Outdated Show resolved Hide resolved
#[inline(never)]
#[cfg(not(feature = "no-cpi-events"))]
pub fn __event_dispatch(program_id: &Pubkey, accounts: &[AccountInfo], event_data: &[u8]) -> anchor_lang::Result<()> {
Ok(())
Copy link
Member

@armaniferrante armaniferrante Mar 30, 2023

Choose a reason for hiding this comment

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

Should we be using the instructions sysvar to check that this call was self-referential, i.e., that the previous instruction was to this program? Do we want to allow other programs or even top level instructions to call this?

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

how about two different macros, emit_cpi! and emit_cpi_signed! ?

I'll look into the introspection stuff.

I think devs should have options

Copy link
Contributor

@Arrowana Arrowana Mar 31, 2023

Choose a reason for hiding this comment

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

i would say log authority is better, it is also an account to be provided but much simpler to check.
What would be the advantage of the ix introspection?
I don't feel dev should have the option when there is no advantage to a solution, it only bloats anchor.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't understand why log_authority is even needed here?

Are people writing indexers that parse ixs from TransactionStatus Metadata separately from their transaction context? Is that the norm? I don't understand how log_authority helps indexers? Sure it prevents other programs from invoking that instruction, but it will still show up in getSignaturesForAddress.

Can you provide a concrete example of a program that benefits from having log_authority guarding the event ix?

Copy link
Contributor

@Arrowana Arrowana Apr 1, 2023

Choose a reason for hiding this comment

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

You need to ask @jarry-xiao, I think it is mainly to avoid an extra burden for log parsers yes, to have to check that the caller is indeed the correct program. So it might not be 100% necessary

Choose a reason for hiding this comment

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

It’s not 100% necessary, but it prevents footguns (which I think is one of the key tenets of anchor).

We’ve chatted about this before, and I gave a concrete example of making a CPI from a rogue program into the log instruction of the target program. The parser can defend against this but the logic becomes more error prone.

I think it’s fine if you provide an indexing example of how one might process this along with a test case of how it blocks out erroneous instances of calling the log instruction (both through top level transaction calls and CPIs from rogue programs)

Choose a reason for hiding this comment

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

Also log_authority means you don’t need introspection, which I think is a win. To my knowledge, you still need to explicitly pass in SysvarInstructions

Choose a reason for hiding this comment

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

Here’s a super concrete example. Suppose you have a target program T and a rogue program R. R first makes a normal call to T, which will emit a regular event. Then in the same instruction it makes a call to T’s log instruction. You can figure out this is erroneous if you have the tx stack depth, but I think it’s ambiguous in the current interface.

In a vacuum you also know nothing about T’s logging patterns. Maybe it calls emit_cpi! a variable number of times. The point is there’s no way to know (again, until stack depth is in the transaction object which could realistically take months)

However if R’s call to T’s log instruction fails 100% of the time, then there’s nothing to ever worry about

Copy link
Contributor

Choose a reason for hiding this comment

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

Another point for the log authority, if your program allows arbitrary cpi (or only arbitrary self cpi) for any reason (flash loan capability...), then someone can write random logs without the log authority.

lang/attribute/event/src/lib.rs Outdated Show resolved Hide resolved
lang/attribute/event/src/lib.rs Outdated Show resolved Hide resolved
lang/attribute/event/src/lib.rs Outdated Show resolved Hide resolved
@austbot
Copy link

austbot commented May 2, 2023

I would couple this to the 1.15 release where we get stack height from geyser. Or have a huge disclaimer that without log parsing you ita tricky to know at what depth the cpi was made

Copy link
Collaborator

@acheroncrypto acheroncrypto left a comment

Choose a reason for hiding this comment

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

Thank you for addressing previous comments. I've left a few more comments, some of them are pretty important. Let me know what you think!

lang/attribute/event/src/lib.rs Show resolved Hide resolved
lang/attribute/event/src/lib.rs Outdated Show resolved Hide resolved
lang/attribute/event/src/lib.rs Outdated Show resolved Hide resolved
lang/attribute/event/src/lib.rs Show resolved Hide resolved
lang/attribute/event/src/lib.rs Outdated Show resolved Hide resolved
lang/attribute/event/src/lib.rs Outdated Show resolved Hide resolved
lang/attribute/event/src/lib.rs Outdated Show resolved Hide resolved
lang/syn/src/codegen/program/dispatch.rs Outdated Show resolved Hide resolved
tests/events/package.json Outdated Show resolved Hide resolved
tests/events/tests/events.js Outdated Show resolved Hide resolved
@acheroncrypto
Copy link
Collaborator

Here is the summary for usage with my last changes:

Instruction

pub fn my_instruction(ctx: Context<MyInstruction>) -> Result<()> {
-    emit_cpi!(
-        ctx.accounts.program.to_account_info(),
-        ctx.accounts.event_authority.clone(),
-        *ctx.bumps.get("event_authority").unwrap(),
-        MyEvent { data: 5 }
-    );
+    emit_cpi!(MyEvent { data: 5 });
    Ok(())
}

Accounts

+#[event_cpi]
#[derive(Accounts)]
-pub struct MyInstruction<'info> {
-    /// CHECK: this account is needed to guarantee that your program is the one doing the logging
-    #[account(seeds=[b"__event_authority"], bump)]
-    pub event_authority: AccountInfo<'info>,
-    pub program: Program<'info, crate::program::MyProgramName>,
+pub struct MyInstruction {}

Client

-await program.methods
-.myInstruction()
-.accounts({
-  eventAuthority: anchor.web3.PublicKey.findProgramAddressSync(
-    [Buffer.from("__event_authority")],
-    program.programId
-  )[0],
-  program: program.programId,
-})
-.rpc();
+await program.methods.myInstruction().rpc();

lang/syn/src/codegen/program/handlers.rs Outdated Show resolved Hide resolved
lang/syn/src/parser/accounts/mod.rs Outdated Show resolved Hide resolved
lang/syn/src/parser/accounts/mod.rs Outdated Show resolved Hide resolved
@acheroncrypto
Copy link
Collaborator

Last changes:

  • Anyone was able to invoke the event handler before 87652d2. We're now validating the event authority signed the transaction and is correct key in the event handler instruction, added a test case for this so that only the program itself can invoke the event handler.
  • Removed the #[account(address = crate::ID)] check from the main instruction because we're invoking with crate::ID as the program id and it will fail if the program wasn't passed in.
  • Renamed the no-cpi-eventsfeature to event-cpi and made the feature opt-in instead of opt-out.

@ngundotra let me know what you think.

@ngundotra
Copy link
Contributor Author

This works great! The emit_cpi!{} macro just works, and the #[event_cpi] attr is great.

I think we need 2 changes to the JS sdk tho.

  1. We need a method like program.cpiEvents.parseTransaction() and gives a list of events from a transaction
  2. Probably the eventAuthority address derivation on program.addresses.eventAuthority (and probably export IDL similarly like program.addresses.idl) - probably can be done in another PR

What do you think?

@acheroncrypto
Copy link
Collaborator

I think we need 2 changes to the JS sdk tho.

  1. We need a method like program.cpiEvents.parseTransaction() and gives a list of events from a transaction
  2. Probably the eventAuthority address derivation on program.addresses.eventAuthority (and probably export IDL similarly like program.addresses.idl) - probably can be done in another PR

What do you think?

Yeah client side needs improvement and I think both of those can be made in a separate PR.

One potential issue here is that we are deriving the key each time the event handler is called to validate the authority(on top of deriving it on the main instruction) but the authority key and bump does not change and can be hard coded inside the program. The solution would most likely be a similar macro to declare_id! which kind of sucks. I'll come back to this if it causes noticable difference in compute units.

I've added and updated the documentation and looking to merge this soon if it looks good to you as well.

@ngundotra
Copy link
Contributor Author

Awesome, that sounds like a plan.

Thanks for updating documentation, your changes look great to me. I tested externally by importing in another standalone repo, and it works like a charm.

🚢 it

@acheroncrypto acheroncrypto merged commit 23b90bf into coral-xyz:master May 26, 2023
Aursen added a commit to Aursen/anchor that referenced this pull request Jun 10, 2023
commit e1afcbf
Author: acheron <98934430+acheroncrypto@users.noreply.github.com>
Date:   Fri Jun 9 18:00:35 2023 +0200

    v0.28.0 (coral-xyz#2527)

commit c7c7319
Author: acheron <98934430+acheroncrypto@users.noreply.github.com>
Date:   Thu Jun 8 18:59:44 2023 +0200

    Allow wider range of dependency versions to reduce dependency issues (coral-xyz#2524)

commit 6df34e7
Author: acheron <98934430+acheroncrypto@users.noreply.github.com>
Date:   Wed Jun 7 19:12:56 2023 +0200

    Update crate authors and remove outdated registry (coral-xyz#2522)

commit 1705d16
Author: Jean Marchand (Exotic Markets) <jeanno11@orange.fr>
Date:   Wed Jun 7 16:29:23 2023 +0200

    docs: Add doc for InitSpace macro (coral-xyz#2521)

commit 3d7c97b
Author: acheron <98934430+acheroncrypto@users.noreply.github.com>
Date:   Tue Jun 6 19:28:24 2023 +0200

    cli: Accept program lib name for `anchor deploy --program-name` (coral-xyz#2519)

commit a88be42
Author: Sergo <rogaldh@radsh.red>
Date:   Tue Jun 6 14:07:33 2023 +0300

    ts: Validate `error.data` exists on simulation response (coral-xyz#2508)

commit 65c9d6e
Author: Jean Marchand (Exotic Markets) <jeanno11@orange.fr>
Date:   Tue Jun 6 09:43:46 2023 +0200

    client: Add async to anchor-client (coral-xyz#2488)

    Co-authored-by: acheron <acheroncrypto@gmail.com>

commit b8eda69
Author: Deep Mehta <65382963+0xdeepmehta@users.noreply.github.com>
Date:   Mon Jun 5 22:35:24 2023 +0530

    cli: Print not found message if the given program cannot be found during deployment (coral-xyz#2517)

commit 1902b8e
Author: CanardMandarin <thibault.marboud@gmail.com>
Date:   Mon Jun 5 14:16:10 2023 +0200

    cli: Update programs in `Anchor.toml` when using `anchor new` (coral-xyz#2516)

commit 383e440
Author: acheron <98934430+acheroncrypto@users.noreply.github.com>
Date:   Sun Jun 4 21:02:16 2023 +0200

    cli: Initialize with the correct program id (coral-xyz#2509)

commit 835dc5b
Author: Sarfaraz Nawaz <sir_nawaz959@yahoo.com>
Date:   Sun Jun 4 23:20:03 2023 +0530

    lang: Rename derive_anchor_deserialize -> derive_init_space (coral-xyz#2510)

commit 1c6f86e
Author: acheron <98934430+acheroncrypto@users.noreply.github.com>
Date:   Sun Jun 4 13:09:39 2023 +0200

    Upgrade Solana to 1.16.0 (coral-xyz#2512)

commit 2bf8afe
Author: acheron <98934430+acheroncrypto@users.noreply.github.com>
Date:   Tue May 30 19:50:45 2023 +0200

    cli: Use `confirmed` commitment level in commands (coral-xyz#2506)

commit 70d9223
Author: acheron <98934430+acheroncrypto@users.noreply.github.com>
Date:   Sun May 28 22:34:53 2023 +0200

    cli: Add `anchor keys sync` command (coral-xyz#2505)

commit 0c8498d
Author: cavemanloverboy <93507302+cavemanloverboy@users.noreply.github.com>
Date:   Sat May 27 06:53:02 2023 -0700

    cli: Exit `anchor clean` without error when dirs don't exist (coral-xyz#2504)

commit 23b90bf
Author: Noah Gundotra <ngundotra@users.noreply.github.com>
Date:   Fri May 26 12:36:46 2023 -0400

    Feature: CPI Events API (coral-xyz#2438)

    Co-authored-by: acheron <acheroncrypto@gmail.com>

commit c3625c8
Author: Last Emperor <46998219+lastemp@users.noreply.github.com>
Date:   Wed May 24 15:05:47 2023 +0300

    examples: Add an example with `instruction` method (coral-xyz#2501)

    Co-authored-by: acheron <acheroncrypto@gmail.com>

commit 67eb752
Author: acheron <98934430+acheroncrypto@users.noreply.github.com>
Date:   Sat May 20 20:34:38 2023 +0200

    tests: Fix zero-copy tests (coral-xyz#2498)

commit f9d0eca
Author: acheron <98934430+acheroncrypto@users.noreply.github.com>
Date:   Fri May 19 13:18:14 2023 +0200

    spl: Update `spl-token-2022` to 0.6.1 (coral-xyz#2496)

commit 4793b90
Author: acheron <98934430+acheroncrypto@users.noreply.github.com>
Date:   Fri May 19 10:58:16 2023 +0200

    Fix `toml_datetime` 1.64.0 MSRV error (coral-xyz#2495)

commit 41a4d82
Author: chalda <chalda@seznam.cz>
Date:   Thu May 18 19:12:25 2023 +0200

    cli: Add print base64 instruction option for some of the IDL commands (coral-xyz#2486)

    Co-authored-by: acheron <acheroncrypto@gmail.com>

commit b7bada1
Author: Pierre <Arrowana@users.noreply.github.com>
Date:   Tue May 16 23:46:40 2023 +1000

    fix: remove skip preflight from cli (coral-xyz#2492)

    ---------

    Co-authored-by: acheron <acheroncrypto@gmail.com>

commit 89e94d1
Author: Ryan De La O <rdelao@users.noreply.github.com>
Date:   Sat May 13 02:17:47 2023 -0700

    cli: Fix incorrect metadata.address generation (coral-xyz#2485)

    Currently when running 'anchor deploy --program-name <name> --program-keypair <specified keypair>' the cli still uses the auto-generated keypair when fetching the program id to add to the IDL metadata at the end. It should instead use the address from the specified keypair.

    ---------

    Co-authored-by: acheron <acheroncrypto@gmail.com>

commit 714d524
Author: CanardMandarin <thibault.marboud@gmail.com>
Date:   Tue May 9 16:17:11 2023 +0200

    lang: Add error message when Mint and TokenAccount with `init` are not ordered correctly (coral-xyz#2484)

commit 9a93a2e
Author: James <juicy66173@gmail.com>
Date:   Mon May 8 10:17:51 2023 +0100

    ts: Improve IDL typing (coral-xyz#2482)

    * Use XOR pattern for enum variants to prevent two variants being used at the same time.

    * Fix unknown for types like Option<[u8; 32]>

commit d1ddf00
Author: CanardMandarin <thibault.marboud@gmail.com>
Date:   Sun May 7 11:03:37 2023 +0200

    lang: Fix incorrectly checking the first init constraint (coral-xyz#2483)
vadorovsky pushed a commit to Lightprotocol/anchor that referenced this pull request Jun 20, 2023
Co-authored-by: acheron <acheroncrypto@gmail.com>
ananas-block pushed a commit to Lightprotocol/anchor that referenced this pull request Jun 20, 2023
Co-authored-by: acheron <acheroncrypto@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

new event api
6 participants