diff --git a/docs/home/300-react-to-events/3-funnel-types/1-intro.md b/docs/home/300-react-to-events/3-funnel-types/1-common-concepts/1-intro.md similarity index 90% rename from docs/home/300-react-to-events/3-funnel-types/1-intro.md rename to docs/home/300-react-to-events/3-funnel-types/1-common-concepts/1-intro.md index 07d59e9c..60532953 100644 --- a/docs/home/300-react-to-events/3-funnel-types/1-intro.md +++ b/docs/home/300-react-to-events/3-funnel-types/1-common-concepts/1-intro.md @@ -4,7 +4,7 @@ - The blockchain node (`CHAIN_URI`) - The deployed Paima Contract address (`CONTRACT_ADDRESS`) -- The set of [Primitives](../2-primitive-catalogue/1-introduction.md) that the developer provided +- The set of [Primitives](../../2-primitive-catalogue/1-introduction.md) that the developer provided Notably, funnels play a key role in allowing Paima to not just synchronize a single chain, but also combine multiple different data sources together such as DA layers, merging L1+L2 data together, or merging NFT data from different chains. @@ -51,9 +51,9 @@ export interface ChainData { When extensions are used, the runtime must start polling from a block height that was before any of the contracts referenced in the Primitives were deployed. Thus all events that take place (ie. all NFT mints/transfer events) are accounted for and are saved in the DB so the state machine has proper access to a valid copy of the current state of the contract. We call this the _pre-sync_ phase. -In other words, this function is meant to gather events for [Primitives](../2-primitive-catalogue/1-introduction.md#accessing-the-collected-data) that happened before `START_BLOCKHEIGHT`, or the equivalent point if other primitives from other chains are used. +In other words, this function is meant to gather events for [Primitives](../../2-primitive-catalogue/1-introduction.md#accessing-the-collected-data) that happened before `START_BLOCKHEIGHT`, or the equivalent point if other primitives from other chains are used. -Any [scheduled events](../1-scheduled-events.md) that are created during this +Any [scheduled events](../../1-scheduled-events.md) that are created during this phase are expected to be scheduled at the beginning of the _sync_ phase, which can be at either `START_BLOCKHEIGHT` or 0 (if using emulated block heights). This way any state derived from events by the state transition function can still be diff --git a/docs/home/300-react-to-events/3-funnel-types/1-common-concepts/2-parallel-networks.mdx b/docs/home/300-react-to-events/3-funnel-types/1-common-concepts/2-parallel-networks.mdx new file mode 100644 index 00000000..ee4a72c8 --- /dev/null +++ b/docs/home/300-react-to-events/3-funnel-types/1-common-concepts/2-parallel-networks.mdx @@ -0,0 +1,120 @@ +import Merge from "./parallel-funnel-merge.svg"; +import MergeDelayed from "./parallel-funnel-merge-delayed.svg"; +import ParallelToMain from "./parallel-to-main.svg"; +import DataAddition from "./parallel-data-addition.svg"; +import Finalized from "./parallel-finalized.svg"; + +# Parallel funnels + +## Merging state from multiple chains + +When running a game, there is always a (single) primary EVM network which is +synchronized by the [block funnel](../block-funnel). This is the network that has +the Paima L2 contract deployed, and it's the one that provides the inputs for +the game. In addition to this, there are funnel types that are used for +synchronizing additional networks in parallel: + +- [parallel evm funnel](../parallel-evm-funnel) +- [carp funnel](../carp-funnel) +- [mina funnel](../mina-funnel) + +Conceptually these funnels are independent from each other, so there can be as +many as needed, but they all depend on the block funnel. + + + +The reason for this is +that the engine makes it _appear_ as if events happened in a single network by +_merging_ the events from the parallel chains into the blocks from the block +funnel. This provides a single interface where adding an extension in an extra +network is no different than adding an extension in a single network. + + + +### Determinism + +Merging of data is done in a way to ensure that the state transition is deterministic. + +For example,
+if on the main chain we have blocks with timestamps 3 and 5,
+and on the parallel chain we have blocks with timestamps 4 and 5,
+the events in the parallel chain will look as if they happened in the main chain's block that has timestamp 5. +And this is always the same regardless of the time of the sync, since the chains are always processed in tandem. +Note that it is possible that there are no blocks to merge at a certain point (if no events we're monitoring occur in these blocks). + +Visually: + + + +## Finalizing blocks + +We cannot go back in time to add information to old blocks that have already been parsed by the game's state machine. +Therefore, it means that a block can only be considered finalized and ready to be given to the state machine once we're certain we have all the information for all chains being monitored. +The only way for us to know that we have all the information for a block is if the _latest_ [**confirmed**](#confirmation-depth) timestamp of all networks we're monitoring is more recent *or* equal ( depending on whether the network can have multiple blocks with the same timestamp), than the timestamp of the main chain block timestamp we're looking at. + +*Note*: this behavior helps protect apps built with Paima from getting in a bad state. If an RPC you're connecting to for a network gets stuck, it should stop block production for the entire application. +Otherwise, it can break determinism because your node (that is missing events from the parallel chain RPC that is stuck) will see a different block history from somebody else running a node connecting to a properly functioning RPC for that parallel chain. + + + +## Delayed state + +When getting information from a chain with probabilistic finality, it's +necessary to have a setup that can avoid rollbacks, since there is no way in for +the engine to handle that otherwise. Because of this, the parallel funnels have +the ability to run in a delayed state. This involves two different variables: + +``` +confirmationDepth: number +delay: number +``` + +### Confirmation depth + +`confirmationDepth` is a parameter that is based on blocks. A +`confirmationDepth` of 0 would be proper for a network with instant finality. +Increasing the argument depends on the underlying network, and the amount of +confidence desired. + +### Delay + +If the `delay` setting is used, the parallel chain is delayed by this amount of +seconds. This is equivalent to either subtracting the delay for the timestamps +of the main network before merging (without changing the result), or to adding +the delay to the parallel network timestamps. In that case the merge process +changes in the following way (with a delay of 60 seconds). + + + +This has the effect that no events newer than `current time - delay` will be +processed by the funnel (relative to the main chain's latest synced block). + +### Guidelines + +In general both of these are needed for the funnel to properly function, and a +good guideline is to set `delay` to a value greater or equal than +`confirmationDepth * block_production_speed`. Having a greater value adds +latency for the engine to react to events, but decreases the chances of the +funnel needing to wait for block production in order to get a confirmed block +(see [finalizing blocks](#finalizing-blocks)). This is particularly problematic +because a single network may stall all of the other ones. + +Considering the above, setting up a `confirmationDepth` without `delay` it's not +really advised, since the funnel will stall with every block. On the other hand, +it's possible to set a `delay` without a `confirmationDepth`, but if the +underlying network stops producing blocks for some reason, it may lead to +eventually finalizing a block that it's not stable, unless the underlying +network has some extra guarantees based exclusively on timings. + +## Performance implications + +Leveraging parallel funnels has multiple performance implications. In some cases the performance impact can be mitigated by [emulated blocks](./400-stable-tick-rate-funnel.mdx), +but we will cover all performance implications in this section + +### Networking overhead + +From Paima's perspective, a block can only be considered complete and ready to send to the game's state machine after it's fetched all necessary information. +That means that fetching blocks is only as fast as your slowest connection when fetching the latest block. + +If you're connecting to a public node for a network and receiving data from that node is slow (ex: geographically far from you, running on cheap hardware, etc.), +the delay in fetching data from that node will delay the final block creation process and cause slowdowns in your game node. \ No newline at end of file diff --git a/docs/home/300-react-to-events/3-funnel-types/1-common-concepts/_category_.json b/docs/home/300-react-to-events/3-funnel-types/1-common-concepts/_category_.json new file mode 100644 index 00000000..79b28bbc --- /dev/null +++ b/docs/home/300-react-to-events/3-funnel-types/1-common-concepts/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Common concepts" +} \ No newline at end of file diff --git a/docs/home/300-react-to-events/3-funnel-types/parallel-data-addition.svg b/docs/home/300-react-to-events/3-funnel-types/1-common-concepts/parallel-data-addition.svg similarity index 100% rename from docs/home/300-react-to-events/3-funnel-types/parallel-data-addition.svg rename to docs/home/300-react-to-events/3-funnel-types/1-common-concepts/parallel-data-addition.svg diff --git a/docs/home/300-react-to-events/3-funnel-types/parallel-finalized.svg b/docs/home/300-react-to-events/3-funnel-types/1-common-concepts/parallel-finalized.svg similarity index 100% rename from docs/home/300-react-to-events/3-funnel-types/parallel-finalized.svg rename to docs/home/300-react-to-events/3-funnel-types/1-common-concepts/parallel-finalized.svg diff --git a/docs/home/300-react-to-events/3-funnel-types/1-common-concepts/parallel-funnel-merge-delayed.svg b/docs/home/300-react-to-events/3-funnel-types/1-common-concepts/parallel-funnel-merge-delayed.svg new file mode 100644 index 00000000..63b62de7 --- /dev/null +++ b/docs/home/300-react-to-events/3-funnel-types/1-common-concepts/parallel-funnel-merge-delayed.svg @@ -0,0 +1,327 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 61 + 63 + Delay shifted + 65 + 67 + + - 60 s + Parallel chain + Main chain + Real time + diff --git a/docs/home/300-react-to-events/3-funnel-types/parallel-evm-funnel-merge.svg b/docs/home/300-react-to-events/3-funnel-types/1-common-concepts/parallel-funnel-merge.svg similarity index 100% rename from docs/home/300-react-to-events/3-funnel-types/parallel-evm-funnel-merge.svg rename to docs/home/300-react-to-events/3-funnel-types/1-common-concepts/parallel-funnel-merge.svg diff --git a/docs/home/300-react-to-events/3-funnel-types/1-common-concepts/parallel-to-main.svg b/docs/home/300-react-to-events/3-funnel-types/1-common-concepts/parallel-to-main.svg new file mode 100644 index 00000000..24e638fb --- /dev/null +++ b/docs/home/300-react-to-events/3-funnel-types/1-common-concepts/parallel-to-main.svg @@ -0,0 +1,103 @@ + + + + + + + parallel evmfunnel + + + + + + + + + carpfunnel + minafunnel + diff --git a/docs/home/300-react-to-events/3-funnel-types/200-configuration.md b/docs/home/300-react-to-events/3-funnel-types/200-configuration.md index 81e4d87b..4c4bfad1 100644 --- a/docs/home/300-react-to-events/3-funnel-types/200-configuration.md +++ b/docs/home/300-react-to-events/3-funnel-types/200-configuration.md @@ -50,8 +50,8 @@ presyncStepSize: number ``` **Note:** These do *not* default to the value in the corresponding -*environment configuration if not provided. Paima only defaults to the values in -*the ENV file when the entire config file is missing. +environment configuration if not provided. Paima only defaults to the values in +the ENV file when the entire config file is missing. All the variables except `funnelBlockGroupSize` have the same purpose and default values as they have in the environment configuration. Please refer to @@ -60,6 +60,10 @@ default values as they have in the environment configuration. Please refer to `funnelBlockGroupSize` it's used to control how many blocks are fetched in a single request per network, which helps if a network has a lower rate limit. +Also, for funnels that synchronize a parallel network, there are options to set +up a delayed state. See [this](common-concepts/parallel-networks#delayed-state) +for more details. + ## Example ```yaml @@ -74,7 +78,7 @@ Hardhat1: blockTime: 2 paimaL2ContractAddress: '0x5FbDB2315678afecb367f032d93F642f64180aa3' -Hardat2: +Hardhat2: type: evm-other chainUri: 'http://localhost:8546' chainId: 31338 diff --git a/docs/home/300-react-to-events/3-funnel-types/500-parallel-evm-funnel.mdx b/docs/home/300-react-to-events/3-funnel-types/500-parallel-evm-funnel.mdx index e53224de..66b0d432 100644 --- a/docs/home/300-react-to-events/3-funnel-types/500-parallel-evm-funnel.mdx +++ b/docs/home/300-react-to-events/3-funnel-types/500-parallel-evm-funnel.mdx @@ -1,7 +1,3 @@ -import Merge from "./parallel-evm-funnel-merge.svg"; -import ParallelToMain from "./parallel-to-main.svg"; -import DataAddition from "./parallel-data-addition.svg"; -import Finalized from "./parallel-finalized.svg"; import Search from "./parallel-search.png"; # Parallel EVM funnel @@ -16,8 +12,6 @@ For example, this allows you to have your game settle user transactions to a che ## Conceptually - - This funnel has the following steps: 1. Fetch blocks & timestamps from the underlying funnel (the main chain you are syncing). @@ -25,45 +19,6 @@ This funnel has the following steps: 4. Use `eth_getLogs` to get the [primitives](../2-primitive-catalogue/1-introduction.md) configured to this funnel in the block range. 5. Merge the primitives fetched from the parallel funnel with the block from the underlying funnel. - - -## Determinism - -Merging of data is done in a way to ensure that the state transition is deterministic. - -For example,
-if on the main chain we have blocks with timestamps 3 and 5,
-and on the parallel chain we have blocks with timestamps 4 and 5,
-the events in the parallel chain will look as if they happened in the main chain's block that has timestamp 5. -And this is always the same regardless of the time of the sync, since the chains are always processed in tandem. -Note that it is possible that there are no blocks to merge at a certain point (if no events we're monitoring occur in these blocks). - - - -## Finalizing blocks - -We cannot go back in time to add information to old blocks that have already been parsed by the game's state machine. -Therefore, it means that a block can only be considered finalized and ready to be given to the state machine once we're certain we have all the information for all chains being monitored. -The only way for us to know that we have all the information for a block is if the latest timestamp of all networks we're monitoring is more recent (not equal since some networks have multiple blocks with the same timestamp) than the timestamp of the main chain block timestamp we're looking at. - -*Note*: this behavior helps protect apps built with Paima from getting in a bad state. If an RPC you're connecting to for a network gets stuck, it should stop block production for the entire application. -Otherwise, it can break determinism because your node (that is missing events from the parallel chain RPC that is stuck) will see a different block history from somebody else running a node connecting to a properly functioning RPC for that parallel chain. - - - -## Performance implications - -Leveraging parallel funnels has multiple performance implications. In some cases the performance impact can be mitigated by [emulated blocks](./400-stable-tick-rate-funnel.mdx), -but we will cover all performance implications in this section - -### Networking overhead - -From Paima's perspective, a block can only be considered complete and ready to send to the game's state machine after it's fetched all necessary information. -That means that fetching blocks is only as fast as your slowest connection when fetching the latest block. - -If you're connecting to a public node for a network and receiving data from that node is slow (ex: geographically far from you, running on cheap hardware, etc.), -the delay in fetching data from that node will delay the final block creation process and cause slowdowns in your game node. - ### Connecting to networks that produce blocks on-demand Some networks only produce blocks if there are actually transactions on the network (otherwise, nothing happens) such as Arbitrum. @@ -73,9 +28,8 @@ The [emulated block funnel](./400-stable-tick-rate-funnel.mdx) can help mitigate ### Connecting to networks with slow blocks -Many networks such as Ethereum L1 have relatively slow block times, making them hard to use as a parallel EVM without slowing down the whole system. - -Improving support for these networks is still a roadmap item. +Many networks such as Ethereum L1 have relatively slow block times. This can be +mitigated by relying on a [delayed state](additional-networks#delayed-state). ## Finding parallel chain blocks @@ -88,7 +42,7 @@ We achieve this in 2 steps: ### 1) Find key blocks during startup with binary search When booting up Paima Engine, there are 2 blocks we are interested in knowing: -1. When the [presync](./1-intro.md#readpresyncdata-function) ends (the last block in the parallel chain that appears before the main chain starting block height) +1. When the [presync](./1-common-concepts/1-intro.md#readpresyncdata-function) ends (the last block in the parallel chain that appears before the main chain starting block height) 2. The earliest parallel chain block that whose events might be included in the next main chain block we have to sync Syncing blocks one-by-one until we find these points would be too slow. diff --git a/docs/home/300-react-to-events/3-funnel-types/600-mina-funnel.mdx b/docs/home/300-react-to-events/3-funnel-types/600-mina-funnel.mdx index 45fe5ddc..3f91192f 100644 --- a/docs/home/300-react-to-events/3-funnel-types/600-mina-funnel.mdx +++ b/docs/home/300-react-to-events/3-funnel-types/600-mina-funnel.mdx @@ -1,5 +1,3 @@ -import Merge from "./mina-funnel-merge.svg"; - # Mina funnel ## Configuration @@ -46,18 +44,6 @@ individually, and progress is tracked in the `cde_tracking_cursor_pagination` table. The presync finishes when there are no more events in the range for any of the configured extensions. -## Determinism - -The merging of events with the underlying funnel works similarly to the -[parallel evm funnel](500-parallel-evm-funnel.mdx). The events from the Mina -chain are delayed by the `delay` configuration instead, to account for -probabilistic finality. This is done by subtracting the delay from the main evm -chain for the merging process (without affecting the timestamp of the merged -`ChainData`). This has the effect that if the main evm chain is running in real -time, the events that are merged into the current block are `delay` seconds old. - - - ## Finalizing blocks Since events parsed by the state transition are final, we need to be sure that diff --git a/docs/home/300-react-to-events/3-funnel-types/mina-funnel-merge.svg b/docs/home/300-react-to-events/3-funnel-types/mina-funnel-merge.svg deleted file mode 100644 index 5df9ac52..00000000 --- a/docs/home/300-react-to-events/3-funnel-types/mina-funnel-merge.svg +++ /dev/null @@ -1,307 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 61 - 63 - Delay shifted - 65 - 67 - - - 60 s - Mina chain - Main chain - diff --git a/docs/home/300-react-to-events/3-funnel-types/parallel-to-main.svg b/docs/home/300-react-to-events/3-funnel-types/parallel-to-main.svg deleted file mode 100644 index 8b60c66d..00000000 --- a/docs/home/300-react-to-events/3-funnel-types/parallel-to-main.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - -