Skip to content

Commit

Permalink
Merge pull request #107 from CosmWasm/aw/move-pinning-docs
Browse files Browse the repository at this point in the history
Move pinning docs
  • Loading branch information
aumetra authored Jul 23, 2024
2 parents 7f51650 + 96596e4 commit aa72a9d
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/pages/core/architecture/_meta.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"actor-model": "Actor Model",
"events": "Events",
"pinning": "Pinning",
"transactions": "Transactions"
}
102 changes: 102 additions & 0 deletions src/pages/core/architecture/pinning.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
tags: ["core", "architecture"]
---

import { Callout } from "nextra/components";

# Contract pinning

Contract pinning is a feature of the CosmWasm virtual machine which ensures that
a previously stored compiled contract code (module) is started from a dedicated
in-memory cache. Starting a module from memory takes ~45µs compared to 1.5ms
when loaded from disk (33x faster).

In contrast to the node-specific Least recently used (LRU) memory cache, pinning
**guarantees** this performance boost across the network. As a consequence wasmd
can charge discounted gas cost[^1].

<Callout>
Pinning is an incredibly valuable tool for chains to optimize gas costs.
According to a [case study][neutron-case-study] by one of our subscribers,
Neutron, pinning improved the gas cost of their contracts somewhere between 15
and 50%!
</Callout>

## The caches

CosmWasm has 3 different caches for modules:

1. `FileSystemCache` the `.module` files stored in the cache directory of the
node
2. `InMemoryCache` the LRU cache
3. `PinnedMemoryCache` a separate cache

Both memory caches (2./3.) work the same in terms of performance but their
elements are tracked separately. A pinned contract is never added to the
standard `InMemoryCache` and the size of pinned contracts is not counted towards
its cache size limit.

## Pinning and Unpinning

In order to add a contract to the `PinnedMemoryCache`, you need to call
[`Cache::pin`] in Rust or `func (vm *VM) Pin(checksum Checksum) error` in
wasmvm. To remove a contract from the cache use [`Cache::unpin`] /
`func (vm *VM) Unpin(checksum Checksum) error`. In both cases a contract is
identified by its checksum (sha256 hash of the Wasm blob).

The VM does not persist pinned memory entries. I.e. you need to call `Pin` every
time you start the process. This is implemented in [`InitializePinnedCodes` in
wasmd][initializepinnedcodes].

At the chain level pinning and unpinning are done via governance proposals. See
`MsgPinCodes`/`MsgUnpinCodes` in wasmd.

When contracts are migrated from one code to another, there is no automatic
pinning or unpinning. This is primarily since the migration of a single instance
does not mean all instances of the same code become unused. In the future we
want to provide hit stats for each checksum in order to easily find unused codes
in the pinned memory cache[^2].

## Best practices

Pinning contracts is a balance between increasing memory usage and boosting
execution speed. Contracts that are known to be heavily used should be pinned.
This can include contracts that are executed as part of begin/end block or the
IBC light client implementations of the Wasm Light Client ([08-wasm]). If a
chain is permissioned and runs on a small number of well known contracts, they
can all be pinned. A permissionless chain might select certain contracts of
strategic importance and pin them.

The estimated size of the pinned contracts is visible in the [Metrics] struct
you can access through [Prometheus]. In order to better estimate which contracts
are worth pinning, CosmWasm also exports metrics per pinned contract.

These metrics are:

- The contract size
- The number of times it was loaded from cache

That way you can better estimate which contracts are worth keeping pinned.

## History

Pinning was developed in 2021 (CosmWasm 0.14) for the Proof of Engagement
consensus system of Tgrade which required certain contracts to be executed in
every block.

[metrics]:
https://github.com/CosmWasm/wasmvm/blob/v2.0.0-rc.2/types/types.go#L174-L185
[`cache::pin`]:
https://docs.rs/cosmwasm-vm/latest/cosmwasm_vm/struct.Cache.html#method.pin
[`cache::unpin`]:
https://docs.rs/cosmwasm-vm/latest/cosmwasm_vm/struct.Cache.html#method.unpin
[08-wasm]:
https://github.com/cosmos/ibc-go/tree/main/modules/light-clients/08-wasm
[initializepinnedcodes]:
https://github.com/CosmWasm/wasmd/blob/v0.50.0/x/wasm/keeper/keeper.go#L1011-L1028
[Prometheus]: https://prometheus.io/
[neutron-case-study]:
https://medium.com/confio/neutron-case-study-optimizing-gas-usage-with-contract-pinning-5970a109c706

[^1]: https://github.com/CosmWasm/wasmd/pull/1799
[^2]: https://github.com/CosmWasm/cosmwasm/issues/2034

0 comments on commit aa72a9d

Please sign in to comment.