-
Notifications
You must be signed in to change notification settings - Fork 5
Add PS mutation #131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Add PS mutation #131
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
be32146
add mutatable private state in live backend
andrew-fleming ddcd7c2
add tests
andrew-fleming af51044
fix doc
andrew-fleming 7a07f8d
add live mutation tests
andrew-fleming a7a8af4
serialize rmw sequence
andrew-fleming bc49471
use buffer in tests
andrew-fleming c52c246
fix tsdoc links
andrew-fleming 2027cc3
resolve updatePrivateState to the written state
andrew-fleming ef87666
fix docs
andrew-fleming 9cc0735
document per-instance scope of queue
andrew-fleming 965688c
extract PrivateStateMutator
andrew-fleming 029c7d1
add PrivateStateMutator tests
andrew-fleming File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| /** | ||
| * Serializes read-modify-write access to a contract's private state. | ||
| * | ||
| * Built from a backend's `getPrivateState`/`setPrivateState` pair, it queues | ||
| * every mutation so a read-modify-write (`update`) can't interleave with another | ||
| * mutation and lose an update (a lost-update race). It is backend-agnostic: the | ||
| * dry, live, and future `sim` backends all reuse it by supplying their own | ||
| * read/write closures. | ||
| * | ||
| * The guarantee is scoped to this instance. On live, two `PrivateStateMutator`s | ||
| * (or two processes) targeting the same `privateStateProvider` + `privateStateId` | ||
| * still race — both read the provider, last write wins — since the queue is | ||
| * local. Cross-instance atomicity would need provider-side | ||
| * compare-and-set/versioning and is out of scope; tests drive one simulator per | ||
| * private state, so the per-instance queue is sufficient in practice. | ||
| * | ||
| * @template P - Private state type. | ||
| */ | ||
| export class PrivateStateMutator<P> { | ||
| /** Tail of the mutation queue; each op runs after the previous drains. */ | ||
| #chain: Promise<unknown> = Promise.resolve(); | ||
| readonly #read: () => Promise<P>; | ||
| readonly #write: (next: P) => Promise<void>; | ||
|
|
||
| /** | ||
| * @param read - Reads the current private state (backend `getPrivateState`). | ||
| * @param write - Replaces the whole private state (backend `setPrivateState`). | ||
| */ | ||
| constructor(read: () => Promise<P>, write: (next: P) => Promise<void>) { | ||
| this.#read = read; | ||
| this.#write = write; | ||
| } | ||
|
|
||
| /** | ||
| * Runs `op` once the queue drains, serialized against every other mutation. | ||
| * The returned promise settles with `op`'s result (or rejection) for the | ||
| * caller; the queue tail deliberately swallows rejections so a failed op does | ||
| * not poison subsequent mutations. | ||
| * | ||
| * @param op - The operation to run once the queue drains. | ||
| * @returns `op`'s result. | ||
| */ | ||
| enqueue<T>(op: () => Promise<T>): Promise<T> { | ||
| const run = this.#chain.then(op); | ||
| this.#chain = run.then( | ||
| () => undefined, | ||
| () => undefined, | ||
| ); | ||
| return run; | ||
| } | ||
|
|
||
| /** | ||
| * Replaces the whole private state, serialized against other mutations. | ||
| * | ||
| * @param next - The new private state. | ||
| */ | ||
| set(next: P): Promise<void> { | ||
| return this.enqueue(() => this.#write(next)); | ||
| } | ||
|
|
||
| /** | ||
| * Read-modify-write, serialized end-to-end so the read and write are atomic | ||
| * against other mutations on this instance. Resolves to the state that was | ||
| * written, so callers need no follow-up read. | ||
| * | ||
| * @param updater - A partial patch to shallow-merge, or a function that | ||
| * receives the current state and returns the next. | ||
| * @returns The private state that was written. | ||
| */ | ||
| update(updater: Partial<P> | ((prev: P) => P)): Promise<P> { | ||
| return this.enqueue(async () => { | ||
| const prev = await this.#read(); | ||
| const next = | ||
| typeof updater === 'function' | ||
| ? (updater as (p: P) => P)(prev) | ||
| : { ...prev, ...updater }; | ||
| await this.#write(next); | ||
| return next; | ||
| }); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.