```
---
fip: <to be assigned>
title: Actor events
author: @raulk, @stebalien
discussions-to: <URL>
status: Draft
type: Technical Core
category: Core
created: <date created on, in ISO 8601 (yyyy-mm-dd) format>
spec-sections:
- <section-id>
- <section-id>
requires (*optional): <FIP number(s)>
replaces (*optional): <FIP number(s)>
---
```
## Simple Summary
This FIP adds the ability for actors to emit events during execution. Events are
discrete self-contained payloads signalling that some externally-relevant action
or transition has ocurred during the execution of an actor. Events enhance
observability by external agents, and may eventually become the source of
internal triggers.
## Abstract
This FIP introduces a minimal design for actor events, including their schema, a new syscall,
a change in the `MessageReceipt` structure, and mechanics to commit these
execution side effects on the chain. By minimal we mean that the protocol stays
largely unopinionated and unprescriptive around eventual indexing, traversal,
and proofs (e.g. inclusion proofs for light clients), and other future features
build on events.
## Change Motivation
There are two main motivations for introducing actor events at this time.
1. They have been long missing from the protocol, forcing Filecoin network
monitoring or accounting tools to fork and instrument built-in actor code
and/or observe state changes by using high-level JSON-RPC methods like
`StateReplay`. Once this feature is introduced, built-in actors could emit
events when power changes, sectors are onboarded, deals are made, sectors are
terminated, penalties are incurred, etc.
2. The upcoming introduction of the Filecoin EVM runtime necessitates an event
mechanism to handle the LOG{0..4} opcodes, and the corresponding Ethereum
JSON-RPC methods.
## Specification
### New chain types
We introduce a DAG-CBOR encoded `Event` type to represent actor events. It is
inspired by structured logging concepts (key-value entries).
It includes an `indexed` bitmap to indicate which entries are to be indexed. At
this stage, this information is used merely as a client hint; in the future it
may be built upon to introduce protocol-level features like populating
(adaptable) bloom filters, block-level indices, on-chain event subscriptions,
and more.
```rust
/// Represents an event emitted throughout actor execution.
struct Event {
/// Key values making up this event.
entries: Vec<Entry>,
/// A hint specifying to clients which indices of `entries` should be indexed,
/// encoded as a plain bitmap of bit length equal to the length of the entries list.
indexed: [u8],
}
struct Entry {
/// The key of this event.
key: String,
/// Any DAG-CBOR encodeable type.
value: any,
}
```
Choosing a _list of tuples_ instead of a map representation is deliberate, as it
enables repeatable keys, and, thus, array-like entries, e.g. signers in a
multisig transaction.
We expressly discarded solutions involving an opaque event payload accompanied
by some form of a type discriminator, as that would necessitate an IDL upfront
to interpret the payload. We believe that this unwrapped data model enables
straightforward introspection and comprehension by chain explorers, developer
tools, and monitoring tools.
Over time, we expect the community to standardise on a set of keys through FRC
proposals.
> TODO:
> - Define maximum lengths for entries, key size, value size?
> - Do we want to introduce standard keys now, e.g. type?
**Event examples**
The following examples demonstrate how to use these structures in practice.
_Fungible token transfer_
```rust
Event {
entries: [
("type", "transfer"),
("sender", <id address>),
("receiver", <id address>),
("amount", <token amount>),
],
indexed: 1110, // bitmap hinting to index type, sender, receiver
}
```
_Non-fungible token transfer_
```rust
Event {
entries: [
("type", "transfer"),
("sender", <id address>),
("receiver", <id address>),
("token_id", <identifier>),
],
indexed: 1111, // bitmap hinting to index all fields
}
```
_Multisig approval_
```rust
Event {
entries: [
("type", "approval"),
("signer", <id address>),
("signer", <id address>),
("signer", <id address>),
("callee", <id address>),
("amount", <token amount>),
("msg_cid", <message cid>),
],
indexed: 1111000, // bitmap hinting to index the type and all signers
}
```
### Chain commitment
The existing `MessageReceipt` chain data structure is augmented with a new
`events` field:
```rust
struct MessageReceipt {
exit_code: ExitCode,
return_data: RawBytes,
gas_used: i64,
events: Vec<Cid>, // new field: Vec<Event>
}
```
During message execution, DAG-CBOR encoded Events are accumulated inside the
FVM, in the same order they were emitted. Upon finishing, the FVM computes the
CID of every event using the BLAKE2b-256 multihash and the DAG-CBOR multicodec,
and returns the list of CIDs in the `events` field, preserving the order.
Events are committed on chain implicitly through the root CID of the
`BlockHeader#ParentMessageReceipts` HAMT.
### Client handling
Honouring indexing hints is optional but highly encouraged at this stage.
Clients may offer new JSON-RPC operations to subscribe to and query events.
Clients may wish to track event data in a dedicated store, segregated from the
chain store, potentially backed by a different database engine more suited to
the expected write, query, and indexing patterns.
How event writes are routed to a different store is an implementation detail.
However, clients can rely on the timing difference between when event CIDs are
returned (at message execution), and when event IPLD blocks are written to the
store (at Machine flush time), to identify writes for events and route them to
the appropriate store.
### New `vm::emit_event` syscall
We define a new syscall under the `vm` namespace to enable actors to emit
events.
```rust
/// Emits an actor event. The supplied payload must be a DAG-CBOR encoded Event type.
/// The FVM will validate the structural, syntatic, and semantic correctness of the
/// supplied payload and fail with `IllegalArgument` if the payload was invalid.
///
/// Calling this syscall may immediately halt execution with an out of gas error, if
/// such condition arises.
fn emit_event(evt_off: *const u8, evt_len: u32) -> Result<()>;
```
We expect FVM SDKs to offer utilities, macros, and sugar to ergonomically
construct event payloads.
**Gas costs**
In addition to the [syscall gas cost], emitting an event carries the following
dynamic costs:
- Per non-indexed entry: <<TODO>> gas per <<TODO>>.
- Per indexed entry: <<TODO>> gas per <<TODO>>.
> TODO need to make syscall gas dynamic based on param length
The following limits apply. Exceeding these limits will result in the event not
being emitted, and the call failing with `IllegalArgument`:
> - Total event payload size: <<TODO, if any; may be bound by gas, once syscall
> gas varies based on param length?>>
> - Maximum number of keys: <<TODO, if any; may be bound by gas>>
> - Maximum number of indexed keys: <<TODO, if any; may be bound by gas>>
### Mapping to EVM logs
> This is defined here for convenience, and will be moved to the upcoming
> Filecoin EVM FIP once submitted.
TODO.
## Design Rationale
<!--The rationale fleshes out the specification by describing what motivated the design and why particular design decisions were made. It should describe alternate designs that were considered and related work, e.g. how the feature is supported in other languages. The rationale may also provide evidence of consensus within the community, and should discuss important objections or concerns raised during discussion.-->
TODO.
## Backwards Compatibility
<!--All FIPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The FIP must explain how the author proposes to deal with these incompatibilities. FIP submissions without a sufficient backwards compatibility treatise may be rejected outright.-->
TODO.
## Test Cases
<!--Test cases for an implementation are mandatory for FIPs that are affecting consensus changes. Other FIPs can choose to include links to test cases if applicable.-->
TODO.
## Security Considerations
<!--All FIPs must contain a section that discusses the security implications/considerations relevant to the proposed change. Include information that might be important for security discussions, surfaces risks and can be used throughout the life cycle of the proposal. E.g. include security-relevant design decisions, concerns, important discussions, implementation-specific guidance and pitfalls, an outline of threats and risks and how they are being addressed. FIP submissions missing the "Security Considerations" section will be rejected. A FIP cannot proceed to status "Final" without a Security Considerations discussion deemed sufficient by the reviewers.-->
TODO.
## Incentive Considerations
<!--All FIPs must contain a section that discusses the incentive implications/considerations relative to the proposed change. Include information that might be important for incentive discussion. A discussion on how the proposed change will incentivize reliable and useful storage is required. FIP submissions missing the "Incentive Considerations" section will be rejected. An FIP cannot proceed to status "Final" without a Incentive Considerations discussion deemed sufficient by the reviewers.-->
TODO.
## Product Considerations
<!--All FIPs must contain a section that discusses the product implications/considerations relative to the proposed change. Include information that might be important for product discussion. A discussion on how the proposed change will enable better storage-related goods and services to be developed on Filecoin. FIP submissions missing the "Product Considerations" section will be rejected. An FIP cannot proceed to status "Final" without a Product Considerations discussion deemed sufficient by the reviewers.-->
TODO.
## Implementation
<!--The implementations must be completed before any core FIP is given status "Final", but it need not be completed before the FIP is accepted. While there is merit to the approach of reaching consensus on the specification and rationale before writing code, the principle of "rough consensus and running code" is still useful when it comes to resolving many discussions of API details.-->
TODO.
## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
[`MessageReceipt` type]: https://spec.filecoin.io/#section-systems.filecoin_vm.message.message-semantic-validation
[syscall gas cost]: https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0032.md#syscall-gas