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

fixNFTokenRemint: prevent NFT re-mint: #4406

Merged
merged 43 commits into from Mar 20, 2023
Merged

Conversation

shawnxie999
Copy link
Collaborator

@shawnxie999 shawnxie999 commented Feb 1, 2023

High Level Overview of Change

Currently, a NFT ID can be reminted by the following steps:

  1. Alice creates an account and mints an NFT
  2. Alice burns the NFT (a NFTokenBurn transaction)
  3. Alice deletes her account (a AccountDelete transaction)
  4. Alice re-creates her account
  5. Alice mints a NFT ( a NFTokenMint transaction with params: NFTokenTaxon = 0, Flags = 9). This will re-mint a NFT with the same NFTokenID as Step 1. The params that construct the NFT ID will cause collision if their values are equal before and after the remint

We want to come up with a new sequence number construct to avoid this scenario

New NFT sequence construct

  • We create a new AccountRoot field FirstNFTSequence, which stays constant over time.This field is set to the current account sequence when the account issues their first NFT. Otherwise, it is not set.

  • The sequence of a newly minted NFT is computed by FirstNFTSequence + MintedNFTokens (then MintedNFTokens increments by 1).

New Account Deletion Restriction

An account can only be deleted if FirstNFTSequence + MintedNFTokens + 256 is less than the current ledger sequence (256 was chosen as a heuristic restriction for account deletion and already exists in the account deletion constraint). Without this restriction, NFT is still remintable with the following scenario:

  1. Alice's account sequence is at 1
  2. Bob is Alice's authorized minter
  3. Bob mints 500 NFTs for Alice (NFTs will have sequences 1 - 501, since NFT sequence is computed by FirstNFTokenSequence + MintedNFTokens)
  4. Alice deletes her account at ledger 257 (enforced by AccountDelete amendment)
  5. Alice re-creates her account at ledger 258
  6. Alice mints a NFT. FirstNFTokenSequence initialized to her account sequence (258), and MintedNFTokens to 0. This newly minted NFT would have a sequence number of 258, which is a duplicate of what she issued through authorized minting before she deleted her account.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (non-breaking change that only restructures code)
  • Tests (You added tests for code that already exists, or your new feature included in this PR)
  • Documentation Updates
  • Release

@shawnxie999 shawnxie999 force-pushed the nft-remint branch 2 times, most recently from ede18a2 to 78dd062 Compare February 2, 2023 02:48
Copy link
Contributor

@nbougalis nbougalis left a comment

Choose a reason for hiding this comment

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

Personally I don't see a problem with this kind or re-minting (burn NFT, delete issuing account, re-create issuing account, re-mint NFT) unless the issue you're trying to solve is leftover offers from previous operations. If that's the concern, then the fix is easy: check for the presence of those during minting, and if present, remove them and if you can't fail the mint.

Even if that's not the concern and there is some other issue, I don't think what this PR does is the right approach anyways.

I am not vetoing this. Just offering my 2¢.

@shawnxie999
Copy link
Collaborator Author

shawnxie999 commented Feb 2, 2023

Personally I don't see a problem with this kind or re-minting (burn NFT, delete issuing account, re-create issuing account, re-mint NFT) unless the issue you're trying to solve is leftover offers from previous operations. If that's the concern, then the fix is easy: check for the presence of those during minting, and if present, remove them and if you can't fail the mint.

Even if that's not the concern and there is some other issue, I don't think what this PR does is the right approach anyways.

I am not vetoing this. Just offering my 2¢.

This doesn't directly have to do with the the previous change of leftover offers. It was a concern that duplicate NFT's could cause issues in the long run

Something brought up by @ledhed2222:

A malicious issuer can mint a token representing, for example, a carbon credit. In the expected workflow, this token is meant to be burned once the carbon has actually been sequestered. As a result, the issuer has set the tfBurnable flag to allow them to burn the token even if they no longer hold it. They then burn said token, indicating that the specific carbon credit is no longer tradable. At this point, the issuer deletes and recreates their account, mints a new token representing the same carbon credit, and sells it again.

@shawnxie999
Copy link
Collaborator Author

shawnxie999 commented Feb 2, 2023

Even if that's not the concern and there is some other issue, I don't think what this PR does is the right approach anyways.

Also could you please elaborate why this wouldn't be the right approach? This change has minimal effect on the ledger and does not change how the people use NFTs.

@shawnxie999 shawnxie999 marked this pull request as ready for review February 2, 2023 20:57
…han 500 (XRPLF#4346)

* Allow offers to be removable
* Delete sell offers first

Signed-off-by: Shawn Xie <shawnxie920@gmail.com>
@intelliot intelliot changed the title fixUnburnableNFToken: New NFTokenID sequence construct + new account deletion constraint fixUnburnableNFToken: Prevent NFT re-mint with NFTokenID sequence construct + account deletion constraint Feb 7, 2023
@shawnxie999
Copy link
Collaborator Author

this is ready for re-review. cc @scottschurr

@shawnxie999 shawnxie999 requested review from scottschurr and removed request for seelabs and ledhed2222 March 8, 2023 14:48
Copy link
Collaborator

@scottschurr scottschurr left a comment

Choose a reason for hiding this comment

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

Looks great. Thanks for accommodating my requests. I noticed one minor comment problem in a test. Sorry I missed that last time. But it doesn't need to hold up this pull request.

src/test/app/NFToken_test.cpp Outdated Show resolved Hide resolved
Copy link
Collaborator

@scottschurr scottschurr left a comment

Choose a reason for hiding this comment

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

👍 Looks great!

(*root)[~sfMintedNFTokens].value_or(0u);

(*root)[sfMintedNFTokens] = mintedNftCnt + 1u;
if ((*root)[sfMintedNFTokens] == 0u)
Copy link
Contributor

Choose a reason for hiding this comment

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

doesn't need to change - but i don't think you need the u specifier since you've already specified that the type is unsigned and arithmetic with a mix of signed/unsigned values should leave the value unsigned in this case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

it is a conservative approach to make sure things are correctly typed proposed by @scottschurr

Copy link
Collaborator

Choose a reason for hiding this comment

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

@ledhed2222, what you describe is not always the case. There are situations where operations on a combination of unsigned and signed integers produce a signed value. See "Usual Arithmetic Conversions": https://en.cppreference.com/w/cpp/language/usual_arithmetic_conversions#Integer_conversion_rank

You'll notice that the rules changed with C++11 and again with C++23.

What the rules seem to be pretty good at holding onto is that if both arguments are unsigned then the result will be unsigned. Since the literals 0 and 1 are implicitly signed, they can influence the type yielded by the operation. That's why, since overflow is possible in this calculation, using 0u and 1u is safer; they are explicitly unsigned.

I did not go to the effort of determining exactly what conversions the compiler would make -- I'm not that smart. I just applied a heuristic so I'm increasing the likelihood the compiler will do what I want.

@intelliot intelliot requested a review from JST5000 March 16, 2023 18:23
@shawnxie999
Copy link
Collaborator Author

shawnxie999 commented Mar 16, 2023

This is ready for quick re-review. Forgot to add fixNFTokenMint flag in tests to disable the amendment

cc. @scottschurr

@shawnxie999 shawnxie999 requested review from scottschurr and removed request for JST5000 March 16, 2023 18:37
Copy link
Collaborator

@scottschurr scottschurr left a comment

Choose a reason for hiding this comment

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

Still LGTM. 👍

@intelliot intelliot added the Passed Passed code review & PR owner thinks it's ready to merge. Perf sign-off may still be required. label Mar 16, 2023
@intelliot
Copy link
Collaborator

intelliot commented Mar 17, 2023

At merging, this PR will be squashed into 1 commit. The commit needs a commit message. Here is what I suggest. @shawnxie999 or anyone else, please feel free to make suggestions or offer an alternative.

`fixNFTokenRemint`: prevent NFT re-mint: (#4406)

Without the protocol amendment introduced by this commit, an NFT ID can
be reminted in this manner:

1. Alice creates an account and mints an NFT.
2. Alice burns the NFT with an `NFTokenBurn` transaction.
3. Alice deletes her account with an `AccountDelete` transaction.
4. Alice re-creates her account.
5. Alice mints an NFT with an `NFTokenMint` transaction with params:
   `NFTokenTaxon` = 0, `Flags` = 9).

This will mint a NFT with the same `NFTokenID` as the one minted in step
1. The params that construct the NFT ID will cause a collision in
`NFTokenID` if their values are equal before and after the remint.

With the `fixNFTokenRemint` amendment, there is a new sequence number
construct which avoids this scenario:

- A new `AccountRoot` field, `FirstNFTSequence`, stays constant over
  time.
  - This field is set to the current account sequence when the account
    issues their first NFT.
  - Otherwise, it is not set.
- The sequence of a newly-minted NFT is computed by: `FirstNFTSequence +
  MintedNFTokens`.
  - `MintedNFTokens` is then incremented by 1 for each mint.

Furthermore, there is a new account deletion restriction:

- An account can only be deleted if `FirstNFTSequence + MintedNFTokens +
  256` is less than the current ledger sequence.
  - 256 was chosen because it already exists in the current account
    deletion constraint.

Without this restriction, an NFT may still be remintable. Example
scenario:

1. Alice's account sequence is at 1.
2. Bob is Alice's authorized minter.
3. Bob mints 500 NFTs for Alice. The NFTs will have sequences 1-501, as
   NFT sequence is computed by `FirstNFTokenSequence + MintedNFTokens`).
4. Alice deletes her account at ledger 257 (as required by the existing
   `AccountDelete` amendment).
5. Alice re-creates her account at ledger 258.
6. Alice mints an NFT. `FirstNFTokenSequence` initializes to her account
   sequence (258), and `MintedNFTokens` initializes as 0. This
   newly-minted NFT would have a sequence number of 258, which is a
   duplicate of what she issued through authorized minting before she
   deleted her account.

---------

Signed-off-by: Shawn Xie <shawnxie920@gmail.com>

This PR will be merged to develop, no earlier than Monday, March 20, 2023 @ 11:30am PT, unless I hear any opposition before then.

@intelliot intelliot changed the title Prevent NFT re-mint with NFTokenID sequence construct + account deletion constraint fixNFTokenRemint: prevent NFT re-mint: Mar 17, 2023
@shawnxie999
Copy link
Collaborator Author

@intelliot looks good

@intelliot intelliot merged commit 305c9a8 into XRPLF:develop Mar 20, 2023
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Added to API Changelog Amendment API Change Bug Passed Passed code review & PR owner thinks it's ready to merge. Perf sign-off may still be required. Testable Will Need Documentation
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

None yet

7 participants