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

Feature/acs404 #3523

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 6 additions & 10 deletions contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,6 @@ namespace AElf.Contracts.MultiToken;

public partial class TokenContract
{
private static bool IsValidSymbolChar(char character)
{
return (character >= 'A' && character <= 'Z') || (character >= '0' && character <= '9') ||
character == TokenContractConstants.NFTSymbolSeparator;
}

private bool IsValidItemIdChar(char character)
{
return character >= '0' && character <= '9';
Expand All @@ -40,8 +34,8 @@ private TokenInfo AssertValidToken(string symbol, long amount)

private void AssertValidSymbolAndAmount(string symbol, long amount)
{
Assert(!string.IsNullOrEmpty(symbol) && symbol.All(IsValidSymbolChar),
"Invalid symbol.");
Assert(!string.IsNullOrEmpty(symbol), "Invalid symbol.");
AssertSymbolIsValid(symbol);
Assert(amount > 0, "Invalid amount.");
}

Expand Down Expand Up @@ -184,8 +178,8 @@ private void AssertCrossChainTransaction(Transaction originalTransaction, Addres
private void RegisterTokenInfo(TokenInfo tokenInfo)
{
CheckTokenExists(tokenInfo.Symbol);
Assert(!string.IsNullOrEmpty(tokenInfo.Symbol) && tokenInfo.Symbol.All(IsValidSymbolChar),
"Invalid symbol.");
Assert(!string.IsNullOrEmpty(tokenInfo.Symbol), "Invalid symbol.");
AssertSymbolIsValid(tokenInfo.Symbol);
Assert(!string.IsNullOrEmpty(tokenInfo.TokenName), "Token name can neither be null nor empty.");
Assert(tokenInfo.TotalSupply > 0, "Invalid total supply.");
Assert(tokenInfo.Issuer != null, "Invalid issuer address.");
Expand Down Expand Up @@ -263,6 +257,8 @@ private void CheckSymbolLength(string symbol, SymbolType symbolType)
{
if (symbolType == SymbolType.Token)
Assert(symbol.Length <= TokenContractConstants.SymbolMaxLength, "Invalid token symbol length");

// there is a max length of 30 for NFT symbol, limiting the amount of sub NFTs that can be created
if (symbolType == SymbolType.Nft || symbolType == SymbolType.NftCollection)
Assert(symbol.Length <= TokenContractConstants.NFTSymbolMaxLength, "Invalid NFT symbol length");
}
Expand Down
51 changes: 46 additions & 5 deletions contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,52 @@
{
private SymbolType GetCreateInputSymbolType(string symbol)
{
var words = symbol.Split(TokenContractConstants.NFTSymbolSeparator);
Assert(words[0].Length > 0 && words[0].All(IsValidCreateSymbolChar), "Invalid Symbol input");
if (words.Length == 1) return SymbolType.Token;
Assert(words.Length == 2 && words[1].Length > 0 && words[1].All(IsValidItemIdChar), "Invalid NFT Symbol input");
return words[1] == TokenContractConstants.CollectionSymbolSuffix ? SymbolType.NftCollection : SymbolType.Nft;
var splitSymbols = symbol.Split(TokenContractConstants.NFTSymbolSeparator);

AssertTokenSymbolIsValid(splitSymbols);

if (splitSymbols.Length == 1) return SymbolType.Token;

AssertNFTSymbolIsValid(splitSymbols);

// if we want to allow sub nft collection to be created with a different owner, we can check against words[^1]
return splitSymbols[1] == TokenContractConstants.CollectionSymbolSuffix ? SymbolType.NftCollection : SymbolType.Nft;
}

private void AssertSymbolIsValid(string symbol)
{
var splitSymbols = symbol.Split(TokenContractConstants.NFTSymbolSeparator);
AssertTokenSymbolIsValid(splitSymbols);
if (splitSymbols.Length == 1) return;
AssertNFTSymbolIsValid(splitSymbols);
}

private void AssertTokenSymbolIsValid(string symbol)
{
var splitSymbols = symbol.Split(TokenContractConstants.NFTSymbolSeparator);
AssertTokenSymbolIsValid(splitSymbols);
}

Check warning on line 33 in contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs

View check run for this annotation

Codecov / codecov/patch

contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs#L30-L33

Added lines #L30 - L33 were not covered by tests

private void AssertTokenSymbolIsValid(string[] splitSymbols)
{
Assert(splitSymbols[0].Length > 0 && splitSymbols[0].All(IsValidCreateSymbolChar), "Invalid Symbol input");
}

private void AssertNFTSymbolIsValid(string symbol)
{
var splitSymbols = symbol.Split(TokenContractConstants.NFTSymbolSeparator);
AssertNFTSymbolIsValid(splitSymbols);
}

Check warning on line 44 in contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs

View check run for this annotation

Codecov / codecov/patch

contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs#L41-L44

Added lines #L41 - L44 were not covered by tests

private void AssertNFTSymbolIsValid(string[] splitSymbols)
{
Assert(splitSymbols.Length >= 2, "Invalid NFT Symbol input");
Assert(splitSymbols[1].Length > 0 && splitSymbols[1].All(IsValidItemIdChar), "Invalid NFT Symbol input");
for (var i = 2; i < splitSymbols.Length; ++i)
{
var word = splitSymbols[i];
Assert(word.Length > 0 && word[0] != '0' && word.All(IsValidItemIdChar), "Invalid NFT Symbol input");
}
}

private void AssertNFTCreateInput(CreateInput input)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ private string GetNftCollectionSymbol(string inputSymbol)
var words = symbol.Split(TokenContractConstants.NFTSymbolSeparator);
const int tokenSymbolLength = 1;
if (words.Length == tokenSymbolLength) return null;
Assert(words.Length == 2 && words[1].All(IsValidItemIdChar), "Invalid NFT Symbol Input");

AssertNFTSymbolIsValid(words);

return symbol == $"{words[0]}-0" ? null : $"{words[0]}-0";
}

Expand Down
147 changes: 147 additions & 0 deletions test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,72 @@ public partial class MultiTokenContractTests
}
}
};

private TokenInfo SubNft1155Info => new()
{
Symbol = "1-12419",
TokenName = "Trump Digital Trading Card #12419",
TotalSupply = TotalSupply,
Decimals = 0,
Issuer = Accounts[0].Address,
IssueChainId = _chainId,
IsBurnable = false,
ExternalInfo = new ExternalInfo()
{
Value =
{
{
NftInfoMetaFields.ImageUrlKey,
"https://i.seadn.io/gcs/files/0f5cdfaaf687de2ebb5834b129a5bef3.png?auto=format&w=3840"
},
{ NftInfoMetaFields.IsBurnedKey, "false" }
}
}
};

private TokenInfo LongIdSubNft1155Info => new()
{
Symbol = "1-19-153-35-8-90-23-4-15-66",
TokenName = "Trump Digital Trading Card #1-19-153-35-8-90-23-4-15-60",
TotalSupply = TotalSupply,
Decimals = 0,
Issuer = Accounts[0].Address,
IssueChainId = _chainId,
IsBurnable = false,
ExternalInfo = new ExternalInfo()
{
Value =
{
{
NftInfoMetaFields.ImageUrlKey,
"https://i.seadn.io/gcs/files/0f5cdfaaf687de2ebb5834b129a5bef3.png?auto=format&w=3840"
},
{ NftInfoMetaFields.IsBurnedKey, "false" }
}
}
};

private TokenInfo ErroneousSubNft1155Info => new()
{
Symbol = "1-0",
TokenName = "Trump Digital Trading Card #12419",
TotalSupply = TotalSupply,
Decimals = 0,
Issuer = Accounts[0].Address,
IssueChainId = _chainId,
IsBurnable = false,
ExternalInfo = new ExternalInfo()
{
Value =
{
{
NftInfoMetaFields.ImageUrlKey,
"https://i.seadn.io/gcs/files/0f5cdfaaf687de2ebb5834b129a5bef3.png?auto=format&w=3840"
},
{ NftInfoMetaFields.IsBurnedKey, "false" }
}
}
};

private async Task<IExecutionResult<Empty>> CreateNftCollectionAsync(TokenInfo collectionInfo)
{
Expand Down Expand Up @@ -282,6 +348,87 @@ public async Task MultiTokenContract_Create_NFTCollection_Input_Check_Test()
result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined);
}
}

[Fact(DisplayName = "[MultiToken_Nft] Create sub nft with valid id")]
public async Task MultiTokenContract_Create_Sub_Nft_With_Valid_Id_Test()
{
var symbols = new List<string>();
var collectionInfo = NftCollection1155Info;
var createCollectionRes = await CreateNftCollectionAsync(collectionInfo);
createCollectionRes.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined);
var collectionSymbolWords = AssertCreateCollection(createCollectionRes, collectionInfo, symbols);

var createNft2Res = await CreateNftAsync(collectionInfo.Symbol, SubNft1155Info);
createNft2Res.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined);
var createNft2Log = TokenCreated.Parser.ParseFrom(createNft2Res.TransactionResult.Logs
.First(l => l.Name == nameof(TokenCreated)).NonIndexed);
var nft2SymbolWords = createNft2Log.Symbol.Split("-");
Assert.True(nft2SymbolWords.Length == 3);
Assert.Equal(nft2SymbolWords[0], collectionSymbolWords[0]);
AssertTokenEqual(createNft2Log, SubNft1155Info);
symbols.Add(createNft2Log.Symbol);
createNft2Log.Symbol.ShouldBe(collectionInfo.Symbol + SubNft1155Info.Symbol);
}

private string[] AssertCreateCollection(IExecutionResult<Empty> createCollectionRes, TokenInfo collectionInfo, List<string> symbols)
{
var createCollectionLog = TokenCreated.Parser.ParseFrom(createCollectionRes.TransactionResult.Logs
.First(l => l.Name == nameof(TokenCreated)).NonIndexed);
var collectionSymbolWords = createCollectionLog.Symbol.Split("-");
Assert.True(collectionSymbolWords.Length == 2);
AssertTokenEqual(createCollectionLog, collectionInfo);
symbols.Add(createCollectionLog.Symbol);
createCollectionLog.Symbol.ShouldBe(collectionInfo.Symbol + "0");
return collectionSymbolWords;
}

[Fact(DisplayName = "[MultiToken_Nft] Create sub nft with long valid id")]
public async Task MultiTokenContract_Create_Sub_Nft_With_Long_Valid_Id_Test()
{
var symbols = new List<string>();
var collectionInfo = NftCollection1155Info;
var createCollectionRes = await CreateNftCollectionAsync(collectionInfo);
createCollectionRes.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined);
var collectionSymbolWords = AssertCreateCollection(createCollectionRes, collectionInfo, symbols);

var createNft2Res = await CreateNftAsync(collectionInfo.Symbol, LongIdSubNft1155Info);
createNft2Res.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined);
var createNft2Log = TokenCreated.Parser.ParseFrom(createNft2Res.TransactionResult.Logs
.First(l => l.Name == nameof(TokenCreated)).NonIndexed);
var nft2SymbolWords = createNft2Log.Symbol.Split("-");
Assert.True(nft2SymbolWords.Length == 11);
Assert.Equal(nft2SymbolWords[0], collectionSymbolWords[0]);
AssertTokenEqual(createNft2Log, LongIdSubNft1155Info);
symbols.Add(createNft2Log.Symbol);
createNft2Log.Symbol.ShouldBe(collectionInfo.Symbol + LongIdSubNft1155Info.Symbol);
}

[Fact(DisplayName = "[MultiToken_Nft] Create sub nft with invalid id of 0")]
public async Task MultiTokenContract_Create_Sub_Nft_With_Invalid_Id_Test()
{
var symbols = new List<string>();
var collectionInfo = NftCollection1155Info;
var createCollectionRes = await CreateNftCollectionAsync(collectionInfo);
createCollectionRes.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined);
AssertCreateCollection(createCollectionRes, collectionInfo, symbols);

var createInput = new CreateInput
{
Symbol = $"{collectionInfo.Symbol}{ErroneousSubNft1155Info.Symbol}",
TokenName = ErroneousSubNft1155Info.TokenName,
TotalSupply = ErroneousSubNft1155Info.TotalSupply,
Decimals = ErroneousSubNft1155Info.Decimals,
Issuer = ErroneousSubNft1155Info.Issuer,
IsBurnable = ErroneousSubNft1155Info.IsBurnable,
IssueChainId = ErroneousSubNft1155Info.IssueChainId,
ExternalInfo = ErroneousSubNft1155Info.ExternalInfo,
Owner = ErroneousSubNft1155Info.Issuer
};

var result = await TokenContractStub.Create.SendWithExceptionAsync(createInput);;
result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed);
result.TransactionResult.Error.ShouldContain("Invalid NFT Symbol input");
}

[Fact(DisplayName = "[MultiToken_Nft] Create nft input check")]
public async Task MultiTokenContract_Create_NFT_Input_Check_Test()
Expand Down
Loading