Skip to content

Commit

Permalink
Merge pull request CosmWasm#2225 from CosmWasm/webmaster128-patch-1
Browse files Browse the repository at this point in the history
Redirect from GAS.md and PINNING.md to docs
  • Loading branch information
webmaster128 authored Aug 28, 2024
2 parents a2a6b5d + 85b8efb commit 1d7a903
Show file tree
Hide file tree
Showing 2 changed files with 4 additions and 171 deletions.
96 changes: 2 additions & 94 deletions docs/GAS.md
Original file line number Diff line number Diff line change
@@ -1,96 +1,4 @@
# Gas

Gas is a way to measure computational expense of a smart contract execution,
including CPU time and storage cost. Its unit is 1, i.e. you can think of it as
countable points. Gas consumption is deterministic, so executing the same thing
costs the same amount of gas across all hardware and operating systems.

## CosmWasm gas vs. Cosmos SDK gas

CosmWasm charges gas for Wasm operations, calls to host functions and calls to
the Cosmos SDK. _CosmWasm gas_ is different from _Cosmos SDK gas_ as the numbers
here are much larger. Since we charge gas for arbitrary user defined operations,
we need to charge each Wasm operation individually and cannot group larger tasks
together. As a result, the gas values become much larger than in Cosmos SDK even
for very fast executions. There is a [multiplier][defaultgasmultiplier] to
translate between CosmWasm gas and Cosmos SDK. It was measured and set to 100 a
while ago and can be adjusted when necessary.

## CosmWasm gas pricing

For CosmWasm gas, the target gas consumption is 1 Teragas (10^12 gas) per
second. This idea is [inspired by NEAR][neargas] and we encourage you to read
their excellent docs on that topic.

In order to meet this target, we execute Argon2 in a test contract ([#1120]).
This is a CPU and memory intense job that does not call out into the host. At a
constant gas cost per operation of 1 (pre CosmWasm 1.0), this consumed 96837752
gas and took 15ms on our CI system. The ideal cost per operation for this system
is `10**12 / (96837752 / (15 / 1000))`: 154. This is rounded to 150 for
simplicity.

CosmWasm 2.1 update: All gas values were re-evaluated and adjusted to meet the 1
Teragas/second target mentioned above. A rerun of the Argon2 test contract
consumed 5270718300 gas with the previous cost of 150, so the operation count
was `5270718300 / 150 = 35138122`. This took 6ms on our benchmark server, so the
new cost per operation is `10**12 / (35138122 / (6 / 1000))`: 171. This is
rounded to 170 for simplicity.

Benchmarking system:

- CPU: Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz (4 cores, 8 threads)
- RAM: 32GB DDR4 2133 MHz

Each machine is different, we know that. But the above target helps us in
multiple ways:

1. Develop an intuition what it means to burn X gas or how much gas can be used
if a block should be executable in e.g. 1 second
2. Have a target for adjustments, e.g. when the Wasm runtime becomes faster or
slower
3. Allow pricing of calls that are not executed in Wasm, such as crypto APIs
4. Find significant over or underpricing

[defaultgasmultiplier]:
https://github.com/CosmWasm/wasmd/blob/v0.19.0/x/wasm/keeper/gas_register.go#L18
[neargas]: https://docs.near.org/docs/concepts/gas
[#1120]: https://github.com/CosmWasm/cosmwasm/pull/1120

## Gas overflow potential

CosmWasm gas aims for 1 Teragas/second, i.e. the uint64 range exceeds after 18
million seconds (5000 hours)<sup>1</sup>. Assuming a max supported block
execution time of 30 seconds, the gas price has to be over-priced by a factor of
614891 (614891 Teragas/second) in order to exceed the uint64 range<sup>2</sup>.
Since serious over or underpricing is considered a bug, using uint64 for gas
measurements is considered safe.

Cosmos SDK gas uses values that are smaller by a factor of 150, so those don't
overflow as well. Since no Cosmos SDK gas values are processed inside of this
repository, this is not our main concern. However, it's good to know that we can
safely pass them in uint64 fields, as long as the full range is supported. This
is the case for the C API as well as [JSON numbers](https://www.json.org/) as
long as both sides support integers in their JSON implementation. Go and Rust do
that while many other implementations don't support integers, and convert them
to IEEE-754 doubles, which has a safe integer range up to about 53 bit (e.g.
JavaScript and jq).

<sup>1</sup> Python3: `(2**64-1) / 10**12`

<sup>2</sup> Python3: `((2**64-1)/30) / 10**12`

## CosmWasm 1.x -> 2.0 changes

In all versions before 2.0, the gas values were bigger by a factor of 1000.
There is no need to have them this big and in order to reduce the risk of
overflow, the gas values were lowered in [#1599]. Here is a breakdown of what
this change entails:

| | CosmWasm 1.x | CosmWasm 2.x |
| -------------------------- | --------------------- | --------------------- |
| Cost target | 1 Teragas/millisecond | 1 Teragas/second |
| Exceeds uint64 range after | 5 hours | 5124 hours (213 days) |
| Cost per Wasm op | 150_000 | 150 |
| Multiplier | 140_000_000 | 140_000 |

[#1599]: https://github.com/CosmWasm/cosmwasm/pull/1599
This page is now maintained in the main CosmWasm docs:
<https://docs.cosmwasm.com/core/architecture/gas>
79 changes: 2 additions & 77 deletions docs/PINNING.md
Original file line number Diff line number Diff line change
@@ -1,79 +1,4 @@
# 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].

## 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](https://prometheus.io/).

## 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

[^1]: https://github.com/CosmWasm/wasmd/pull/1799

[^2]: https://github.com/CosmWasm/cosmwasm/issues/2034
This page is now maintained in the main CosmWasm docs:
<https://docs.cosmwasm.com/core/architecture/pinning>

0 comments on commit 1d7a903

Please sign in to comment.