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

Revamp invalid state transition proof (part 2) #1270

Merged
merged 7 commits into from
Mar 23, 2023

Conversation

liuchengxu
Copy link
Contributor

This PR finishes the verification of pre_state_root and post_state_root in the invalid state transition proof. The main change is replacing the full call data in ExecutionPhase with the corresponding trace index for the verifier to retrieve the state root. Some tests are disabled temperorarily, they will be enabled again when the last task in #1230 completes.

Part of #1230

Code contributor checklist:

- On the verifier side, there is no need to include the full call data as
the verifier has to retrieve it on its own.
- On the prover side, now the call data is passed as an argument.

The tests are disabled temperorarily.
`total_extrinsic` is added to `ExecutionPhase::FinalizeBlock`.
A new runtime api is added to fetch the state root of specific domain
block as `pre_state_root` for `ExecutionPhase::InitializeBlock` is the state root
of parent block.

This commit completes the `pre_state_root` verification.
Changes the return type of `verify_pre_state_root` from `bool` to `Result<(), VerificationError>`.
…ng `post_state_root` as well

This avoids doing the computation unnecessarily if someone creates a proof that proves nothing bad,
verifying the execution is relatively a high cost operation for the verifier.
Copy link
Member

@NingLin-P NingLin-P left a comment

Choose a reason for hiding this comment

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

Rest LGTM.

call_data: new_header.encode(),
};
let execution_phase = ExecutionPhase::InitializeBlock;
let initialize_block_call_data = new_header.encode();
Copy link
Member

Choose a reason for hiding this comment

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

The new_header is mock, I'm curious about why use a mock header here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's not a mock and not supposed to be a mock, new_header is essentially the exact state transition data for InitializeBlock by which all the on_initialize hooks are executed.

It was correct when initially introduced, but now I think it's problematic for the system domain due to the changes on digests (we manually inject the primary block has info as a digest when building the system domain block), we should do the same here when proving the execution. For the core domain, it should still be correct.

I believe such glitches can be catched earlier by the tests when we re-enable them again. Probably nice to fix it when you update this test to the new testing framework?https://github.com/subspace/subspace/blob/236b3345ec028b521e6d6e1fbc5dec0a99f44eed/crates/subspace-fraud-proof/src/tests.rs#L171

Copy link
Member

Choose a reason for hiding this comment

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

Sure! I will update this test with the new testing framework to cover the initialize_block case.

Copy link
Member

@vedhavyas vedhavyas left a comment

Choose a reason for hiding this comment

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

I'm confused on the traces indexes we use to verify pre and post state root. Can you help me understand pls

ExecutionPhase::InitializeBlock => todo!("Get state_root of parent domain block"),
ExecutionPhase::ApplyExtrinsic(trace_index_of_pre_state_root)
| ExecutionPhase::FinalizeBlock {
total_extrinsics: trace_index_of_pre_state_root,
Copy link
Member

Choose a reason for hiding this comment

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

This seems wrong to me. The total extrinsics for example are 4, we would have a total of 6 including initialise and finalise. From the executor changes, this seems to give extrinsic count which is 4, so the pre state root will be, pre start root of the finalize extrinsinc instead of initaialize extrinsic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There are 4 extrinsics, then the trace will a total of 6 state roots. Let's make a table for it:

InitializeBlock ApplyExtrinsic(0) ApplyExtrinsic(1) ApplyExtrinsic(2) ApplyExtirnsic(3) FinalizeBlock
trace0 trace1 trace2 trace3 trace4 trace5

The pre_state_root of ApplyExtrinsic(0) is the state root after the last stage, i.e., trace0 after InitializeBlock.
The post_state_root of ApplyExtrinsic(0) is the state root after this stage itself, i.e., trace1 after ApplyExtrinsic(0).

In general, the trace index of pre_state_root and post_state_root of given ApplyExtrinsic(u32)/FinalizeBlock{total_extrinsics} is u32/total_extrinsics and u32/total_extrinsics + 1 respectively.


let post_state_root_onchain = match execution_phase {
ExecutionPhase::InitializeBlock => trace
.get(0)
Copy link
Member

Choose a reason for hiding this comment

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

confused here again. Shouldn't we be lookig at the last state root in the trace?

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.

3 participants