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

Return reason of why proof cant be generated #2258

Open
wants to merge 19 commits into
base: master
Choose a base branch
from

Conversation

rafal-ch
Copy link
Contributor

@rafal-ch rafal-ch commented Sep 26, 2024

Closes #1394

Description

message_proof() API is modified to return either the proof or an error with the description why the proof couldn't be created. Previously the reason was discarded by returning Ok(None).

Before requesting review

  • I have reviewed the code myself

@rafal-ch rafal-ch self-assigned this Sep 26, 2024
@rafal-ch rafal-ch added the breaking A breaking api change label Sep 27, 2024
@rafal-ch rafal-ch marked this pull request as ready for review September 27, 2024 11:59
@rafal-ch rafal-ch requested a review from a team September 27, 2024 12:00
@acerone85
Copy link
Contributor

In general my understanding is that breaking changes in graphQL are discouraged, and instead you can add a "no_proof_reason" field to the API. Third-party Applications will be able to use the service, but can update their clients to request the "no_proof_reason" field and handling it accordingly.

Overall I don't have a strong opinion, and a breaking change might be okay in this case (I don't have enough context to say whether this is the case), just thought of mentioning the alternative

@@ -970,7 +970,7 @@ type Query {
"""
owner: Address, first: Int, after: String, last: Int, before: String
): MessageConnection!
messageProof(transactionId: TransactionId!, nonce: Nonce!, commitBlockId: BlockId, commitBlockHeight: U32): MessageProof
messageProof(transactionId: TransactionId!, nonce: Nonce!, commitBlockId: BlockId, commitBlockHeight: U32): MessageProof!
Copy link
Collaborator

Choose a reason for hiding this comment

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

@acerone85 @luizstacio @LuizAsFight is it a breaking change for you?

If yes, we can just always return Some(proof).

Choose a reason for hiding this comment

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

This is not a breaking change for the TS SDK, we have null handling but it'll just be redundant.

Not sure about the FE apps though as they do have some custom GQL logic.

Choose a reason for hiding this comment

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

no breaking change on FE side

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the feedback, I'm removing the "breaking" label then.

Comment on lines 264 to 272
// Check if we found a leaf.
let Some(proof_index) = proof_index else {
return Err(anyhow::anyhow!("unable to find leaf").into())
};

// Get the proof set.
let Some((_, proof_set)) = tree.prove(proof_index) else {
return Err(anyhow::anyhow!("unable to get proof set").into());
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

It would be nice to get more information during which operation you we got an error

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can consider getting rid of the ok() here and provide details carried over in MerkleTreeError.

Is this what you meant?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As agreed, the comment was about using more user-friendly error messages. Updated in Updated in 2199e87.

Comment on lines 187 to 189
let Some(block) = database
.block(&block_height)
.into_api_result::<CompressedBlock, StorageError>()?
Copy link
Collaborator

Choose a reason for hiding this comment

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

I like old naming for vairables more=)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

message_block and message_block_height are back in 0c2dbc5

Copy link
Collaborator

@xgreenx xgreenx left a comment

Choose a reason for hiding this comment

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

What do you think about starting the error message with capital letters?=)

@rafal-ch
Copy link
Contributor Author

What do you think about starting the error message with capital letters?=)

I prefer lowercase mostly because they are usually a part of bigger sentences - and it reads better without a capital letter in the middle of a message.
Also, I think that's the convention Rust language devs use.

But after all, there's a merit in having consistent error messages. I see that we mostly use uppercase with just a few exceptions.

_ => return Ok(None),
.into_api_result::<TransactionStatus, StorageError>()?
else {
return Err(anyhow::anyhow!("unable to obtain block height").into())
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
return Err(anyhow::anyhow!("unable to obtain block height").into())
return Err(anyhow::anyhow!("Unable to obtain the message block height").into())

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated in 2199e87

Some(r) => r,
None => return Ok(None),
let Some(data) = data else {
return Err(anyhow::anyhow!("output message doesn't contain any `data`").into())
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
return Err(anyhow::anyhow!("output message doesn't contain any `data`").into())
return Err(anyhow::anyhow!("Output message doesn't contain any `data`").into())

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated in 2199e87

});
})
.ok_or::<StorageError>(
anyhow::anyhow!("desired `nonce` missing in transaction receipts").into(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
anyhow::anyhow!("desired `nonce` missing in transaction receipts").into(),
anyhow::anyhow!("Desired `nonce` missing in transaction receipts").into(),

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated in 2199e87

) -> StorageResult<Option<MessageProof>> {
// Check if the receipts for this transaction actually contain this message id or exit.
let receipt = database
) -> StorageResult<MessageProof> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the error should be anyhow since part of the error are not from the storage

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree the error structure is a little off. We might also take this opportunity and reconsider having Other(anyhow::Error) variant inside the fuel_core_storage::Error enum.

I'm keen to create a follow-up issue for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Follow-up: #2291

Some(t) => t.into_inner(),
None => return Ok(None),
else {
return Err(anyhow::anyhow!("unable to get block from database").into())
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
return Err(anyhow::anyhow!("unable to get block from database").into())
return Err(anyhow::anyhow!("Unable to get the message block from the database").into())

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated in 2199e87

None => return Ok(None),
else {
return Err(
anyhow::anyhow!("unable to get commit block header from database").into(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
anyhow::anyhow!("unable to get commit block header from database").into(),
anyhow::anyhow!("Unable to get commit block header from database").into(),

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated in 2199e87

let verifiable_commit_block_height =
block_height.pred().expect("We checked the height above");
let Some(verifiable_commit_block_height) = commit_block_header.height().pred() else {
return Err(anyhow::anyhow!("can not look beyond the genesis block").into())
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
return Err(anyhow::anyhow!("can not look beyond the genesis block").into())
return Err(anyhow::anyhow!("Impossible to generate proof beyond the genesis block").into())

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated in 2199e87

}

fn message_receipts_proof<T: MessageProofData + ?Sized>(
database: &T,
message_id: MessageId,
message_block_txs: &[Bytes32],
) -> StorageResult<Option<MerkleProof>> {
) -> StorageResult<MerkleProof> {
// Get the message receipts from the block.
let leaves: Vec<Vec<Receipt>> = message_block_txs
.iter()
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we can remove the usage of into_api_result method in this file now since we have an error

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's right. into_api_result was used to disguise the NotFound error as Ok(None). Currently we either have a proper value or a distinct error.

Updated in 10109c9

}
// Check if we found a leaf.
let Some(proof_index) = proof_index else {
return Err(anyhow::anyhow!("unable to find leaf").into())
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
return Err(anyhow::anyhow!("unable to find leaf").into())
return Err(anyhow::anyhow!("Unable to find the message receipt in the transaction to generate the proof").into())

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated in 2199e87


// Get the proof set.
let Some((_, proof_set)) = tree.prove(proof_index) else {
return Err(anyhow::anyhow!("unable to get proof set").into());
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
return Err(anyhow::anyhow!("unable to get proof set").into());
return Err(anyhow::anyhow!("Unable to generate the Merkle proof for the message from its receipts").into());

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated in 2199e87

@rafal-ch
Copy link
Contributor Author

rafal-ch commented Oct 1, 2024

But after all, there's a merit in having consistent error messages. I see that we mostly use uppercase with just a few exceptions.

For the sake of consistency we'll use the capitalized error messages.

xgreenx
xgreenx previously approved these changes Oct 1, 2024
@rafal-ch rafal-ch removed the breaking A breaking api change label Oct 2, 2024
@rafal-ch rafal-ch requested a review from a team October 2, 2024 07:13
Copy link
Collaborator

@xgreenx xgreenx left a comment

Choose a reason for hiding this comment

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

We need to change the fuel-core-client side as well.

image

The change will be breaking because of that. But it is okay to be only breaking from the Rust type system but not from API perspective(old code still should work with new API).

@rafal-ch
Copy link
Contributor Author

rafal-ch commented Oct 2, 2024

We need to change the fuel-core-client side as well.

This commit should handle that: eee16f2

Copy link
Collaborator

@xgreenx xgreenx 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 to me, but let's merge it after release to avoid any potential problems with API=)

Copy link
Contributor

@acerone85 acerone85 left a comment

Choose a reason for hiding this comment

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

LGTM. Happy to approve after release.

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.

Return the reason WHY we can't generate the proof instead of None
5 participants