Skip to content
Merged
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
2 changes: 2 additions & 0 deletions contract/AElf.Contracts.MultiToken/TokenContractState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,6 @@ public partial class TokenContractState : ContractState

// Alias -> Actual Symbol
public MappedState<string, string> SymbolAliasMap { get; set; }

public MappedState<Address, bool> TransferBlackList { get; set; }
}
17 changes: 17 additions & 0 deletions contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,7 @@
/// <returns></returns>
public override Empty CrossChainTransfer(CrossChainTransferInput input)
{
Assert(!IsInTransferBlackListInternal(Context.Sender), "Sender is in transfer blacklist.");

Check warning on line 562 in contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs

View check run for this annotation

Codecov / codecov/patch

contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs#L562

Added line #L562 was not covered by tests
var tokenInfo = AssertValidToken(input.Symbol, input.Amount);
AssertValidMemo(input.Memo);
var issueChainId = GetIssueChainId(tokenInfo.Symbol);
Expand Down Expand Up @@ -849,4 +850,20 @@
Assert(parts.Last() == TokenContractConstants.CollectionSymbolSuffix, "Incorrect collection symbol suffix.");
Assert(alias == parts.First(), $"Alias for an item of {collectionSymbol} cannot be {alias}.");
}

public override Empty AddToTransferBlackList(Address input)
{
AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress);

Check warning on line 856 in contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs

View check run for this annotation

Codecov / codecov/patch

contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs#L855-L856

Added lines #L855 - L856 were not covered by tests
Assert(input != null && !input.Value.IsNullOrEmpty(), "Invalid address.");
State.TransferBlackList[input] = true;
return new Empty();
}

Check warning on line 860 in contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs

View check run for this annotation

Codecov / codecov/patch

contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs#L858-L860

Added lines #L858 - L860 were not covered by tests

public override Empty RemoveFromTransferBlackList(Address input)
{
AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress);

Check warning on line 864 in contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs

View check run for this annotation

Codecov / codecov/patch

contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs#L863-L864

Added lines #L863 - L864 were not covered by tests
Assert(input != null && !input.Value.IsNullOrEmpty(), "Invalid address.");
State.TransferBlackList[input] = false;
return new Empty();
}

Check warning on line 868 in contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs

View check run for this annotation

Codecov / codecov/patch

contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs#L866-L868

Added lines #L866 - L868 were not covered by tests
}
1 change: 1 addition & 0 deletions contract/AElf.Contracts.MultiToken/TokenContract_Fees.cs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ private void ModifyDelegation(TransactionFeeBill bill, TransactionFreeFeeAllowan
private void ModifyBalance(Address fromAddress, TransactionFeeBill bill,
TransactionFreeFeeAllowanceBill allowanceBill)
{
Assert(!IsInTransferBlackListInternal(fromAddress), "Charge fee address is in transfer blacklist.");
SetOrRefreshTransactionFeeFreeAllowances(fromAddress);
var freeAllowancesMap = CalculateTransactionFeeFreeAllowances(fromAddress);

Expand Down
6 changes: 6 additions & 0 deletions contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ private void AssertValidInputAddress(Address input)

private void DoTransfer(Address from, Address to, string symbol, long amount, string memo = null)
{
Assert(!IsInTransferBlackListInternal(from), "From address is in transfer blacklist.");
Assert(from != to, "Can't do transfer to sender itself.");
AssertValidMemo(memo);
ModifyBalance(from, symbol, -amount);
Expand Down Expand Up @@ -419,4 +420,9 @@ private void SetTokenInfo(TokenInfo tokenInfo)
var symbol = tokenInfo.Symbol;
State.TokenInfos[symbol] = tokenInfo;
}

private bool IsInTransferBlackListInternal(Address address)
{
return State.TransferBlackList[address];
}
}
6 changes: 6 additions & 0 deletions contract/AElf.Contracts.MultiToken/TokenContract_Views.cs
Original file line number Diff line number Diff line change
Expand Up @@ -292,4 +292,10 @@

return aliasOrSymbol;
}

[View]
public override BoolValue IsInTransferBlackList(Address input)
{
return new BoolValue { Value = State.TransferBlackList[input] };
}

Check warning on line 300 in contract/AElf.Contracts.MultiToken/TokenContract_Views.cs

View check run for this annotation

Codecov / codecov/patch

contract/AElf.Contracts.MultiToken/TokenContract_Views.cs#L298-L300

Added lines #L298 - L300 were not covered by tests
}
11 changes: 11 additions & 0 deletions protobuf/token_contract_impl.proto
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,17 @@ service TokenContractImpl {

rpc ExtendSeedExpirationTime (ExtendSeedExpirationTimeInput) returns (google.protobuf.Empty) {
}

// Add an address to the transfer blacklist. Only parliament owner can call this method.
rpc AddToTransferBlackList (aelf.Address) returns (google.protobuf.Empty) {
}
// Remove an address from the transfer blacklist. Only parliament owner can call this method.
rpc RemoveFromTransferBlackList (aelf.Address) returns (google.protobuf.Empty) {
}
// Check if an address is in the transfer blacklist.
rpc IsInTransferBlackList (aelf.Address) returns (google.protobuf.BoolValue) {
option (aelf.is_view) = true;
}
}

message AdvanceResourceTokenInput {
Expand Down
2 changes: 1 addition & 1 deletion templates/build-template-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
displayName: Build tasks (Linux) [${{ parameters.n }} of ${{ parameters.parts }} parts]
timeoutInMinutes: 120
pool:
vmImage: ubuntu-latest
vmImage: ubuntu-22.04
variables:
CI_TEST: true
steps:
Expand Down
169 changes: 169 additions & 0 deletions test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1904,4 +1904,173 @@ public async Task ExtendSeedExpirationTime_Test(string symbol, long expirationTi

await TokenContractStub.ExtendSeedExpirationTime.CallAsync(input);
}

[Fact]
public async Task MultiTokenContract_Transfer_BlackList_Test()
{
await MultiTokenContract_Approve_Test();

var trafficToken = "TRAFFIC";
await CreateAndIssueCustomizeTokenAsync(DefaultAddress, trafficToken, 10000, 10000);

// 1. Non-owner cannot add to blacklist
var addBlackListResult = await TokenContractStubUser.AddToTransferBlackList.SendWithExceptionAsync(DefaultAddress);
addBlackListResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed);
addBlackListResult.TransactionResult.Error.ShouldContain("Unauthorized behavior");
var isInTransferBlackList = await TokenContractStubUser.IsInTransferBlackList.CallAsync(DefaultAddress);
isInTransferBlackList.Value.ShouldBe(false);

// 2. Owner adds DefaultAddress to blacklist via parliament proposal
var defaultParliament = await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty());
var proposalId = await CreateProposalAsync(TokenContractAddress, defaultParliament, nameof(TokenContractStub.AddToTransferBlackList), DefaultAddress);
await ApproveWithMinersAsync(proposalId);
await ParliamentContractStub.Release.SendAsync(proposalId);
isInTransferBlackList = await TokenContractStubUser.IsInTransferBlackList.CallAsync(DefaultAddress);
isInTransferBlackList.Value.ShouldBe(true);

// 3. Transfer should fail when sender is in blacklist
var transferResult = (await TokenContractStub.Transfer.SendWithExceptionAsync(new TransferInput
{
Amount = Amount,
Memo = "blacklist test",
Symbol = AliceCoinTokenInfo.Symbol,
To = User1Address
})).TransactionResult;
transferResult.Status.ShouldBe(TransactionResultStatus.Failed);
transferResult.Error.ShouldContain("From address is in transfer blacklist");

// 4. TransferFrom should fail when from address is in blacklist
var user1Stub = GetTester<TokenContractImplContainer.TokenContractImplStub>(TokenContractAddress, User1KeyPair);
var transferFromResult = (await user1Stub.TransferFrom.SendWithExceptionAsync(new TransferFromInput
{
Amount = Amount,
From = DefaultAddress,
Memo = "blacklist test",
Symbol = AliceCoinTokenInfo.Symbol,
To = User1Address
})).TransactionResult;
transferFromResult.Status.ShouldBe(TransactionResultStatus.Failed);
transferFromResult.Error.ShouldContain("From address is in transfer blacklist");

// 5. CrossChainTransfer should fail when sender is in blacklist
var crossChainTransferResult = (await TokenContractStub.CrossChainTransfer.SendWithExceptionAsync(new CrossChainTransferInput
{
Symbol = AliceCoinTokenInfo.Symbol,
Amount = Amount,
To = User1Address,
IssueChainId = 9992731,
Memo = "blacklist test",
ToChainId = 9992732
})).TransactionResult;
crossChainTransferResult.Status.ShouldBe(TransactionResultStatus.Failed);
crossChainTransferResult.Error.ShouldContain("Sender is in transfer blacklist");

// 6. Lock should fail when sender is in blacklist
var lockId = HashHelper.ComputeFrom("lockId");
var lockTokenResult = (await BasicFunctionContractStub.LockToken.SendWithExceptionAsync(new LockTokenInput
{
Address = DefaultAddress,
Amount = Amount,
Symbol = AliceCoinTokenInfo.Symbol,
LockId = lockId,
Usage = "Testing."
})).TransactionResult;
lockTokenResult.Status.ShouldBe(TransactionResultStatus.Failed);
lockTokenResult.Error.ShouldContain("From address is in transfer blacklist");

// 7. Transfer to contract should fail when sender is in blacklist
var transferToContractResult = (await BasicFunctionContractStub.TransferTokenToContract.SendWithExceptionAsync(
new TransferTokenToContractInput
{
Amount = Amount,
Symbol = AliceCoinTokenInfo.Symbol
})).TransactionResult;
transferToContractResult.Status.ShouldBe(TransactionResultStatus.Failed);
transferToContractResult.Error.ShouldContain("From address is in transfer blacklist");

// 8. AdvanceResourceToken should fail when sender is in blacklist
var advanceRet = await TokenContractStub.AdvanceResourceToken.SendWithExceptionAsync(
new AdvanceResourceTokenInput
{
ContractAddress = BasicFunctionContractAddress,
Amount = Amount,
ResourceTokenSymbol = trafficToken
});
advanceRet.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed);
advanceRet.TransactionResult.Error.ShouldContain("From address is in transfer blacklist");

// 9. Non-owner cannot remove from blacklist
var removeBlackListResult = await TokenContractStubUser.RemoveFromTransferBlackList.SendWithExceptionAsync(DefaultAddress);
removeBlackListResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed);
removeBlackListResult.TransactionResult.Error.ShouldContain("Unauthorized behavior");

// 10. Owner removes DefaultAddress from blacklist via parliament proposal
var removeProposalId = await CreateProposalAsync(TokenContractAddress, defaultParliament, nameof(TokenContractStub.RemoveFromTransferBlackList), DefaultAddress);
await ApproveWithMinersAsync(removeProposalId);
await ParliamentContractStub.Release.SendAsync(removeProposalId);
isInTransferBlackList = await TokenContractStubUser.IsInTransferBlackList.CallAsync(DefaultAddress);
isInTransferBlackList.Value.ShouldBe(false);

// 11. Transfer should succeed after removing from blacklist
var transferResult2 = await TokenContractStub.Transfer.SendAsync(new TransferInput
{
Amount = Amount,
Memo = "blacklist test",
Symbol = AliceCoinTokenInfo.Symbol,
To = User1Address
});
transferResult2.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined);

// 12. TransferFrom should succeed after removing from blacklist
transferFromResult = (await user1Stub.TransferFrom.SendAsync(new TransferFromInput
{
Amount = Amount,
From = DefaultAddress,
Memo = "blacklist test",
Symbol = AliceCoinTokenInfo.Symbol,
To = User1Address
})).TransactionResult;
transferFromResult.Status.ShouldBe(TransactionResultStatus.Mined);

// 13. CrossChainTransfer should succeed after removing from blacklist
crossChainTransferResult = (await TokenContractStub.CrossChainTransfer.SendAsync(new CrossChainTransferInput
{
Symbol = AliceCoinTokenInfo.Symbol,
Amount = Amount,
To = User1Address,
IssueChainId = 9992731,
Memo = "blacklist test",
ToChainId = 9992732
})).TransactionResult;
crossChainTransferResult.Status.ShouldBe(TransactionResultStatus.Mined);

// 14. Lock should succeed after removing from blacklist
lockTokenResult = (await BasicFunctionContractStub.LockToken.SendAsync(new LockTokenInput
{
Address = DefaultAddress,
Amount = Amount,
Symbol = AliceCoinTokenInfo.Symbol,
LockId = lockId,
Usage = "Testing."
})).TransactionResult;
lockTokenResult.Status.ShouldBe(TransactionResultStatus.Mined);

// 15. Transfer to contract should succeed after removing from blacklist
transferToContractResult = (await BasicFunctionContractStub.TransferTokenToContract.SendAsync(new TransferTokenToContractInput
{
Amount = Amount,
Symbol = AliceCoinTokenInfo.Symbol
})).TransactionResult;
transferToContractResult.Status.ShouldBe(TransactionResultStatus.Mined);

// 16. AdvanceResourceToken should succeed after removing from blacklist
advanceRet = await TokenContractStub.AdvanceResourceToken.SendAsync(
new AdvanceResourceTokenInput
{
ContractAddress = BasicFunctionContractAddress,
Amount = Amount,
ResourceTokenSymbol = trafficToken
});
advanceRet.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined);
}
}
Loading