{"id":19460134,"url":"https://github.com/offchainlabs/cuckoocache","last_synced_at":"2025-05-01T09:16:37.641Z","repository":{"id":223482784,"uuid":"755698539","full_name":"OffchainLabs/cuckoocache","owner":"OffchainLabs","description":null,"archived":false,"fork":false,"pushed_at":"2024-02-21T11:20:24.000Z","size":75,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":22,"default_branch":"main","last_synced_at":"2025-05-01T09:16:31.676Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/OffchainLabs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-02-10T20:39:30.000Z","updated_at":"2024-11-26T03:06:17.000Z","dependencies_parsed_at":"2024-02-20T13:54:37.975Z","dependency_job_id":"a1a7abaa-69ec-44e0-bc27-07211a5de6a8","html_url":"https://github.com/OffchainLabs/cuckoocache","commit_stats":null,"previous_names":["offchainlabs/cuckoocache"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OffchainLabs%2Fcuckoocache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OffchainLabs%2Fcuckoocache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OffchainLabs%2Fcuckoocache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OffchainLabs%2Fcuckoocache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OffchainLabs","download_url":"https://codeload.github.com/OffchainLabs/cuckoocache/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251850182,"owners_count":21653978,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-10T17:35:41.736Z","updated_at":"2025-05-01T09:16:37.555Z","avatar_url":"https://github.com/OffchainLabs.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"This implements an on-chain cache index, and corresponding off-chain local node cache, to allow better pricing of accesses to read-only data, \nsuch as contract code, from the Ethereum state tree. \n\n### On-chain and off-chain components\n\nOn-chain we store a cache index, which tracks a set of items\nthat are deemed to be in-cache. The on-chain index does not\nstore the actual data, just a data structure allowing to\ndetermine whether a particular item is in-cache. The state of\nthe on-chain index is part of the consensus state of the \nArbitrum chain.\n\nOff-chain we store an actual cache, which will be managed\nseparately by each node's execution engine. Different nodes \ncan have differently sized caches.\n\nThe mechanism maintains an *inclusion property* which \nguarantees that if an item is in the on-chain index, then it\nis in the cache of every local node. This makes it safe to\ncharge less gas for accesses to items that are in-cache.\n\n### How to use this\n\nTo use the mechanism with an Arbitrum Nitro chain, you need\nto implement three interfaces:\n\n`onChainStorage.OnChainStorage` is called by the cache to \nread and write the on-chain storage that backs the on-chain\ncache index. The interface provides a key-value store\nwith both keys and values having type `common.Hash`.\n\n`cacheKeys.LocalNodeCacheKey` is the type of key used to index the \nlocal node's cache. For example, if cache items are indexed\nby `common.Address` then you should provide an implementation\nof `cacheKeys.LocalNodeCacheKey` that contains a `common.Address`.\n(For `common.Address` this is already provided, as type\ntype `cacheKeys.AddressLocalCacheKey`)\nImplementations of this interface must provide \na `ToCacheKey()` method that returns a 24-byte digest of \nthe key, which will typically be a truncated hash of the key.\nThe digest should be a hash or similar pseudorandom-like\nfunction of the key, to get the best efficiency from the\ncache.\n\n`cacheBackingStore.CacheBackingStore` is called by the cache \nto fetch an item (presumably from some database) that is \ngoing to be cached.\n\nThe main configuration choice is how large the cache will be.\nFirst, choose the capacity of the on-chain index. Then each\nnode can choose the capacity of its own local cache.\n\n*The capacity of every local node cache must be greater than or\nequal to the capacity of the on-chain index.* This is required\nin order to guarantee the inclusion property. So you should\nchoose the capacity of the on-chain index to be the capacity\nthat you're willing to force upon the minimally-resourced\nnode.\n\n### Code examples\n\nTo connect to the on-chain cache index, do\n\n`cacheIndex := onChainIndex.OpenOnChainCuckooTable(storage, capacity)`\n\nwhere `storage` is an instance of `onChainStorage.OnChainStorage` usable\nfor reading and writing the index's on-chain state, and `capacity` is the\nmaxmimum number of items you'll allow in the index.\n\nNote that `OpenOnChainCuckooTable` assumes that it is connecting to an on-chain\nstructure that is already initialized and might be non-empty. If you need to\ninitialize a fresh on-chain index, do \n\n`cacheIndex.Initialize(capacity)`.\n\nHaving opened the on-chain index, you can now initialize the local node cache \nby doing\n\n`cache := NewLocalNodeCache[CacheKeyType](capacity, onChainIndex, backingStore)`\n\nHere `capacity` is the capacity you want for the local node cache, which\ncan be different on different nodes, but must be greater than or equal to\nthe capacity of the on-chain index. If you pass in a `capacity` less than\nthe capacity of `onChainIndex.Capacity` then `cache` will silently use\na capacity equal to `onChainIndex.Capacity`.  So it's safe to pass in \na `capacity` of zero, and you'll get the smallest local node cache that is\nsafe.\n\nHaving set up your local node cache, you can now read items:\n\n`data, wasCacheHit := ReadItemFromLocalCache(cache, itemKey)`\n\n`data` is a byte slice containing the item's data, and `wasCacheHit` will\nbe true iff the access was a hit in the on-chain index.\n\nIf you need to flush the caches, do\n\n`FlushLocalNodeCache(cache, alsoFlushOnChain)`\n\nThe will make the local node cache empty, and if `alsoFlushOnChain` is true\nit will also make the on-chain index empty. You can also flush a single\nitem by doing\n\n`FlushOneItemFromLocalNodeCache(cache, itemKey, alsoFlushOnChain)`\n\n### Cache replacement policies\n\nThe local node cache uses an LRU (Least Recently Used)\nreplacement policy.\n\nThe on-chain index uses a generational cache replacement\npolicy, in order to reduce the number of accesses to storage.\n(This is sufficient to guarantee the inclusion property, because\nwe can prove that a generational cache of capacity `N` always\ncontains a subset of the items that would be in an LRU cache\nof capacity `N` or greater.)\n\nGenerational caching keeps a current generation number, which\nincrements occasionally. Every item is tagged with the latest\ngeneration in which it was accessed. If the current generation \nis `G` then an item is in-cache if its latest access was in \ngeneration `G` or `G-1`. For a cache of capacity `C`,\nthe generation number increments if an access would cause the\nnumber of in-cache items to be greater than `C` or \nthe number of items in the current generation to be\ngreater than `3C/4`.\n\nGenerational replacement can be seen as an approximation to\nLRU. The main advantage of generational over LRU is that\ngenerational makes many fewer writes to storage. With\ngenerational, an access to an item in the latest generation \nrequires no changes to the index, and any other access requires\nonly one item in the index to be created or modified.\nBy contrast, a typical implementation of LRU would require \nmodifying at least three places in storage on every access\n(except for accesses to the most recently used item). So we\noptimize by using generational in the on-chain index, where\nstate changes are expensive because the state is in consensus;\nbut we use LRU in the local node cache where state changes are\ncheaper.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foffchainlabs%2Fcuckoocache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foffchainlabs%2Fcuckoocache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foffchainlabs%2Fcuckoocache/lists"}