Skip to content
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

Run module initializers during genesis #542

Merged
merged 17 commits into from
Mar 1, 2022
Merged

Run module initializers during genesis #542

merged 17 commits into from
Mar 1, 2022

Conversation

666lcz
Copy link
Contributor

@666lcz 666lcz commented Feb 23, 2022

#503

Note to reviewers: This revision passes all the tests but is not very polished yet. Just wanted to get some early feedback on the approach and improvement areas.

The first four commits are simple refactoring, and the core changes are in the fifth commit.

@666lcz
Copy link
Contributor Author

666lcz commented Feb 23, 2022

Context

To support running module initializer in genesis, I can think of two approaches:

  1. Calling the module init functions in Authority::new
  2. Submit a Publish Order for each custom module so that module initialization is taken care of like regular Publish Orders

The disadvantage of the first approach is that there're a lot more refactoring needed(see investigation below).

The disadvantage of the second approach is that the approach does not work for the Sui and Move framework lib as these two are the prerequisite for move vm(correct me if this is a false assumption). We could continue treating these two libs specially and write them directly to DB , but we have to take Approach 1 if we decide to add an init method to one of these two libraries.

Investigation for approach 1:

To execute the the module initializer, we cannot simply insert objects to the DB like what we did for storing modules. Instead, we need to run the vm with a AuthorityTemporaryStore

https://github.com/MystenLabs/fastnft/blob/871500f40a3be4de5db51252ad27021615633996/sui_core/src/authority.rs#L301

The input objects can be constructed in this way
https://github.com/MystenLabs/fastnft/blob/871500f40a3be4de5db51252ad27021615633996/sui_types/src/messages.rs#L503-L536

After we obtain the side effects from the VM, we need to call AuthorityState.update_state(temporary_store, certificate, to_signed_effects) to persist these side effects

https://github.com/MystenLabs/fastnft/blob/871500f40a3be4de5db51252ad27021615633996/sui_core/src/authority.rs#L280-L292

The self.update_state on the last line will store the CertifiedTransaction
https://github.com/MystenLabs/fastnft/blob/ae2057253484bb4b05f24cb65d33355b050730f3/sui_core/src/authority/authority_store.rs#L322-L325

To conclude, the tricky part of approach 1 is that we need to construct a CertifiedTransaction and refactor/skip a lot of logic.

My questions

  1. Which one is the better approach(approach 1 vs approach 2)?
  2. Is it feasible to construct a proper CertifiedTransaction? Or should I just create a mock one and skip some of the verification logic? Should we skip storing it in UpdateState if it's mock one?
  3. How would the Genesis process work if there's a new authority joining?

cc @sblackshear @lxfind @awelc Your suggestions are very appreciated here

@awelc
Copy link
Contributor

awelc commented Feb 23, 2022

My initial idea/suggestion was along the lines of solution #1. It seems like an easier and less invasive add-on than doing "full" publishing for the genesis code. At the same time, I believe that "publishing" has two components:

  1. The Sui side, which ultimately results in package objects being put into the storage
  2. The Move VM side which results in VM-side resolution of the published code which makes it available for subsequent call

At this point, I am not sure how the second part is done for the genesis code if we don't do full publishing but it seems like genesis code IS (somehow) available for subsequent Move call after genesis runs. Consequently, it should be possible to simply call into initializers of all modules that are available during genesis construction in the AuthorityState.new function call, and transfer the side effects from temporary storage to "main authority storage" on the Sui side.

@666lcz
Copy link
Contributor Author

666lcz commented Feb 23, 2022

transfer the side effects from temporary storage to "main authority storage" on the Sui side.

Yeah there's no question of calling into initializers. But this is the tricky part

https://github.com/MystenLabs/fastnft/blob/776b203bf21d12fb0b2f2362f21705c66b6b83f6/sui_core/src/authority.rs#L282

I was trying to understand if storing the certificates is a hard requirement or not and what's the best way of constructing them.

@awelc
Copy link
Contributor

awelc commented Feb 23, 2022

I think you can iterate over objects in temporary store and insert them to the database one by one using insert_object function:

https://github.com/MystenLabs/fastnft/blob/871500f40a3be4de5db51252ad27021615633996/sui_core/src/authority.rs#L517-L521

@666lcz
Copy link
Contributor Author

666lcz commented Feb 24, 2022

@awelc thanks for the good suggestion! It looks like we don't need to store the certificate and signed_effects then, so my plan is to skip the following logic

https://github.com/MystenLabs/fastnft/blob/ae2057253484bb4b05f24cb65d33355b050730f3/sui_core/src/authority/authority_store.rs#L320-L331

And extract the following logic into a function to reuse for Genesis so that we can also handle the delete/updated cases(although delete will rarely happen in init(), but it's good to handle them so that developers are not surprised)

https://github.com/MystenLabs/fastnft/blob/ae2057253484bb4b05f24cb65d33355b050730f3/sui_core/src/authority/authority_store.rs#L333-L421

@sblackshear
Copy link
Collaborator

I think the problem you're running into is a deep one: genesis is fundamentally a bootstrapping process. Everything must start from either a set of intial objects, or a transaction. But every Sui object knows the hash of the transaction that created it, and every Sui transaction has >0 input objects. We have to break that circularity somehow, and I think (1)/(2) roughly correspond to "start with objects" and "start with a tx" (respectively).

I think "objects first" is probably the best way to go, since there's no easy way to get the user or authority signatures on a CertifiedTransaction. And I think what Adam is suggesting is along the right lines for this:

I think you can iterate over objects in temporary store and insert them to the database one by one using insert_object function:

Now, to answer some other questions:

How would the Genesis process work if there's a new authority joining?

Genesis is a one-time event at the beginning of the network. The logic to generate the genesis state should be hardcoded in the validator

but we have to take Approach 1 if we decide to add an init method to one of these two libraries.

There is already an init function in the Sui Framework here, and I expect that we will add more over time. So that's more motivation for approach 1.

@sblackshear
Copy link
Collaborator

@awelc thanks for the good suggestion! It looks like we don't need to store the certificate and signed_effects then, so my plan is to skip the following logic

https://github.com/MystenLabs/fastnft/blob/ae2057253484bb4b05f24cb65d33355b050730f3/sui_core/src/authority/authority_store.rs#L320-L331

And extract the following logic into a function to reuse for Genesis so that we can also handle the delete/updated cases(although delete will rarely happen in init(), but it's good to handle them so that developers are not surprised)

https://github.com/MystenLabs/fastnft/blob/ae2057253484bb4b05f24cb65d33355b050730f3/sui_core/src/authority/authority_store.rs#L333-L421

All sounds good to me!

@lxfind
Copy link
Contributor

lxfind commented Feb 24, 2022

Yes what Adam suggested is along the right direction.
There is already an implementation called InMemoryStorage in adapter_tests.rs. I think you can pull that one out and make it available for non-testing use. Then you could execute the Move call using this in memory storage, and retrieve the objects in the end, which then can be passed to AuthorityState::new.

@666lcz 666lcz force-pushed the 02-22-genesis branch 2 times, most recently from c914e34 to 79b33da Compare February 24, 2022 20:19
sui_core/src/authority.rs Outdated Show resolved Hide resolved
@666lcz 666lcz linked an issue Feb 24, 2022 that may be closed by this pull request
@666lcz 666lcz marked this pull request as ready for review February 24, 2022 20:48
sui_core/src/authority.rs Outdated Show resolved Hide resolved
sui_core/src/authority.rs Outdated Show resolved Hide resolved
sui_core/src/authority.rs Outdated Show resolved Hide resolved
@666lcz 666lcz changed the title [RFC] Run module initializers during genesis Run module initializers during genesis Feb 25, 2022
@666lcz 666lcz requested a review from sblackshear February 25, 2022 02:31
Copy link
Collaborator

@sblackshear sblackshear left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thank you! Wouldn't mind a second pair of eyes from @awelc on the adapter publishing refactoring.

sui_core/src/authority.rs Outdated Show resolved Hide resolved
sui_core/src/authority.rs Outdated Show resolved Hide resolved
sui_core/src/authority.rs Outdated Show resolved Hide resolved
@@ -484,7 +495,8 @@ impl AuthorityState {
name: AuthorityName,
secret: StableSyncAuthoritySigner,
store: Arc<AuthorityStore>,
genesis_modules: Vec<Object>,
genesis_packages: Vec<Vec<CompiledModule>>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did we change the parameter from Vec<Object> to Vec<Vec<CompiledModule>>?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason is that we need to access the CompiledModule instead of Object for module initialization

https://github.com/MystenLabs/fastnft/blob/904e51ffce1239bc4ee4dce37297c2db7a99b8ed/sui_programmability/adapter/src/adapter.rs#L344

Comment on lines 558 to 560
temporary_store.ensure_active_inputs_mutated();
let unwrapped_object_ids = self.get_unwrapped_object_ids(temporary_store.written())?;
temporary_store.patch_unwrapped_object_version(unwrapped_object_ids);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are not necessary for the genesis case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. But could you explain why the unwrap case is not necessary for the genesis case? I think in theory developers can unwrap objects in init?

sui_core/src/authority.rs Show resolved Hide resolved
sui_core/src/authority.rs Outdated Show resolved Hide resolved
sui_core/src/authority/authority_store.rs Outdated Show resolved Hide resolved
sui_core/src/authority/authority_store.rs Outdated Show resolved Hide resolved
sui_programmability/adapter/src/genesis.rs Outdated Show resolved Hide resolved
sui_types/src/messages.rs Show resolved Hide resolved
sui_types/src/messages.rs Show resolved Hide resolved
Copy link
Contributor

@lxfind lxfind left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@666lcz 666lcz merged commit 13aa4ca into main Mar 1, 2022
@666lcz 666lcz deleted the 02-22-genesis branch March 1, 2022 00:13
666lcz added a commit that referenced this pull request Mar 1, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[sui adapter] Run module initializers during genesis
4 participants