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

fix: Remove caller ID workaround for root contexts #857

Merged
merged 1 commit into from
Sep 11, 2024

Conversation

igamigo
Copy link
Collaborator

@igamigo igamigo commented Sep 6, 2024

Closes #851
Also refactors tests to check against VM error codes. This resulted in some tests being slightly modified to account for things that were out of place (eg, some create_notes did not have parameters up to date and the process.is_err() would make the tests pass anyway).
Also adds some missing kernel errors to the kernel errors array.

Comment on lines 360 to 361
// ERR_FUNGIBLE_ASSET_MISMATCH
assert_execution_errored(process, 0x00020039);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm not entirely sure all these error codes are correct and in line of what was being returned originally. I mean to double check this soon

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I confirmed this by going back and checking the expected error codes for these tests


# double check that on storage slot is indeed the new map
push.{item_index}
exec.account::get_item
call.account::get_map_item
end
",
item_index = storage_item.index,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This test is the only failing one. Not sure what it is, seems to be related to the stack state after set_map_item

Copy link
Collaborator Author

@igamigo igamigo Sep 6, 2024

Choose a reason for hiding this comment

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

This was fixed, but I'm not entirely sure how the fix is valid. Something in the interplay between get_item and set_map_item is likely not correct

Copy link
Contributor

Choose a reason for hiding this comment

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

get_item() reads the word in a given slot, regardless of the underlying type of the slot. Currently, we have two types of slots: simple values and maps. When the type of a slat is map, its value is actually the commitment to the map (i.e., its root).

So, if we update the map via set_map_item and then do get_item for the same storage slot, we'll get the root of the updated storage map back.

Let me know if this clarifies it.

Copy link
Contributor

Choose a reason for hiding this comment

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

set_map_item returns [OLD_VALUE, NEW_ROOT] hence in this test OLD_VALUE is asserted for but NEW_ROOT is not as of now.

We should add the following assertion:

assert_eq!(
        new_storage_map.root(),
        RpoDigest::from(process.get_stack_word(2)),
        "The returned updated value doesn't match the stored updated value",
);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

get_item() reads the word in a given slot, regardless of the underlying type of the slot. Currently, we have two types of slots: simple values and maps. When the type of a slat is map, its value is actually the commitment to the map (i.e., its root).

So, if we update the map via set_map_item and then do get_item for the same storage slot, we'll get the root of the updated storage map back.

Let me know if this clarifies it.

I get the difference, but this is how I had to define get_item on the mock account code for it to work:

        export.get_item
            exec.account::get_item 
            movup.8 drop movup.8 drop movup.8 drop 
        end

set_map_item returns [OLD_VALUE, NEW_ROOT] hence in this test OLD_VALUE is asserted for but NEW_ROOT is not as of now.

It seems that ::miden::account::set_map_item returns [OLD_MAP_ROOT, OLD_MAP_VALUE]

Copy link
Contributor

Choose a reason for hiding this comment

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

I get the difference, but this is how I had to define get_item on the mock account code for it to work:

        export.get_item
            exec.account::get_item 
            movup.8 drop movup.8 drop movup.8 drop 
        end

This is related to #685. Basically, we expect the above procedure to be call-ed and so it must leave exactly 16 items on the stack. But when we call exec.account::get_item it pushes 3 items onto the stack and so we need to drop them before returning from the call.

As discussed in #685, we should probably adapt an ABI that makes these things explicit. One general solution to this is that at the end of an account interface procedure, we should always call std::sys::truncate_stack. This is a little bit more expensive (in this case would take 28 cycles) - but probably not a big price to pay for a solution that would work every time.

@igamigo
Copy link
Collaborator Author

igamigo commented Sep 6, 2024

This is also possibly a breaking change (under testing at least), but I'm not sure we need to add a changelog line for this

@igamigo igamigo added the no changelog This PR does not require an entry in the `CHANGELOG.md` file label Sep 6, 2024
@igamigo
Copy link
Collaborator Author

igamigo commented Sep 6, 2024

Some tests were failing with incorrect errors (missing note types in note-creation scripts, etc.). Changing this PR to draft until all are fixed.

@igamigo igamigo marked this pull request as draft September 6, 2024 21:49
@phklive
Copy link
Contributor

phklive commented Sep 7, 2024

Looking into the PR, I think this is the right solution, though previously when trying this method we hit limitations with the assembler erroring out on PhantomCalls did you face similar issues?

@igamigo igamigo requested a review from phklive September 9, 2024 02:41
@igamigo igamigo marked this pull request as ready for review September 9, 2024 02:41
Copy link
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

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

Thank you! Looks good! I left a few comments/questions inline.


# double check that on storage slot is indeed the new map
push.{item_index}
exec.account::get_item
call.account::get_map_item
end
",
item_index = storage_item.index,
Copy link
Contributor

Choose a reason for hiding this comment

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

get_item() reads the word in a given slot, regardless of the underlying type of the slot. Currently, we have two types of slots: simple values and maps. When the type of a slat is map, its value is actually the commitment to the map (i.e., its root).

So, if we update the map via set_map_item and then do get_item for the same storage slot, we'll get the root of the updated storage map back.

Let me know if this clarifies it.

Copy link
Contributor

@phklive phklive left a comment

Choose a reason for hiding this comment

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

Great work!

Love to see the workaround be removed.

A few proposed changes.

@@ -565,24 +558,11 @@ export.authenticate_procedure

# get procedure info (PROC_ELEMENTS, storage_offset) from memory stored at index
exec.get_procedure_info
# In production:
# => [PROC_ELEMENTS, storage_offset, PROC_ROOT]
Copy link
Contributor

Choose a reason for hiding this comment

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

I would change the return naming here from PROC_ELEMENTS to PROC_ROOT and in the above comment.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

@@ -368,6 +368,7 @@ proc.validate_new_account
# empty SMT)
exec.constants::get_empty_smt_root assert_eqw.err=ERR_PROLOGUE_NEW_NON_FUNGIBLE_FAUCET_INVALID_RESERVED_SLOT
# => []
push.1001 drop # TODO: remove line, see miden-vm/#1122
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need to add this here specifically? Did we have an error with decorators previously?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, it's what Bobbin mentioned yesterday (and related to the error mentioned in the comment). Essentially for asserts with errors the MAST root would be the same and thus the error returned by the VM could be unrelated to the actual error happening in the execution. I do not know the inner workings but adding this push and drop fixes it and we can assert for the actual error in the test.


# double check that on storage slot is indeed the new map
push.{item_index}
exec.account::get_item
call.account::get_map_item
end
",
item_index = storage_item.index,
Copy link
Contributor

Choose a reason for hiding this comment

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

set_map_item returns [OLD_VALUE, NEW_ROOT] hence in this test OLD_VALUE is asserted for but NEW_ROOT is not as of now.

We should add the following assertion:

assert_eq!(
        new_storage_map.root(),
        RpoDigest::from(process.get_stack_word(2)),
        "The returned updated value doesn't match the stored updated value",
);

fn test_authenticate_procedure() {
let tx_context = TransactionContextBuilder::with_standard_account(ONE).build();
let account = tx_context.tx_inputs().account();
fn test_procedure_in_advice_provider() {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would keep the naming of this test test_authenticate_procedure because authenticate_procedure is a kernel proc.

And it is the one that we are trying to test the logic of here.

Copy link
Collaborator Author

@igamigo igamigo Sep 10, 2024

Choose a reason for hiding this comment

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

I changed it back, but there's a subtle distinction I think: Even though authenticate_procedure is the actually called procedure, it errors immediately where the push_procedure event is emitted instead of the check for comparing roots and authenticating against the account code.

export.authenticate_procedure
    # load procedure index
    emit.ACCOUNT_PUSH_PROCEDURE_INDEX_EVENT adv_push.1 <- Execution fails here
    # => [index, PROC_ROOT]

    # get procedure info (PROC_ROOT, storage_offset) from memory stored at index
    exec.get_procedure_info
    # => [PROC_ROOT, storage_offset, PROC_ROOT]

    # verify that PROC_ROOT exists in memory at index
    movup.4 movdn.8 assert_eqw.err=ERR_PROC_NOT_PART_OF_ACCOUNT_CODE
    # => [storage_offset]
end

If I'm not wrong the account code could contain the procedure, but if the procedure is not in the advice provider, the execution would fail anyway (with the same error it throws now) and the test would pass anyway, which is why I thought the other name was a tiny bit more appropriate. In any case the positive cases do test the whole proc for valid cases.

Copy link
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

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

Looks good! Thank you! I left some small comments inline.


# double check that on storage slot is indeed the new map
push.{item_index}
exec.account::get_item
call.account::get_map_item
end
",
item_index = storage_item.index,
Copy link
Contributor

Choose a reason for hiding this comment

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

I get the difference, but this is how I had to define get_item on the mock account code for it to work:

        export.get_item
            exec.account::get_item 
            movup.8 drop movup.8 drop movup.8 drop 
        end

This is related to #685. Basically, we expect the above procedure to be call-ed and so it must leave exactly 16 items on the stack. But when we call exec.account::get_item it pushes 3 items onto the stack and so we need to drop them before returning from the call.

As discussed in #685, we should probably adapt an ABI that makes these things explicit. One general solution to this is that at the end of an account interface procedure, we should always call std::sys::truncate_stack. This is a little bit more expensive (in this case would take 28 cycles) - but probably not a big price to pay for a solution that would work every time.

@igamigo igamigo force-pushed the igamigo-wallet branch 3 times, most recently from 45e16cf to 5db5c7c Compare September 11, 2024 15:52
Base automatically changed from igamigo-wallet to next September 11, 2024 16:08
Copy link
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

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

All looks good! Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
no changelog This PR does not require an entry in the `CHANGELOG.md` file
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants