diff --git a/AElf.All.sln b/AElf.All.sln index 7554592ce4..b95c9f20ab 100644 --- a/AElf.All.sln +++ b/AElf.All.sln @@ -369,6 +369,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.CSharp.CodeOps.UnitTes EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Kernel.SmartContract.ExecutionPluginForUserContractFee.Tests.TestContract", "test\AElf.Kernel.SmartContract.ExecutionPluginForUserContractFee.Tests.TestContract\AElf.Kernel.SmartContract.ExecutionPluginForUserContractFee.Tests.TestContract.csproj", "{AB27298B-E6BE-4ACB-ADF1-64239E2A7A1C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Contracts.TestContract.Vote", "test\AElf.Contracts.TestContract.Vote\AElf.Contracts.TestContract.Vote.csproj", "{860947CF-F081-4A3B-BE65-199ECE793616}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Contracts.TestContract.MockParliament", "test\AElf.Contracts.TestContract.MockParliament\AElf.Contracts.TestContract.MockParliament.csproj", "{C7EDD0EB-CCE7-4B06-BF1E-F9CA7F149097}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Contracts.TestContract.VirtualAddress", "test\AElf.Contracts.TestContract.VirtualAddress\AElf.Contracts.TestContract.VirtualAddress.csproj", "{64498F8C-B827-4E1C-B5FB-4B9188C839A8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1059,6 +1065,18 @@ Global {AB27298B-E6BE-4ACB-ADF1-64239E2A7A1C}.Debug|Any CPU.Build.0 = Debug|Any CPU {AB27298B-E6BE-4ACB-ADF1-64239E2A7A1C}.Release|Any CPU.ActiveCfg = Release|Any CPU {AB27298B-E6BE-4ACB-ADF1-64239E2A7A1C}.Release|Any CPU.Build.0 = Release|Any CPU + {860947CF-F081-4A3B-BE65-199ECE793616}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {860947CF-F081-4A3B-BE65-199ECE793616}.Debug|Any CPU.Build.0 = Debug|Any CPU + {860947CF-F081-4A3B-BE65-199ECE793616}.Release|Any CPU.ActiveCfg = Release|Any CPU + {860947CF-F081-4A3B-BE65-199ECE793616}.Release|Any CPU.Build.0 = Release|Any CPU + {C7EDD0EB-CCE7-4B06-BF1E-F9CA7F149097}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7EDD0EB-CCE7-4B06-BF1E-F9CA7F149097}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C7EDD0EB-CCE7-4B06-BF1E-F9CA7F149097}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C7EDD0EB-CCE7-4B06-BF1E-F9CA7F149097}.Release|Any CPU.Build.0 = Release|Any CPU + {64498F8C-B827-4E1C-B5FB-4B9188C839A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {64498F8C-B827-4E1C-B5FB-4B9188C839A8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {64498F8C-B827-4E1C-B5FB-4B9188C839A8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {64498F8C-B827-4E1C-B5FB-4B9188C839A8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1245,5 +1263,8 @@ Global {1B44277E-74EB-49B2-B8FD-05C29EE51985} = {4E54480A-D155-43ED-9736-1A5BE7957211} {D1A00CD6-958E-4E9F-8325-354309E3029E} = {4E54480A-D155-43ED-9736-1A5BE7957211} {AB27298B-E6BE-4ACB-ADF1-64239E2A7A1C} = {4E54480A-D155-43ED-9736-1A5BE7957211} + {860947CF-F081-4A3B-BE65-199ECE793616} = {D3950CC9-808F-4ED8-946A-79A992F3F8EF} + {C7EDD0EB-CCE7-4B06-BF1E-F9CA7F149097} = {D3950CC9-808F-4ED8-946A-79A992F3F8EF} + {64498F8C-B827-4E1C-B5FB-4B9188C839A8} = {D3950CC9-808F-4ED8-946A-79A992F3F8EF} EndGlobalSection EndGlobal diff --git a/bench/AElf.Benchmark/VRFTests.cs b/bench/AElf.Benchmark/VRFTests.cs new file mode 100644 index 0000000000..9dae76aafd --- /dev/null +++ b/bench/AElf.Benchmark/VRFTests.cs @@ -0,0 +1,45 @@ +using System; +using AElf.Cryptography; +using AElf.Cryptography.ECDSA; +using BenchmarkDotNet.Attributes; + +namespace AElf.Benchmark; + +[MarkdownExporterAttribute.GitHub] +public class VrfTests : BenchmarkTestBase +{ + private ECKeyPair _key; + private byte[] _alphaBytes; + private byte[] _piBytes; + + [Params(1,10,100,1000)] public int VectorCount; + + [GlobalSetup] + public void GlobalSetup() + { + _key = CryptoHelper.FromPrivateKey( + ByteArrayHelper.HexStringToByteArray("2eaeb403475a9962ad59302ab53fa2b668d2d24bf5a2a917707e5b2f4ded392b")); + var alpha = "fb779e58991c424eaaf10b3d364c3b3e756c7b435109a985e547c058964d7bd5"; + _alphaBytes = Convert.FromHexString(alpha); + var pi = "03ea6c4bdb4a9e1ae0a17c427ec074f68cdac7a57e4f3fded1b07d20dd5385baf05a9d1e4064cd1c2c5e8608e96b7e3e2058500f178b414b8e910178f17a7a77af7e88befeabceb77cae3e9fd2e1a6c051"; + _piBytes = Convert.FromHexString(pi); + } + + [Benchmark] + public void VrfProveTest() + { + for (var i = 0; i < VectorCount; i++) + { + CryptoHelper.ECVrfProve(_key, _alphaBytes); + } + } + + [Benchmark] + public void VrfVerifyTest() + { + for (var i = 0; i < VectorCount; i++) + { + CryptoHelper.ECVrfVerify(_key.PublicKey, _alphaBytes, _piBytes); + } + } +} \ No newline at end of file diff --git a/contract/AElf.Contracts.Consensus.AEDPoS/AEDPoSContract.cs b/contract/AElf.Contracts.Consensus.AEDPoS/AEDPoSContract.cs index 9517ca71b1..e129ea9236 100644 --- a/contract/AElf.Contracts.Consensus.AEDPoS/AEDPoSContract.cs +++ b/contract/AElf.Contracts.Consensus.AEDPoS/AEDPoSContract.cs @@ -158,7 +158,7 @@ public override Empty RecordCandidateReplacement(RecordCandidateReplacementInput #region NextRound - public override Empty NextRound(Round input) + public override Empty NextRound(NextRoundInput input) { SupplyCurrentRoundInformation(); ProcessConsensusInformation(input); diff --git a/contract/AElf.Contracts.Consensus.AEDPoS/AEDPoSContract_ACS4_ConsensusInformationProvider.cs b/contract/AElf.Contracts.Consensus.AEDPoS/AEDPoSContract_ACS4_ConsensusInformationProvider.cs index 4bbf853a65..9c059c426b 100644 --- a/contract/AElf.Contracts.Consensus.AEDPoS/AEDPoSContract_ACS4_ConsensusInformationProvider.cs +++ b/contract/AElf.Contracts.Consensus.AEDPoS/AEDPoSContract_ACS4_ConsensusInformationProvider.cs @@ -1,5 +1,6 @@ using System.Linq; using AElf.Standards.ACS4; +using AElf.Types; using Google.Protobuf; using Google.Protobuf.WellKnownTypes; @@ -66,9 +67,10 @@ public override TransactionList GenerateConsensusTransactions(BytesValue input) "Data to request consensus information should contain pubkey."); var pubkey = triggerInformation.Pubkey; + var randomNumber = triggerInformation.RandomNumber; var consensusInformation = new AElfConsensusHeaderInformation(); consensusInformation.MergeFrom(GetConsensusBlockExtraData(input, true).Value); - var transactionList = GenerateTransactionListByExtraData(consensusInformation, pubkey); + var transactionList = GenerateTransactionListByExtraData(consensusInformation, pubkey, randomNumber); return transactionList; } @@ -126,7 +128,7 @@ public override ValidationResult ValidateConsensusAfterExecution(BytesValue inpu } private TransactionList GenerateTransactionListByExtraData(AElfConsensusHeaderInformation consensusInformation, - ByteString pubkey) + ByteString pubkey, ByteString randomNumber) { var round = consensusInformation.Round; var behaviour = consensusInformation.Behaviour; @@ -140,7 +142,7 @@ private TransactionList GenerateTransactionListByExtraData(AElfConsensusHeaderIn Transactions = { GenerateTransaction(nameof(UpdateValue), - round.ExtractInformationToUpdateConsensus(pubkey.ToHex())) + round.ExtractInformationToUpdateConsensus(pubkey.ToHex(), randomNumber)) } }; case AElfConsensusBehaviour.TinyBlock: @@ -154,7 +156,8 @@ private TransactionList GenerateTransactionListByExtraData(AElfConsensusHeaderIn { ActualMiningTime = minerInRound.ActualMiningTimes.Last(), ProducedBlocks = minerInRound.ProducedBlocks, - RoundId = round.RoundIdForValidation + RoundId = round.RoundIdForValidation, + RandomNumber = randomNumber }) } }; @@ -163,7 +166,7 @@ private TransactionList GenerateTransactionListByExtraData(AElfConsensusHeaderIn { Transactions = { - GenerateTransaction(nameof(NextRound), round) + GenerateTransaction(nameof(NextRound), NextRoundInput.Create(round,randomNumber)) } }; case AElfConsensusBehaviour.NextTerm: @@ -171,7 +174,7 @@ private TransactionList GenerateTransactionListByExtraData(AElfConsensusHeaderIn { Transactions = { - GenerateTransaction(nameof(NextTerm), round) + GenerateTransaction(nameof(NextTerm), NextTermInput.Create(round,randomNumber)) } }; default: diff --git a/contract/AElf.Contracts.Consensus.AEDPoS/AEDPoSContract_NextTerm.cs b/contract/AElf.Contracts.Consensus.AEDPoS/AEDPoSContract_NextTerm.cs index fbe886f8a4..d6569b7cdd 100644 --- a/contract/AElf.Contracts.Consensus.AEDPoS/AEDPoSContract_NextTerm.cs +++ b/contract/AElf.Contracts.Consensus.AEDPoS/AEDPoSContract_NextTerm.cs @@ -10,7 +10,7 @@ namespace AElf.Contracts.Consensus.AEDPoS; // ReSharper disable once InconsistentNaming public partial class AEDPoSContract { - public override Empty NextTerm(Round input) + public override Empty NextTerm(NextTermInput input) { SupplyCurrentRoundInformation(); ProcessConsensusInformation(input); diff --git a/contract/AElf.Contracts.Consensus.AEDPoS/AEDPoSContract_ProcessConsensusInformation.cs b/contract/AElf.Contracts.Consensus.AEDPoS/AEDPoSContract_ProcessConsensusInformation.cs index 4e37a46580..fd1e42e738 100644 --- a/contract/AElf.Contracts.Consensus.AEDPoS/AEDPoSContract_ProcessConsensusInformation.cs +++ b/contract/AElf.Contracts.Consensus.AEDPoS/AEDPoSContract_ProcessConsensusInformation.cs @@ -5,6 +5,7 @@ using AElf.Sdk.CSharp; using AElf.Standards.ACS10; using AElf.Types; +using Google.Protobuf; using Google.Protobuf.WellKnownTypes; namespace AElf.Contracts.Consensus.AEDPoS; @@ -28,19 +29,25 @@ private void ProcessConsensusInformation(dynamic input, [CallerMemberName] strin State.RoundBeforeLatestExecution.Value = GetCurrentRoundInformation(new Empty()); + ByteString randomNumber = null; + // The only difference. switch (input) { - case Round round when callerMethodName == nameof(NextRound): - ProcessNextRound(round); + case NextRoundInput nextRoundInput: + randomNumber = nextRoundInput.RandomNumber; + ProcessNextRound(nextRoundInput); break; - case Round round when callerMethodName == nameof(NextTerm): - ProcessNextTerm(round); + case NextTermInput nextTermInput: + randomNumber = nextTermInput.RandomNumber; + ProcessNextTerm(nextTermInput); break; case UpdateValueInput updateValueInput: + randomNumber = updateValueInput.RandomNumber; ProcessUpdateValue(updateValueInput); break; case TinyBlockInput tinyBlockInput: + randomNumber = tinyBlockInput.RandomNumber; ProcessTinyBlock(tinyBlockInput); break; } @@ -65,14 +72,12 @@ private void ProcessConsensusInformation(dynamic input, [CallerMemberName] strin Context.LogDebug(() => $"Current round information:\n{currentRound.ToString(_processingBlockMinerPubkey)}"); - var latestSignature = GetLatestSignature(currentRound); - var previousRandomHash = State.RandomHashes[Context.CurrentHeight.Sub(1)]; - var randomHash = previousRandomHash == null - ? latestSignature - : HashHelper.XorAndCompute(previousRandomHash, latestSignature); - + var previousRandomHash = State.RandomHashes[Context.CurrentHeight.Sub(1)] ?? Hash.Empty; + Assert( + Context.ECVrfVerify(Context.RecoverPublicKey(), previousRandomHash.ToByteArray(), + randomNumber.ToByteArray(), out var beta), "Failed to verify random number."); + var randomHash = Hash.LoadFromByteArray(beta); State.RandomHashes[Context.CurrentHeight] = randomHash; - Context.LogDebug(() => $"New random hash generated: {randomHash} - height {Context.CurrentHeight}"); if (!State.IsMainChain.Value && currentRound.RoundNumber > 1) Release(); @@ -100,8 +105,10 @@ private Hash GetLatestSignature(Round currentRound) return latestSignature; } - private void ProcessNextRound(Round nextRound) + private void ProcessNextRound(NextRoundInput input) { + var nextRound = input.ToRound(); + RecordMinedMinerListOfCurrentRound(); TryToGetCurrentRoundInformation(out var currentRound); @@ -149,8 +156,10 @@ private void ProcessNextRound(Round nextRound) Assert(TryToUpdateRoundNumber(nextRound.RoundNumber), "Failed to update round number."); } - private void ProcessNextTerm(Round nextRound) + private void ProcessNextTerm(NextTermInput input) { + var nextRound = input.ToRound(); + RecordMinedMinerListOfCurrentRound(); // Count missed time slot of current round. diff --git a/contract/AElf.Contracts.Consensus.AEDPoS/AElf.Contracts.Consensus.AEDPoS.csproj b/contract/AElf.Contracts.Consensus.AEDPoS/AElf.Contracts.Consensus.AEDPoS.csproj index 13ae50e26c..e197019f68 100644 --- a/contract/AElf.Contracts.Consensus.AEDPoS/AElf.Contracts.Consensus.AEDPoS.csproj +++ b/contract/AElf.Contracts.Consensus.AEDPoS/AElf.Contracts.Consensus.AEDPoS.csproj @@ -1,5 +1,5 @@ - + net6.0 diff --git a/contract/AElf.Contracts.Consensus.AEDPoS/Types/NextRoundInput.cs b/contract/AElf.Contracts.Consensus.AEDPoS/Types/NextRoundInput.cs new file mode 100644 index 0000000000..e3240dc417 --- /dev/null +++ b/contract/AElf.Contracts.Consensus.AEDPoS/Types/NextRoundInput.cs @@ -0,0 +1,41 @@ +using Google.Protobuf; + +namespace AElf.Contracts.Consensus.AEDPoS; + +public partial class NextRoundInput +{ + public static NextRoundInput Create(Round round, ByteString randomNumber) + { + return new NextRoundInput + { + RoundNumber = round.RoundNumber, + RealTimeMinersInformation = { round.RealTimeMinersInformation }, + ExtraBlockProducerOfPreviousRound = round.ExtraBlockProducerOfPreviousRound, + BlockchainAge = round.BlockchainAge, + TermNumber = round.TermNumber, + ConfirmedIrreversibleBlockHeight = round.ConfirmedIrreversibleBlockHeight, + ConfirmedIrreversibleBlockRoundNumber = round.ConfirmedIrreversibleBlockRoundNumber, + IsMinerListJustChanged = round.IsMinerListJustChanged, + RoundIdForValidation = round.RoundIdForValidation, + MainChainMinersRoundNumber = round.MainChainMinersRoundNumber, + RandomNumber = randomNumber + }; + } + + public Round ToRound() + { + return new Round + { + RoundNumber = RoundNumber, + RealTimeMinersInformation = { RealTimeMinersInformation }, + ExtraBlockProducerOfPreviousRound = ExtraBlockProducerOfPreviousRound, + BlockchainAge = BlockchainAge, + TermNumber = TermNumber, + ConfirmedIrreversibleBlockHeight = ConfirmedIrreversibleBlockHeight, + ConfirmedIrreversibleBlockRoundNumber = ConfirmedIrreversibleBlockRoundNumber, + IsMinerListJustChanged = IsMinerListJustChanged, + RoundIdForValidation = RoundIdForValidation, + MainChainMinersRoundNumber = MainChainMinersRoundNumber + }; + } +} \ No newline at end of file diff --git a/contract/AElf.Contracts.Consensus.AEDPoS/Types/NextTermInput.cs b/contract/AElf.Contracts.Consensus.AEDPoS/Types/NextTermInput.cs new file mode 100644 index 0000000000..74afc3daac --- /dev/null +++ b/contract/AElf.Contracts.Consensus.AEDPoS/Types/NextTermInput.cs @@ -0,0 +1,41 @@ +using Google.Protobuf; + +namespace AElf.Contracts.Consensus.AEDPoS; + +public partial class NextTermInput +{ + public static NextTermInput Create(Round round, ByteString randomNumber) + { + return new NextTermInput + { + RoundNumber = round.RoundNumber, + RealTimeMinersInformation = { round.RealTimeMinersInformation }, + ExtraBlockProducerOfPreviousRound = round.ExtraBlockProducerOfPreviousRound, + BlockchainAge = round.BlockchainAge, + TermNumber = round.TermNumber, + ConfirmedIrreversibleBlockHeight = round.ConfirmedIrreversibleBlockHeight, + ConfirmedIrreversibleBlockRoundNumber = round.ConfirmedIrreversibleBlockRoundNumber, + IsMinerListJustChanged = round.IsMinerListJustChanged, + RoundIdForValidation = round.RoundIdForValidation, + MainChainMinersRoundNumber = round.MainChainMinersRoundNumber, + RandomNumber = randomNumber + }; + } + + public Round ToRound() + { + return new Round + { + RoundNumber = RoundNumber, + RealTimeMinersInformation = { RealTimeMinersInformation }, + ExtraBlockProducerOfPreviousRound = ExtraBlockProducerOfPreviousRound, + BlockchainAge = BlockchainAge, + TermNumber = TermNumber, + ConfirmedIrreversibleBlockHeight = ConfirmedIrreversibleBlockHeight, + ConfirmedIrreversibleBlockRoundNumber = ConfirmedIrreversibleBlockRoundNumber, + IsMinerListJustChanged = IsMinerListJustChanged, + RoundIdForValidation = RoundIdForValidation, + MainChainMinersRoundNumber = MainChainMinersRoundNumber + }; + } +} \ No newline at end of file diff --git a/contract/AElf.Contracts.Consensus.AEDPoS/Types/Round_ExtractInformationToUpdateConsensus.cs b/contract/AElf.Contracts.Consensus.AEDPoS/Types/Round_ExtractInformationToUpdateConsensus.cs index 786eef5ca0..b0d93721cb 100644 --- a/contract/AElf.Contracts.Consensus.AEDPoS/Types/Round_ExtractInformationToUpdateConsensus.cs +++ b/contract/AElf.Contracts.Consensus.AEDPoS/Types/Round_ExtractInformationToUpdateConsensus.cs @@ -1,5 +1,6 @@ using System.Linq; using AElf.Types; +using Google.Protobuf; namespace AElf.Contracts.Consensus.AEDPoS; @@ -10,8 +11,9 @@ public partial class Round /// will record this purpose to their FinalOrderOfNextRound field. /// /// + /// /// - public UpdateValueInput ExtractInformationToUpdateConsensus(string pubkey) + public UpdateValueInput ExtractInformationToUpdateConsensus(string pubkey, ByteString randomNumber) { if (!RealTimeMinersInformation.ContainsKey(pubkey)) return null; @@ -43,7 +45,8 @@ public UpdateValueInput ExtractInformationToUpdateConsensus(string pubkey) EncryptedPieces = { minerInRound.EncryptedPieces }, DecryptedPieces = { decryptedPreviousInValues }, MinersPreviousInValues = { minersPreviousInValues }, - ImpliedIrreversibleBlockHeight = minerInRound.ImpliedIrreversibleBlockHeight + ImpliedIrreversibleBlockHeight = minerInRound.ImpliedIrreversibleBlockHeight, + RandomNumber = randomNumber }; } } \ No newline at end of file diff --git a/contract/AElf.Contracts.CrossChain/CrossChainContract_Helper.cs b/contract/AElf.Contracts.CrossChain/CrossChainContract_Helper.cs index 9ae9dac72b..5f01430998 100644 --- a/contract/AElf.Contracts.CrossChain/CrossChainContract_Helper.cs +++ b/contract/AElf.Contracts.CrossChain/CrossChainContract_Helper.cs @@ -189,7 +189,8 @@ private void CreateSideChainToken(SideChainCreationRequest sideChainCreationRequ Issuer = creator, IssueChainId = chainId, Symbol = sideChainCreationRequest.SideChainTokenCreationRequest.SideChainTokenSymbol, - TotalSupply = sideChainCreationRequest.SideChainTokenCreationRequest.SideChainTokenTotalSupply + TotalSupply = sideChainCreationRequest.SideChainTokenCreationRequest.SideChainTokenTotalSupply, + Owner = creator }); } diff --git a/contract/AElf.Contracts.Economic/EconomicContract.cs b/contract/AElf.Contracts.Economic/EconomicContract.cs index f9c0fbe353..e971433777 100644 --- a/contract/AElf.Contracts.Economic/EconomicContract.cs +++ b/contract/AElf.Contracts.Economic/EconomicContract.cs @@ -59,7 +59,8 @@ private void CreateNativeToken(InitialEconomicSystemInput input) Decimals = input.NativeTokenDecimals, IsBurnable = input.IsNativeTokenBurnable, Issuer = Context.Self, - LockWhiteList = { lockWhiteList } + LockWhiteList = { lockWhiteList }, + Owner = Context.Self }); State.TokenContract.SetPrimaryTokenSymbol.Send(new SetPrimaryTokenSymbolInput @@ -88,7 +89,8 @@ private void CreateResourceTokens() Decimals = EconomicContractConstants.ResourceTokenDecimals, Issuer = Context.Self, LockWhiteList = { lockWhiteList }, - IsBurnable = true + IsBurnable = true, + Owner = Context.Self }); State.TokenContract.Issue.Send(new IssueInput @@ -120,7 +122,8 @@ private void CreateElectionTokens() Decimals = EconomicContractConstants.ElectionTokenDecimals, Issuer = Context.Self, IsBurnable = true, - LockWhiteList = { lockWhiteList } + LockWhiteList = { lockWhiteList }, + Owner = Context.Self }); State.TokenContract.Issue.Send(new IssueInput { diff --git a/contract/AElf.Contracts.Election/ElectionContractState.cs b/contract/AElf.Contracts.Election/ElectionContractState.cs index 90920ac79d..f9bfd7125e 100644 --- a/contract/AElf.Contracts.Election/ElectionContractState.cs +++ b/contract/AElf.Contracts.Election/ElectionContractState.cs @@ -15,6 +15,7 @@ public partial class ElectionContractState : ContractState public SingletonState FlexibleHash { get; set; } public SingletonState WelcomeHash { get; set; } + // Old:Pubkey/New:Address -> ElectorVote public MappedState ElectorVotes { get; set; } public MappedState CandidateVotes { get; set; } diff --git a/contract/AElf.Contracts.Election/ElectionContract_Elector.cs b/contract/AElf.Contracts.Election/ElectionContract_Elector.cs index a021802fb6..eaf28224fe 100644 --- a/contract/AElf.Contracts.Election/ElectionContract_Elector.cs +++ b/contract/AElf.Contracts.Election/ElectionContract_Elector.cs @@ -3,12 +3,10 @@ using System.Linq; using AElf.Contracts.MultiToken; using AElf.Contracts.Profit; -using AElf.Contracts.Treasury; using AElf.Contracts.Vote; using AElf.CSharp.Core; using AElf.Sdk.CSharp; using AElf.Types; -using Google.Protobuf; using Google.Protobuf.WellKnownTypes; namespace AElf.Contracts.Election; @@ -129,7 +127,7 @@ private void ExtendVoterWelfareProfits(Hash voteId) { var treasury = State.ProfitContract.GetScheme.Call(State.TreasuryHash.Value); var electionVotingRecord = GetElectionVotingRecordByVoteId(voteId); - + // Extend endPeriod from now no, so the lockTime will *NOT* be changed. var lockTime = State.LockTimeMap[voteId]; var lockPeriod = lockTime.Div(State.TimeEachTerm.Value); @@ -182,7 +180,7 @@ private ProfitDetail GetProfitDetailByElectionVotingRecord(ElectionVotingRecord { profitDetail = profitDetails.Details.LastOrDefault(d => d.Shares == electionVotingRecord.Weight); } - + return profitDetail; } @@ -326,7 +324,7 @@ private void AssertValidLockSeconds(long lockSeconds) Assert(lockSeconds <= State.MaximumLockTime.Value, $"Invalid lock time. At most {State.MaximumLockTime.Value.Div(60).Div(60).Div(24)} days"); } - + private void LockTokensOfVoter(long amount, Hash voteId) { State.TokenContract.Lock.Send(new LockInput @@ -499,14 +497,13 @@ private void TryToBecomeAValidationDataCenter(VoteMinerInput input, long candida /// private void UpdateElectorInformation(byte[] recoveredPublicKey, long amount, Hash voteId) { - var voterPublicKey = recoveredPublicKey.ToHex(); - var voterPublicKeyByteString = ByteString.CopyFrom(recoveredPublicKey); - var voterVotes = State.ElectorVotes[voterPublicKey]; + var voterVotes = GetElectorVote(recoveredPublicKey); + if (voterVotes == null) { voterVotes = new ElectorVote { - Pubkey = voterPublicKeyByteString, + Address = Context.Sender, ActiveVotingRecordIds = { voteId }, ActiveVotedVotesAmount = amount, AllVotedVotesAmount = amount @@ -519,7 +516,25 @@ private void UpdateElectorInformation(byte[] recoveredPublicKey, long amount, Ha voterVotes.AllVotedVotesAmount = voterVotes.AllVotedVotesAmount.Add(amount); } - State.ElectorVotes[voterPublicKey] = voterVotes; + State.ElectorVotes[Context.Sender.ToBase58()] = voterVotes; + } + + private ElectorVote GetElectorVote(byte[] recoveredPublicKey) + { + var voterVotes = State.ElectorVotes[Context.Sender.ToBase58()]; + if (voterVotes != null) return voterVotes; + + if (recoveredPublicKey == null) return null; + + var publicKey = recoveredPublicKey.ToHex(); + + voterVotes = State.ElectorVotes[publicKey]?.Clone(); + + if (voterVotes == null) return null; + voterVotes.Address ??= Context.Sender; + + State.ElectorVotes.Remove(publicKey); + return voterVotes; } /// @@ -620,22 +635,23 @@ public override Empty Withdraw(Hash input) Assert(actualLockedTime >= claimedLockDays, $"Still need {claimedLockDays.Sub(actualLockedTime).Div(86400)} days to unlock your token."); - // Update Elector's Votes information. - var voterPublicKey = Context.RecoverPublicKey().ToHex(); - var voterVotes = State.ElectorVotes[voterPublicKey]; - if (voterVotes == null) throw new AssertionException($"Voter {voterPublicKey} never votes before."); + var voterPublicKey = Context.RecoverPublicKey(); + + var voterVotes = GetElectorVote(voterPublicKey); + + Assert(voterVotes != null, $"Voter {Context.Sender.ToBase58()} never votes before"); voterVotes.ActiveVotingRecordIds.Remove(input); voterVotes.WithdrawnVotingRecordIds.Add(input); voterVotes.ActiveVotedVotesAmount = voterVotes.ActiveVotedVotesAmount.Sub(votingRecord.Amount); - State.ElectorVotes[voterPublicKey] = voterVotes; + + State.ElectorVotes[Context.Sender.ToBase58()] = voterVotes; // Update Candidate's Votes information. var newestPubkey = GetNewestPubkey(votingRecord.Option); var candidateVotes = State.CandidateVotes[newestPubkey]; - if (candidateVotes == null) - throw new AssertionException( - $"Newest pubkey {newestPubkey} is invalid. Old pubkey is {votingRecord.Option}"); + + Assert(candidateVotes != null, $"Newest pubkey {newestPubkey} is invalid. Old pubkey is {votingRecord.Option}"); candidateVotes.ObtainedActiveVotingRecordIds.Remove(input); candidateVotes.ObtainedWithdrawnVotingRecordIds.Add(input); @@ -744,12 +760,12 @@ private void NotifyProfitReplaceCandidateInDataCenter(string oldCandidateInDataC #region subsidy helper - private Hash GenerateSubsidyId(string pubkey,Address beneficiaryAddress) + private Hash GenerateSubsidyId(string pubkey, Address beneficiaryAddress) { return HashHelper.ConcatAndCompute(HashHelper.ComputeFrom(pubkey), HashHelper.ComputeFrom(beneficiaryAddress), HashHelper.ComputeFrom(Context.Self)); } - + private Address GetProfitsReceiverOrDefault(string pubkey) { if (State.TreasuryContract.Value == null) @@ -765,7 +781,7 @@ private Address GetProfitsReceiverOrDefault(string pubkey) private void AddBeneficiary(string candidatePubkey, Address profitsReceiver = null) { var beneficiaryAddress = GetBeneficiaryAddress(candidatePubkey, profitsReceiver); - var subsidyId = GenerateSubsidyId(candidatePubkey,beneficiaryAddress); + var subsidyId = GenerateSubsidyId(candidatePubkey, beneficiaryAddress); State.ProfitContract.AddBeneficiary.Send(new AddBeneficiaryInput { SchemeId = State.SubsidyHash.Value, @@ -778,10 +794,10 @@ private void AddBeneficiary(string candidatePubkey, Address profitsReceiver = nu }); } - private void RemoveBeneficiary(string candidatePubkey,Address profitsReceiver = null) + private void RemoveBeneficiary(string candidatePubkey, Address profitsReceiver = null) { var beneficiaryAddress = GetBeneficiaryAddress(candidatePubkey, profitsReceiver); - var previousSubsidyId = GenerateSubsidyId(candidatePubkey,beneficiaryAddress); + var previousSubsidyId = GenerateSubsidyId(candidatePubkey, beneficiaryAddress); State.ProfitContract.RemoveBeneficiary.Send(new RemoveBeneficiaryInput { SchemeId = State.SubsidyHash.Value, @@ -798,6 +814,6 @@ private Address GetBeneficiaryAddress(string candidatePubkey, Address profitsRec : Address.FromPublicKey(ByteArrayHelper.HexStringToByteArray(candidatePubkey)); return beneficiaryAddress; } - + #endregion } \ No newline at end of file diff --git a/contract/AElf.Contracts.Election/ViewMethods.cs b/contract/AElf.Contracts.Election/ViewMethods.cs index e3f318e211..ef8735b3d5 100644 --- a/contract/AElf.Contracts.Election/ViewMethods.cs +++ b/contract/AElf.Contracts.Election/ViewMethods.cs @@ -161,20 +161,16 @@ private TermSnapshot GetPreviousTermSnapshotWithNewestPubkey() public override ElectorVote GetElectorVote(StringValue input) { - return State.ElectorVotes[input.Value] ?? new ElectorVote - { - Pubkey = ByteStringHelper.FromHexString(input.Value) - }; + return GetElectorVote(input.Value); } public override ElectorVote GetElectorVoteWithRecords(StringValue input) { - var votes = State.ElectorVotes[input.Value]; - if (votes == null) - return new ElectorVote - { - Pubkey = ByteStringHelper.FromHexString(input.Value) - }; + var votes = GetElectorVote(input.Value); + + if (votes.Address == null && votes.Pubkey == null) + return votes; + var votedRecords = State.VoteContract.GetVotingRecords.Call(new GetVotingRecordsInput { Ids = { votes.ActiveVotingRecordIds } @@ -189,6 +185,21 @@ public override ElectorVote GetElectorVoteWithRecords(StringValue input) return votes; } + private ElectorVote GetElectorVote(string value) + { + Assert(value != null && value.Length > 1, "Invalid input."); + + var voterVotes = State.ElectorVotes[value]; + + if (voterVotes == null && !AddressHelper.VerifyFormattedAddress(value)) + { + voterVotes = State.ElectorVotes[ + Address.FromPublicKey(ByteArrayHelper.HexStringToByteArray(value)).ToBase58()]; + } + + return voterVotes ?? new ElectorVote(); + } + public override ElectorVote GetElectorVoteWithAllRecords(StringValue input) { var votes = GetElectorVoteWithRecords(input); diff --git a/contract/AElf.Contracts.Genesis/BasicContractZero.cs b/contract/AElf.Contracts.Genesis/BasicContractZero.cs index ff29b81689..9f47577bed 100644 --- a/contract/AElf.Contracts.Genesis/BasicContractZero.cs +++ b/contract/AElf.Contracts.Genesis/BasicContractZero.cs @@ -365,6 +365,7 @@ public override Empty SetContractProposalExpirationTimePeriod(SetContractProposa public override DeployUserSmartContractOutput DeployUserSmartContract(ContractDeploymentInput input) { + AssertInlineDeployOrUpdateUserContract(); AssertUserDeployContract(); var codeHash = HashHelper.ComputeFrom(input.Code.ToByteArray()); @@ -395,6 +396,8 @@ public override DeployUserSmartContractOutput DeployUserSmartContract(ContractDe public override Empty UpdateUserSmartContract(ContractUpdateInput input) { + AssertInlineDeployOrUpdateUserContract(); + var info = State.ContractInfos[input.Address]; Assert(info != null, "Contract not found."); Assert(Context.Sender == info.Author, "No permission."); diff --git a/contract/AElf.Contracts.Genesis/BasicContractZero_Helper.cs b/contract/AElf.Contracts.Genesis/BasicContractZero_Helper.cs index 04aa2da726..40678c4203 100644 --- a/contract/AElf.Contracts.Genesis/BasicContractZero_Helper.cs +++ b/contract/AElf.Contracts.Genesis/BasicContractZero_Helper.cs @@ -359,6 +359,16 @@ private void AssertContractExists(Hash codeHash) { Assert(State.SmartContractRegistrations[codeHash] == null, "contract code has already been deployed before."); } + + private void AssertInlineDeployOrUpdateUserContract() + { + Assert(Context.Origin == Context.Sender || !IsMainChain(), "Deploy or update contracts using inline transactions is not allowed."); + } + + private bool IsMainChain() + { + return Context.GetContractAddressByName(SmartContractConstants.TreasuryContractSystemName) != null; + } } public static class AddressHelper diff --git a/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs b/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs index 2f407988d3..a14a4e9496 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs @@ -14,8 +14,12 @@ public static class TokenContractConstants public const string LockCallbackExternalInfoKey = "aelf_lock_callback"; public const string UnlockCallbackExternalInfoKey = "aelf_unlock_callback"; public const string LogEventExternalInfoKey = "aelf_log_event"; - public const int DELEGATEE_MAX_COUNT = 128; + public const int DELEGATEE_MAX_COUNT = 24; public const char NFTSymbolSeparator = '-'; public const int NFTSymbolMaxLength = 30; - public const string UserContractMethodFeeKey = "UserContractMethodFee"; + public const string UserContractMethodFeeKey = "UserContractMethodFee"; + public const string CollectionSymbolSuffix = "0"; + public const string SeedCollectionSymbol = "SEED-0"; + public const string SeedOwnedSymbolExternalInfoKey = "__seed_owned_symbol"; + public const string SeedExpireTimeExternalInfoKey = "__seed_exp_time"; } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContractState.cs b/contract/AElf.Contracts.MultiToken/TokenContractState.cs index cac95c40af..92da423908 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContractState.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContractState.cs @@ -9,6 +9,7 @@ public partial class TokenContractState : ContractState public StringState ChainPrimaryTokenSymbol { get; set; } public MappedState TokenInfos { get; set; } + public MappedState SymbolSeedMap { get; set; } public MappedState Balances { get; set; } public MappedState Allowances { get; set; } @@ -54,4 +55,12 @@ public partial class TokenContractState : ContractState public MappedState CreateTokenWhiteListMap { get; set; } public MappedState TransactionFeeDelegateesMap { get; set; } + + /// + /// delegator address -> contract address -> method name -> delegatee info + /// + public MappedState TransactionFeeDelegateInfoMap { get; set; } + + public SingletonState
ElectionContractAddress { get; set; } + public SingletonState
VoteContractAddress { get; set; } } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContractState_ChargeFee.cs b/contract/AElf.Contracts.MultiToken/TokenContractState_ChargeFee.cs index 9fdf6cbe0c..d841f24d7e 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContractState_ChargeFee.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContractState_ChargeFee.cs @@ -34,8 +34,28 @@ public partial class TokenContractState /// Symbol -> Amount ///
public MappedState OwningRental { get; set; } - + public SingletonState MethodFeeFreeAllowancesConfig { get; set; } public MappedState MethodFeeFreeAllowancesMap { get; set; } public MappedState MethodFeeFreeAllowancesLastRefreshTimeMap { get; set; } + + /// + /// Symbol List + /// + public SingletonState TransactionFeeFreeAllowancesSymbolList { get; set; } + + /// + /// Symbol -> TransactionFeeFreeAllowanceConfig + /// + public MappedState TransactionFeeFreeAllowancesConfigMap { get; set; } + + /// + /// Address -> Symbol -> TransactionFeeFreeAllowanceMap + /// + public MappedState TransactionFeeFreeAllowances { get; set; } + + /// + /// Address -> Symbol -> LastRefreshTime + /// + public MappedState TransactionFeeFreeAllowancesLastRefreshTimes { get; set; } } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_ACS1_MethodFeeProvider.cs b/contract/AElf.Contracts.MultiToken/TokenContract_ACS1_MethodFeeProvider.cs index ff15f9a9c6..9ef198b39c 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_ACS1_MethodFeeProvider.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_ACS1_MethodFeeProvider.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using AElf.Sdk.CSharp; using AElf.Standards.ACS1; using AElf.Standards.ACS3; @@ -47,20 +48,6 @@ public override MethodFees GetMethodFee(StringValue input) IsSizeFeeFree = true }; var fees = State.TransactionFees[input.Value]; - if (input.Value == nameof(Create) && fees == null) - return new MethodFees - { - MethodName = input.Value, - Fees = - { - new MethodFee - { - Symbol = Context.Variables.NativeSymbol, - BasicFee = 10000_00000000 - } - } - }; - return fees; } @@ -74,14 +61,33 @@ public override AuthorityInfo GetMethodFeeController(Empty input) #region private methods - private List GetMethodFeeSymbols() + private List GetTransactionFeeSymbols(string methodName) { var symbols = new List(); - var primaryTokenSymbol = GetPrimaryTokenSymbol(new Empty()).Value; - if (primaryTokenSymbol != string.Empty) symbols.Add(primaryTokenSymbol); + if (State.TransactionFees[methodName] != null) + { + foreach (var methodFee in State.TransactionFees[methodName].Fees) + { + if (!symbols.Contains(methodFee.Symbol) && methodFee.BasicFee > 0) + symbols.Add(methodFee.Symbol); + } + if (State.TransactionFees[methodName].IsSizeFeeFree) + { + return symbols; + } + } + + if (State.SymbolListToPayTxSizeFee.Value == null) return symbols; + + foreach (var sizeFee in State.SymbolListToPayTxSizeFee.Value.SymbolsToPayTxSizeFee) + { + if (!symbols.Contains(sizeFee.TokenSymbol)) + symbols.Add(sizeFee.TokenSymbol); + } return symbols; } + private void RequiredMethodFeeControllerSet() { if (State.MethodFeeController.Value != null) return; diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_ACS2_StatePathsProvider.cs b/contract/AElf.Contracts.MultiToken/TokenContract_ACS2_StatePathsProvider.cs index bf075eec0c..8d12f604e1 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_ACS2_StatePathsProvider.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_ACS2_StatePathsProvider.cs @@ -1,4 +1,4 @@ -using System; +using System.Collections.Generic; using System.Linq; using AElf.Standards.ACS2; using AElf.Types; @@ -20,19 +20,20 @@ public override ResourceInfo GetResourceInfo(Transaction txn) WritePaths = { GetPath(nameof(TokenContractState.Balances), txn.From.ToString(), args.Symbol), - GetPath(nameof(TokenContractState.Balances), args.To.ToString(), args.Symbol), - GetPath(nameof(TokenContractState.MethodFeeFreeAllowancesMap), txn.From.ToString()), - GetPath(nameof(TokenContractState.MethodFeeFreeAllowancesLastRefreshTimeMap), txn.From.ToString()) + GetPath(nameof(TokenContractState.Balances), args.To.ToString(), args.Symbol) }, ReadPaths = { GetPath(nameof(TokenContractState.TokenInfos), args.Symbol), GetPath(nameof(TokenContractState.ChainPrimaryTokenSymbol)), - GetPath(nameof(TokenContractState.MethodFeeFreeAllowancesConfig)) + GetPath(nameof(TokenContractState.TransactionFeeFreeAllowancesSymbolList)) } }; - AddPathForTransactionFee(resourceInfo, txn.From.ToString()); - AddPathForDelegatees(resourceInfo, txn.From); + + AddPathForTransactionFee(resourceInfo, txn.From.ToString(), txn.MethodName); + AddPathForDelegatees(resourceInfo, txn.From, txn.To, txn.MethodName); + AddPathForTransactionFeeFreeAllowance(resourceInfo, txn.From); + return resourceInfo; } @@ -47,19 +48,20 @@ public override ResourceInfo GetResourceInfo(Transaction txn) args.Symbol), GetPath(nameof(TokenContractState.Balances), args.From.ToString(), args.Symbol), GetPath(nameof(TokenContractState.Balances), args.To.ToString(), args.Symbol), - GetPath(nameof(TokenContractState.LockWhiteLists), args.Symbol, txn.From.ToString()), - GetPath(nameof(TokenContractState.MethodFeeFreeAllowancesMap), txn.From.ToString()), - GetPath(nameof(TokenContractState.MethodFeeFreeAllowancesLastRefreshTimeMap), txn.From.ToString()) + GetPath(nameof(TokenContractState.LockWhiteLists), args.Symbol, txn.From.ToString()) }, ReadPaths = { GetPath(nameof(TokenContractState.TokenInfos), args.Symbol), GetPath(nameof(TokenContractState.ChainPrimaryTokenSymbol)), - GetPath(nameof(TokenContractState.MethodFeeFreeAllowancesConfig)) + GetPath(nameof(TokenContractState.TransactionFeeFreeAllowancesSymbolList)) } }; - AddPathForTransactionFee(resourceInfo, txn.From.ToString()); - AddPathForDelegatees(resourceInfo, txn.From); + + AddPathForTransactionFee(resourceInfo, txn.From.ToString(), txn.MethodName); + AddPathForDelegatees(resourceInfo, txn.From, txn.To, txn.MethodName); + AddPathForTransactionFeeFreeAllowance(resourceInfo, txn.From); + return resourceInfo; } @@ -68,9 +70,10 @@ public override ResourceInfo GetResourceInfo(Transaction txn) } } - private void AddPathForTransactionFee(ResourceInfo resourceInfo, String from) + + private void AddPathForTransactionFee(ResourceInfo resourceInfo, string from, string methodName) { - var symbols = GetMethodFeeSymbols(); + var symbols = GetTransactionFeeSymbols(methodName); var primaryTokenSymbol = GetPrimaryTokenSymbol(new Empty()).Value; if (_primaryTokenSymbol != string.Empty && !symbols.Contains(primaryTokenSymbol)) symbols.Add(primaryTokenSymbol); @@ -95,20 +98,60 @@ private ScopedStatePath GetPath(params string[] parts) } } }; - } - - private void AddPathForDelegatees(ResourceInfo resourceInfo, Address from) + } + + private void AddPathForDelegatees(ResourceInfo resourceInfo, Address from, Address to, string methodName) { - var allDelegatees = State.TransactionFeeDelegateesMap[from]; + var delegateeList = new List(); + //get and add first-level delegatee list + delegateeList.AddRange(GetDelegateeList(from, to, methodName)); + if (delegateeList.Count <= 0) return; + var secondDelegateeList = new List(); + //get and add second-level delegatee list + foreach (var delegateeAddress in delegateeList.Select(Address.FromBase58)) + { + //delegatee of the first-level delegate is delegator of the second-level delegate + secondDelegateeList.AddRange(GetDelegateeList(delegateeAddress, to, methodName)); + } + delegateeList.AddRange(secondDelegateeList); + foreach (var delegatee in delegateeList.Distinct()) + { + AddPathForTransactionFee(resourceInfo, delegatee, methodName); + AddPathForTransactionFeeFreeAllowance(resourceInfo, Address.FromBase58(delegatee)); + } + } + + private List GetDelegateeList(Address delegator, Address to, string methodName) + { + var delegateeList = new List(); + var allDelegatees = State.TransactionFeeDelegateInfoMap[delegator][to][methodName] + ?? State.TransactionFeeDelegateesMap[delegator]; + if (allDelegatees != null) { - foreach (var delegations in allDelegatees.Delegatees.Keys) + delegateeList.AddRange(allDelegatees.Delegatees.Keys.ToList()); + } + + return delegateeList; + } + + private void AddPathForTransactionFeeFreeAllowance(ResourceInfo resourceInfo, Address from) + { + var symbols = State.TransactionFeeFreeAllowancesSymbolList.Value?.Symbols; + if (symbols != null) + { + foreach (var symbol in symbols) { - if (delegations == null) return; - var add = Address.FromBase58(delegations).ToString(); - AddPathForTransactionFee(resourceInfo, add); - resourceInfo.WritePaths.Add(GetPath(nameof(TokenContractState.MethodFeeFreeAllowancesMap), add)); - resourceInfo.WritePaths.Add(GetPath(nameof(TokenContractState.MethodFeeFreeAllowancesLastRefreshTimeMap), add)); + resourceInfo.WritePaths.Add(GetPath(nameof(TokenContractState.TransactionFeeFreeAllowances), + from.ToBase58(), symbol)); + resourceInfo.WritePaths.Add(GetPath( + nameof(TokenContractState.TransactionFeeFreeAllowancesLastRefreshTimes), from.ToBase58(), symbol)); + + var path = GetPath(nameof(TokenContractState.TransactionFeeFreeAllowancesConfigMap), symbol); + if (!resourceInfo.ReadPaths.Contains(path)) + { + resourceInfo.ReadPaths.Add(path); + } } } } diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index f627b867a7..483f073b54 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -35,7 +35,10 @@ public override Empty Create(CreateInput input) // can not call create on side chain Assert(State.SideChainCreator.Value == null, "Failed to create token if side chain creator already set."); var inputSymbolType = GetCreateInputSymbolType(input.Symbol); - ChargeCreateFees(); + if (input.Owner == null) + { + input.Owner = input.Issuer; + } return inputSymbolType switch { SymbolType.NftCollection => CreateNFTCollection(input), @@ -47,6 +50,20 @@ public override Empty Create(CreateInput input) private Empty CreateToken(CreateInput input, SymbolType symbolType = SymbolType.Token) { AssertValidCreateInput(input, symbolType); + if (symbolType == SymbolType.Token || symbolType == SymbolType.NftCollection) + { + if (!IsAddressInCreateWhiteList(Context.Sender) && + input.Symbol != TokenContractConstants.SeedCollectionSymbol) + { + var symbolSeed = State.SymbolSeedMap[input.Symbol]; + CheckSeedNFT(symbolSeed, input.Symbol); + // seed nft for one-time use only + long balance = State.Balances[Context.Sender][symbolSeed]; + DoTransferFrom(Context.Sender, Context.Self, Context.Self, symbolSeed, balance, ""); + Burn(Context.Self, symbolSeed, balance); + } + } + var tokenInfo = new TokenInfo { Symbol = input.Symbol, @@ -56,7 +73,8 @@ private Empty CreateToken(CreateInput input, SymbolType symbolType = SymbolType. Issuer = input.Issuer, IsBurnable = input.IsBurnable, IssueChainId = input.IssueChainId == 0 ? Context.ChainId : input.IssueChainId, - ExternalInfo = input.ExternalInfo ?? new ExternalInfo() + ExternalInfo = input.ExternalInfo ?? new ExternalInfo(), + Owner = input.Owner }; RegisterTokenInfo(tokenInfo); if (string.IsNullOrEmpty(State.NativeTokenSymbol.Value)) @@ -81,12 +99,29 @@ private Empty CreateToken(CreateInput input, SymbolType symbolType = SymbolType. Issuer = tokenInfo.Issuer, IsBurnable = tokenInfo.IsBurnable, IssueChainId = tokenInfo.IssueChainId, - ExternalInfo = tokenInfo.ExternalInfo + ExternalInfo = tokenInfo.ExternalInfo, + Owner = tokenInfo.Owner }); return new Empty(); } + private void CheckSeedNFT(string symbolSeed, String symbol) + { + Assert(!string.IsNullOrEmpty(symbolSeed), "Seed NFT does not exist."); + var tokenInfo = State.TokenInfos[symbolSeed]; + Assert(tokenInfo != null, "Seed NFT does not exist."); + Assert(State.Balances[Context.Sender][symbolSeed] > 0, "Seed NFT balance is not enough."); + Assert(tokenInfo.ExternalInfo != null && tokenInfo.ExternalInfo.Value.TryGetValue( + TokenContractConstants.SeedOwnedSymbolExternalInfoKey, out var ownedSymbol) && ownedSymbol == symbol, + "Invalid OwnedSymbol."); + Assert(tokenInfo.ExternalInfo.Value.TryGetValue(TokenContractConstants.SeedExpireTimeExternalInfoKey, + out var expirationTime) + && long.TryParse(expirationTime, out var expirationTimeLong) && + Context.CurrentBlockTime.Seconds <= expirationTimeLong, "OwnedSymbol is expired."); + } + + /// /// Set primary token symbol. /// @@ -95,7 +130,7 @@ private Empty CreateToken(CreateInput input, SymbolType symbolType = SymbolType. public override Empty SetPrimaryTokenSymbol(SetPrimaryTokenSymbolInput input) { Assert(State.ChainPrimaryTokenSymbol.Value == null, "Failed to set primary token symbol."); - Assert(State.TokenInfos[input.Symbol] != null, "Invalid input."); + Assert(!string.IsNullOrWhiteSpace(input.Symbol) && State.TokenInfos[input.Symbol] != null, "Invalid input symbol."); State.ChainPrimaryTokenSymbol.Value = input.Symbol; Context.Fire(new ChainPrimaryTokenSymbolSet { TokenSymbol = input.Symbol }); @@ -150,8 +185,13 @@ public override Empty Transfer(TransferInput input) public override Empty Lock(LockInput input) { + Assert(!string.IsNullOrWhiteSpace(input.Symbol), "Invalid input symbol."); + AssertValidInputAddress(input.Address); AssertSystemContractOrLockWhiteListAddress(input.Symbol); - Assert(Context.Origin == input.Address, "Lock behaviour should be initialed by origin address."); + + Assert(IsInLockWhiteList(Context.Sender) || Context.Origin == input.Address, + "Lock behaviour should be initialed by origin address."); + var allowance = State.Allowances[input.Address][Context.Sender][input.Symbol]; if (allowance >= input.Amount) State.Allowances[input.Address][Context.Sender][input.Symbol] = allowance.Sub(input.Amount); @@ -174,8 +214,13 @@ public override Empty Lock(LockInput input) public override Empty Unlock(UnlockInput input) { + Assert(!string.IsNullOrWhiteSpace(input.Symbol), "Invalid input symbol."); + AssertValidInputAddress(input.Address); AssertSystemContractOrLockWhiteListAddress(input.Symbol); - Assert(Context.Origin == input.Address, "Unlock behaviour should be initialed by origin address."); + + Assert(IsInLockWhiteList(Context.Sender) || Context.Origin == input.Address, + "Unlock behaviour should be initialed by origin address."); + AssertValidToken(input.Symbol, input.Amount); var fromVirtualAddress = HashHelper.ComputeFrom(Context.Sender.Value.Concat(input.Address.Value) .Concat(input.LockId.Value).ToArray()); @@ -206,6 +251,7 @@ public override Empty TransferFrom(TransferFromInput input) public override Empty Approve(ApproveInput input) { + AssertValidInputAddress(input.Spender); AssertValidToken(input.Symbol, input.Amount); State.Allowances[Context.Sender][input.Spender][input.Symbol] = input.Amount; Context.Fire(new Approved @@ -220,6 +266,7 @@ public override Empty Approve(ApproveInput input) public override Empty UnApprove(UnApproveInput input) { + AssertValidInputAddress(input.Spender); AssertValidToken(input.Symbol, input.Amount); var oldAllowance = State.Allowances[Context.Sender][input.Spender][input.Symbol]; var amountOrAll = Math.Min(input.Amount, oldAllowance); @@ -236,22 +283,28 @@ public override Empty UnApprove(UnApproveInput input) public override Empty Burn(BurnInput input) { - var tokenInfo = AssertValidToken(input.Symbol, input.Amount); + return Burn(Context.Sender, input.Symbol, input.Amount); + } + + private Empty Burn(Address address, string symbol, long amount) + { + var tokenInfo = AssertValidToken(symbol, amount); Assert(tokenInfo.IsBurnable, "The token is not burnable."); - ModifyBalance(Context.Sender, input.Symbol, -input.Amount); - tokenInfo.Supply = tokenInfo.Supply.Sub(input.Amount); + ModifyBalance(address, symbol, -amount); + tokenInfo.Supply = tokenInfo.Supply.Sub(amount); Context.Fire(new Burned { - Burner = Context.Sender, - Symbol = input.Symbol, - Amount = input.Amount + Burner = address, + Symbol = symbol, + Amount = amount }); return new Empty(); } public override Empty CheckThreshold(CheckThresholdInput input) { + AssertValidInputAddress(input.Sender); var meetThreshold = false; var meetBalanceSymbolList = new List(); foreach (var symbolToThreshold in input.SymbolToThreshold) @@ -323,6 +376,7 @@ public override Empty TransferToContract(TransferToContractInput input) public override Empty AdvanceResourceToken(AdvanceResourceTokenInput input) { + AssertValidInputAddress(input.ContractAddress); Assert( Context.Variables.GetStringArray(TokenContractConstants.PayTxFeeSymbolListName) .Contains(input.ResourceTokenSymbol), @@ -336,6 +390,8 @@ public override Empty AdvanceResourceToken(AdvanceResourceTokenInput input) public override Empty TakeResourceTokenBack(TakeResourceTokenBackInput input) { + Assert(!string.IsNullOrWhiteSpace(input.ResourceTokenSymbol), "Invalid input resource token symbol."); + AssertValidInputAddress(input.ContractAddress); var advancedAmount = State.AdvancedResourceToken[input.ContractAddress][Context.Sender][input.ResourceTokenSymbol]; Assert(advancedAmount >= input.Amount, "Can't take back that more."); @@ -347,13 +403,14 @@ public override Empty TakeResourceTokenBack(TakeResourceTokenBackInput input) public override Empty ValidateTokenInfoExists(ValidateTokenInfoExistsInput input) { + Assert(!string.IsNullOrWhiteSpace(input.Symbol), "Invalid input symbol."); var tokenInfo = State.TokenInfos[input.Symbol]; if (tokenInfo == null) throw new AssertionException("Token validation failed."); var validationResult = tokenInfo.TokenName == input.TokenName && tokenInfo.IsBurnable == input.IsBurnable && tokenInfo.Decimals == input.Decimals && tokenInfo.Issuer == input.Issuer && tokenInfo.TotalSupply == input.TotalSupply && - tokenInfo.IssueChainId == input.IssueChainId; + tokenInfo.IssueChainId == input.IssueChainId && tokenInfo.Owner == input.Owner; if (tokenInfo.ExternalInfo != null && tokenInfo.ExternalInfo.Value.Count > 0 || input.ExternalInfo != null && input.ExternalInfo.Count > 0) @@ -368,32 +425,6 @@ public override Empty ValidateTokenInfoExists(ValidateTokenInfoExistsInput input return new Empty(); } - public override Empty ChangeTokenIssuer(ChangeTokenIssuerInput input) - { - var tokenInfo = State.TokenInfos[input.Symbol]; - Assert(tokenInfo != null, $"invalid token symbol: {input.Symbol}"); - // ReSharper disable once PossibleNullReferenceException - Assert(tokenInfo.Issuer == Context.Sender && tokenInfo.IssueChainId == Context.ChainId, - "Permission denied"); - tokenInfo.Issuer = input.NewTokenIssuer; - State.TokenInfos[input.Symbol] = tokenInfo; - return new Empty(); - } - - public override Empty ResetExternalInfo(ResetExternalInfoInput input) - { - var tokenInfo = State.TokenInfos[input.Symbol]; - Assert(tokenInfo.Issuer == Context.Sender, "No permission to reset external info."); - tokenInfo.ExternalInfo = input.ExternalInfo; - State.TokenInfos[input.Symbol] = tokenInfo; - Context.Fire(new ExternalInfoChanged - { - Symbol = input.Symbol, - ExternalInfo = input.ExternalInfo - }); - return new Empty(); - } - public override Empty AddAddressToCreateTokenWhiteList(Address input) { AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); @@ -433,7 +464,8 @@ public override Empty CrossChainCreateToken(CrossChainCreateTokenInput input) Issuer = validateTokenInfoExistsInput.Issuer, IsBurnable = validateTokenInfoExistsInput.IsBurnable, IssueChainId = validateTokenInfoExistsInput.IssueChainId, - ExternalInfo = new ExternalInfo { Value = { validateTokenInfoExistsInput.ExternalInfo } } + ExternalInfo = new ExternalInfo { Value = { validateTokenInfoExistsInput.ExternalInfo } }, + Owner = validateTokenInfoExistsInput.Owner ?? validateTokenInfoExistsInput.Issuer }; RegisterTokenInfo(tokenInfo); @@ -446,7 +478,8 @@ public override Empty CrossChainCreateToken(CrossChainCreateTokenInput input) Issuer = validateTokenInfoExistsInput.Issuer, IsBurnable = validateTokenInfoExistsInput.IsBurnable, IssueChainId = validateTokenInfoExistsInput.IssueChainId, - ExternalInfo = new ExternalInfo { Value = { validateTokenInfoExistsInput.ExternalInfo } } + ExternalInfo = new ExternalInfo { Value = { validateTokenInfoExistsInput.ExternalInfo } }, + Owner = tokenInfo.Owner }); return new Empty(); diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Delegation.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Delegation.cs index b38acad74a..1ac302ccef 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Delegation.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Delegation.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Linq; using AElf.Sdk.CSharp; using AElf.Types; @@ -11,6 +12,7 @@ public partial class TokenContract public override SetTransactionFeeDelegationsOutput SetTransactionFeeDelegations( SetTransactionFeeDelegationsInput input) { + AssertValidInputAddress(input.DelegatorAddress); Assert(input.Delegations != null, "Delegations cannot be null!"); // get all delegatees, init it if null. @@ -192,4 +194,240 @@ public override GetTransactionFeeDelegateesOutput GetTransactionFeeDelegatees( DelegateeAddresses = { allDelegatees.Delegatees.Keys.Select(Address.FromBase58) } }; } + + public override Empty SetTransactionFeeDelegateInfos(SetTransactionFeeDelegateInfosInput input) + { + Assert(input.DelegatorAddress != null && input.DelegateInfoList.Count > 0, + "Delegator address and delegate info cannot be null."); + var toAddTransactionList = new DelegateTransactionList(); + var toUpdateTransactionList = new DelegateTransactionList(); + var toCancelTransactionList = new DelegateTransactionList(); + var delegatorAddress = input.DelegatorAddress; + foreach (var delegateInfo in input.DelegateInfoList) + { + //If isUnlimitedDelegate is false,delegate info list should > 0. + Assert(delegateInfo.IsUnlimitedDelegate || delegateInfo.Delegations.Count > 0, + "Delegation cannot be null."); + Assert(delegateInfo.ContractAddress != null && !string.IsNullOrEmpty(delegateInfo.MethodName), + "Invalid contract address and method name."); + + var existDelegateeInfoList = + State.TransactionFeeDelegateInfoMap[delegatorAddress][delegateInfo.ContractAddress] + [delegateInfo.MethodName] ?? new TransactionFeeDelegatees(); + var delegateeAddress = Context.Sender.ToBase58(); + var existDelegateeList = existDelegateeInfoList.Delegatees; + //If the transaction contains delegatee,update delegate info. + if (existDelegateeList.TryGetValue(delegateeAddress, out var value)) + { + toUpdateTransactionList.Value.Add(UpdateDelegateInfo(value, delegateInfo)); + } //else,add new delegate info. + else + { + Assert(existDelegateeList.Count < TokenContractConstants.DELEGATEE_MAX_COUNT, + "The quantity of delegatee has reached its limit"); + existDelegateeList.Add(delegateeAddress, new TransactionFeeDelegations()); + var transactionFeeDelegations = existDelegateeList[delegateeAddress]; + toAddTransactionList.Value.Add(AddDelegateInfo(transactionFeeDelegations, delegateInfo)); + } + + if (existDelegateeInfoList.Delegatees[delegateeAddress].Delegations.Count == 0 && + !existDelegateeInfoList.Delegatees[delegateeAddress].IsUnlimitedDelegate) + { + existDelegateeInfoList.Delegatees.Remove(delegateeAddress); + toCancelTransactionList.Value.Add(new DelegateTransaction + { + ContractAddress = delegateInfo.ContractAddress, + MethodName = delegateInfo.MethodName + }); + } + + State.TransactionFeeDelegateInfoMap[delegatorAddress][delegateInfo.ContractAddress] + [delegateInfo.MethodName] = existDelegateeInfoList; + } + + FireLogEvent(toAddTransactionList, toUpdateTransactionList, toCancelTransactionList, delegatorAddress); + + return new Empty(); + } + + private DelegateTransaction AddDelegateInfo(TransactionFeeDelegations existDelegateeList, DelegateInfo delegateInfo) + { + if (!delegateInfo.IsUnlimitedDelegate) + { + foreach (var (symbol, amount) in delegateInfo.Delegations) + { + AssertValidToken(symbol, amount); + existDelegateeList.Delegations[symbol] = amount; + } + } + existDelegateeList.BlockHeight = Context.CurrentHeight; + existDelegateeList.IsUnlimitedDelegate = delegateInfo.IsUnlimitedDelegate; + return new DelegateTransaction + { + ContractAddress = delegateInfo.ContractAddress, + MethodName = delegateInfo.MethodName + }; + } + + private DelegateTransaction UpdateDelegateInfo(TransactionFeeDelegations existDelegateInfo, DelegateInfo delegateInfo) + { + var existDelegation = existDelegateInfo.Delegations; + if (delegateInfo.IsUnlimitedDelegate) + { + existDelegation.Clear(); + } + else + { + var delegation = delegateInfo.Delegations; + foreach (var (symbol, amount) in delegation) + { + if (existDelegation.ContainsKey(symbol)) + { + if (amount <= 0) + { + existDelegation.Remove(symbol); + } + else + { + AssertValidToken(symbol, amount); + existDelegation[symbol] = amount; + } + } + else + { + AssertValidToken(symbol, amount); + existDelegation[symbol] = amount; + } + } + } + + existDelegateInfo.BlockHeight = Context.CurrentHeight; + existDelegateInfo.IsUnlimitedDelegate = delegateInfo.IsUnlimitedDelegate; + return new DelegateTransaction + { + ContractAddress = delegateInfo.ContractAddress, + MethodName = delegateInfo.MethodName + }; + } + + private void FireLogEvent(DelegateTransactionList toAddTransactionList, + DelegateTransactionList toUpdateTransactionList, DelegateTransactionList toCancelTransactionList, + Address delegatorAddress) + { + if (toAddTransactionList.Value.Count > 0) + { + Context.Fire(new TransactionFeeDelegateInfoAdded + { + Caller = Context.Sender, + Delegatee = Context.Sender, + Delegator = delegatorAddress, + DelegateTransactionList = toAddTransactionList + }); + } + + if (toUpdateTransactionList.Value.Count > 0) + { + Context.Fire(new TransactionFeeDelegateInfoUpdated + { + Caller = Context.Sender, + Delegatee = Context.Sender, + Delegator = delegatorAddress, + DelegateTransactionList = toUpdateTransactionList + }); + } + + if (toCancelTransactionList.Value.Count > 0) + { + Context.Fire(new TransactionFeeDelegateInfoCancelled + { + Caller = Context.Sender, + Delegatee = Context.Sender, + Delegator = delegatorAddress, + DelegateTransactionList = toCancelTransactionList + }); + } + } + + public override Empty RemoveTransactionFeeDelegateeInfos(RemoveTransactionFeeDelegateeInfosInput input) + { + Assert(input.DelegateeAddress != null, "Delegatee address cannot be null."); + Assert(input.DelegateTransactionList.Count > 0, "Delegate transaction list should not be null."); + var delegatorAddress = Context.Sender; + var delegateeAddress = input.DelegateeAddress?.ToBase58(); + RemoveTransactionFeeDelegateInfo(input.DelegateTransactionList.ToList(), delegatorAddress, delegateeAddress); + return new Empty(); + } + + public override Empty RemoveTransactionFeeDelegatorInfos(RemoveTransactionFeeDelegatorInfosInput input) + { + Assert(input.DelegatorAddress != null, "Delegator address cannot be null."); + Assert(input.DelegateTransactionList.Count > 0, "Delegate transaction list should not be null."); + var delegateeAddress = Context.Sender.ToBase58(); + var delegatorAddress = input.DelegatorAddress; + RemoveTransactionFeeDelegateInfo(input.DelegateTransactionList.ToList(), delegatorAddress, delegateeAddress); + return new Empty(); + } + + private void RemoveTransactionFeeDelegateInfo(List delegateTransactionList,Address delegatorAddress,string delegateeAddress) + { + var toCancelTransactionList = new DelegateTransactionList(); + foreach (var delegateTransaction in delegateTransactionList.Distinct()) + { + Assert(delegateTransaction.ContractAddress != null && !string.IsNullOrEmpty(delegateTransaction.MethodName), + "Invalid contract address and method name."); + + var delegateeInfo = + State.TransactionFeeDelegateInfoMap[delegatorAddress][delegateTransaction.ContractAddress][ + delegateTransaction.MethodName]; + if (delegateeInfo == null || !delegateeInfo.Delegatees.ContainsKey(delegateeAddress)) continue; + delegateeInfo.Delegatees.Remove(delegateeAddress); + toCancelTransactionList.Value.Add(delegateTransaction); + State.TransactionFeeDelegateInfoMap[delegatorAddress][delegateTransaction.ContractAddress][ + delegateTransaction.MethodName] = delegateeInfo; + } + + if (toCancelTransactionList.Value.Count > 0) + { + Context.Fire(new TransactionFeeDelegateInfoCancelled + { + Caller = Context.Sender, + Delegator = delegatorAddress, + Delegatee = Address.FromBase58(delegateeAddress), + DelegateTransactionList = toCancelTransactionList + }); + } + } + + public override GetTransactionFeeDelegateeListOutput GetTransactionFeeDelegateeList( + GetTransactionFeeDelegateeListInput input) + { + Assert(input.DelegatorAddress != null && input.ContractAddress != null && input.MethodName != null, + "Invalid input."); + var allDelegatees = + State.TransactionFeeDelegateInfoMap[input.DelegatorAddress][input.ContractAddress][input.MethodName]; + + if (allDelegatees?.Delegatees == null || allDelegatees.Delegatees.Count == 0) + { + return new GetTransactionFeeDelegateeListOutput(); + } + + return new GetTransactionFeeDelegateeListOutput + { + DelegateeAddresses = { allDelegatees.Delegatees.Keys.Select(Address.FromBase58) } + }; + } + + public override TransactionFeeDelegations GetTransactionFeeDelegateInfo( + GetTransactionFeeDelegateInfoInput input) + { + var allDelegatees = + State.TransactionFeeDelegateInfoMap[input.DelegatorAddress][input.ContractAddress][input.MethodName]; + var delegateeAddress = input.DelegateeAddress.ToBase58(); + // According to protoBuf, return an empty object, but null. + if (allDelegatees == null) return new TransactionFeeDelegations(); + + var allDelegateesMap = allDelegatees.Delegatees; + var result = allDelegateesMap.TryGetValue(delegateeAddress, out var value); + return result ? value : new TransactionFeeDelegations(); + } } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Fee_Calculate_Coefficient.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Fee_Calculate_Coefficient.cs index 8efefd5df0..09e4ebb328 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Fee_Calculate_Coefficient.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Fee_Calculate_Coefficient.cs @@ -15,6 +15,7 @@ public partial class TokenContract /// public override Empty UpdateCoefficientsForContract(UpdateCoefficientsInput input) { + Assert(input.Coefficients != null, "Invalid input coefficients."); Assert(input.Coefficients.FeeTokenType != (int)FeeTypeEnum.Tx, "Invalid fee type."); AssertDeveloperFeeController(); UpdateCoefficients(input); @@ -23,6 +24,7 @@ public override Empty UpdateCoefficientsForContract(UpdateCoefficientsInput inpu public override Empty UpdateCoefficientsForSender(UpdateCoefficientsInput input) { + Assert(input.Coefficients != null, "Invalid input coefficients."); AssertUserFeeController(); input.Coefficients.FeeTokenType = (int)FeeTypeEnum.Tx; // The only possible for now. UpdateCoefficients(input); diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Fees.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Fees.cs index da35910cb8..25f853ec11 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Fees.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Fees.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using AElf.CSharp.Core; @@ -7,6 +8,7 @@ using AElf.Standards.ACS12; using AElf.Types; using Google.Protobuf; +using Google.Protobuf.Collections; using Google.Protobuf.WellKnownTypes; namespace AElf.Contracts.MultiToken; @@ -21,11 +23,12 @@ public partial class TokenContract /// public override ChargeTransactionFeesOutput ChargeTransactionFees(ChargeTransactionFeesInput input) { + Context.LogDebug(() => "ChargeTransactionFees Start"); AssertPermissionAndInput(input); // Primary token not created yet. if (State.ChainPrimaryTokenSymbol.Value == null) { - return new ChargeTransactionFeesOutput {Success = true}; + return new ChargeTransactionFeesOutput { Success = true }; } // Record tx fee bill during current charging process. @@ -33,7 +36,7 @@ public override ChargeTransactionFeesOutput ChargeTransactionFees(ChargeTransact var allowanceBill = new TransactionFreeFeeAllowanceBill(); var fromAddress = Context.Sender; var methodFees = Context.Call(input.ContractAddress, nameof(GetMethodFee), - new StringValue {Value = input.MethodName}); + new StringValue { Value = input.MethodName }); var fee = new Dictionary(); var isSizeFeeFree = false; if (methodFees != null) @@ -55,7 +58,7 @@ public override ChargeTransactionFeesOutput ChargeUserContractTransactionFees(Ch // Primary token not created yet. if (State.ChainPrimaryTokenSymbol.Value == null) { - return new ChargeTransactionFeesOutput {Success = true}; + return new ChargeTransactionFeesOutput { Success = true }; } // Record tx fee bill during current charging process. @@ -82,18 +85,43 @@ private ChargeTransactionFeesOutput TryToChargeTransactionFee(ChargeTransactionF TransactionFeeBill bill, TransactionFreeFeeAllowanceBill allowanceBill, Dictionary fee, bool isSizeFeeFree) { + Context.LogDebug(() => "TryToChargeTransactionFee Start"); var chargingResult = ChargeTransactionFeesToBill(input, fromAddress, ref bill, ref allowanceBill, fee, isSizeFeeFree); if (!chargingResult) { + var delegatorAddress = fromAddress; chargingResult = ChargeFromDelegations(input, ref fromAddress, ref bill, ref allowanceBill, fee, - isSizeFeeFree); + isSizeFeeFree, delegatorAddress); + } + + if (!chargingResult) + { + var transactionFeeDelegatees = + State.TransactionFeeDelegateInfoMap[fromAddress][input.ContractAddress][input.MethodName] ?? + State.TransactionFeeDelegateesMap[fromAddress]; + if (transactionFeeDelegatees != null) + { + var delegateeAddress = transactionFeeDelegatees.Delegatees; + foreach (var (delegatee, _) in delegateeAddress) + { + chargingResult = ChargeFromDelegations(input, ref fromAddress, ref bill, ref allowanceBill, fee, + isSizeFeeFree, Address.FromBase58(delegatee)); + if (chargingResult) + { + break; + } + } + } } ModifyBalance(fromAddress, bill, allowanceBill); - var chargingOutput = new ChargeTransactionFeesOutput {Success = chargingResult}; + var chargingOutput = new ChargeTransactionFeesOutput { Success = chargingResult }; if (!chargingResult) chargingOutput.ChargingInformation = "Transaction fee not enough."; + + Context.LogDebug(() => "TryToChargeTransactionFee End"); + Context.LogDebug(() => "ChargeTransactionFees End"); return chargingOutput; } @@ -120,6 +148,7 @@ private UserContractMethodFees GetActualFee(Address contractAddress, string meth fee.MergeFrom(spec.Value); return fee; } + //If special key is null,get the normal fee set by the configuration contract. //configuration_key:UserContractMethod var value = State.ConfigurationContract.GetConfiguration.Call(new StringValue @@ -130,18 +159,28 @@ private UserContractMethodFees GetActualFee(Address contractAddress, string meth { return new UserContractMethodFees(); } + fee.MergeFrom(value.Value); return fee; } private bool ChargeFromDelegations(ChargeTransactionFeesInput input, ref Address fromAddress, ref TransactionFeeBill bill, ref TransactionFreeFeeAllowanceBill allowanceBill, - Dictionary fee, bool isSizeFeeFree) + Dictionary fee, bool isSizeFeeFree, Address delegatorAddress) { var chargingResult = false; // Try to charge delegatees - if (State.TransactionFeeDelegateesMap[fromAddress]?.Delegatees == null) return false; - foreach (var (delegatee, delegations) in State.TransactionFeeDelegateesMap[fromAddress].Delegatees) + // Get delegatee list according to the delegator + var delegationInfo = + State.TransactionFeeDelegateInfoMap[delegatorAddress][input.ContractAddress][input.MethodName]?.Delegatees ?? + State.TransactionFeeDelegateesMap[delegatorAddress]?.Delegatees; + + if (delegationInfo == null) + { + return false; + } + + foreach (var (delegatee, delegations) in delegationInfo) { // compare current block height with the block height when the delegatee added if (Context.Transaction.RefBlockNumber < delegations.BlockHeight) continue; @@ -158,45 +197,48 @@ private bool ChargeFromDelegations(ChargeTransactionFeesInput input, ref Address allowanceBill = delegateeAllowanceBill; fromAddress = delegateeAddress; chargingResult = true; - ModifyDelegation(delegateeBill, delegateeAllowanceBill, fromAddress); + if (!delegations.IsUnlimitedDelegate) + { + ModifyDelegation(delegateeBill, delegateeAllowanceBill, fromAddress, input.ContractAddress, + input.MethodName, delegatorAddress); + } + break; } + return chargingResult; } private void ModifyDelegation(TransactionFeeBill bill, TransactionFreeFeeAllowanceBill allowanceBill, - Address delegateeAddress) + Address delegateeAddress, Address contractAddress, string methodName, Address delegatorAddress) { foreach (var (symbol, amount) in bill.FeesMap) { - if (amount > 0) - { - State.TransactionFeeDelegateesMap[Context.Sender].Delegatees[delegateeAddress.ToBase58()] - .Delegations[symbol] = - State.TransactionFeeDelegateesMap[Context.Sender].Delegatees[delegateeAddress.ToBase58()] - .Delegations[symbol] - .Sub(amount); - } + if (amount <= 0) continue; + var delegateInfo = + State.TransactionFeeDelegateInfoMap[delegatorAddress][contractAddress][methodName] ?? + State.TransactionFeeDelegateesMap[delegatorAddress]; + delegateInfo.Delegatees[delegateeAddress.ToBase58()].Delegations[symbol] = + delegateInfo.Delegatees[delegateeAddress.ToBase58()].Delegations[symbol].Sub(amount); } foreach (var (symbol, amount) in allowanceBill.FreeFeeAllowancesMap) { - if (amount > 0) - { - State.TransactionFeeDelegateesMap[Context.Sender].Delegatees[delegateeAddress.ToBase58()] - .Delegations[symbol] = - State.TransactionFeeDelegateesMap[Context.Sender].Delegatees[delegateeAddress.ToBase58()] - .Delegations[symbol] - .Sub(amount); - } + if (amount <= 0) continue; + + var delegateInfo = + State.TransactionFeeDelegateInfoMap[delegatorAddress][contractAddress][methodName] ?? + State.TransactionFeeDelegateesMap[delegatorAddress]; + delegateInfo.Delegatees[delegateeAddress.ToBase58()].Delegations[symbol] = + delegateInfo.Delegatees[delegateeAddress.ToBase58()].Delegations[symbol].Sub(amount); } } private void ModifyBalance(Address fromAddress, TransactionFeeBill bill, TransactionFreeFeeAllowanceBill allowanceBill) { - SetOrRefreshMethodFeeFreeAllowances(fromAddress); - var freeAllowances = CalculateMethodFeeFreeAllowances(fromAddress)?.Clone(); + SetOrRefreshTransactionFeeFreeAllowances(fromAddress); + var freeAllowancesMap = CalculateTransactionFeeFreeAllowances(fromAddress); // Update balances and allowances foreach (var (symbol, amount) in bill.FeesMap) @@ -211,15 +253,15 @@ private void ModifyBalance(Address fromAddress, TransactionFeeBill bill, }); } + if (freeAllowancesMap.Map == null || freeAllowancesMap.Map.Count == 0) return; + foreach (var (symbol, amount) in allowanceBill.FreeFeeAllowancesMap) { if (amount > 0) { - ModifyFreeFeeAllowanceAmount(freeAllowances, symbol, -amount); + ModifyFreeFeeAllowanceAmount(fromAddress, freeAllowancesMap, symbol, -amount); } } - - State.MethodFeeFreeAllowancesMap[fromAddress] = freeAllowances; } private bool ChargeTransactionFeesToBill(ChargeTransactionFeesInput input, Address fromAddress, @@ -229,14 +271,20 @@ private bool ChargeTransactionFeesToBill(ChargeTransactionFeesInput input, Addre { var successToChargeBaseFee = true; - SetOrRefreshMethodFeeFreeAllowances(fromAddress); - var freeAllowances = CalculateMethodFeeFreeAllowances(fromAddress)?.Clone(); + SetOrRefreshTransactionFeeFreeAllowances(fromAddress); + var freeAllowancesMap = CalculateTransactionFeeFreeAllowances(fromAddress); if (fee.Count != 0) { // If base fee is set before, charge base fee. successToChargeBaseFee = - ChargeBaseFee(fee, fromAddress, ref bill, freeAllowances, ref allowanceBill, delegations); + ChargeBaseFee(fee, fromAddress, ref bill, freeAllowancesMap, ref allowanceBill, delegations); + } + + //For delegation, if the base fee fails to be charged, the size fee will not be charged + if (delegations != null && !successToChargeBaseFee) + { + return false; } var successToChargeSizeFee = true; @@ -244,28 +292,30 @@ private bool ChargeTransactionFeesToBill(ChargeTransactionFeesInput input, Addre { // If IsSizeFeeFree == true, do not charge size fee. successToChargeSizeFee = - ChargeSizeFee(input, fromAddress, ref bill, freeAllowances, ref allowanceBill, delegations); + ChargeSizeFee(input, fromAddress, ref bill, freeAllowancesMap, ref allowanceBill, delegations); } return successToChargeBaseFee && successToChargeSizeFee; } - private void SetOrRefreshMethodFeeFreeAllowances(Address address) + private void SetOrRefreshTransactionFeeFreeAllowances(Address address) { - var config = State.MethodFeeFreeAllowancesConfig.Value; - if (config == null || State.Balances[address][Context.Variables.NativeSymbol] < config.Threshold) - { - return; - } + var config = State.TransactionFeeFreeAllowancesSymbolList.Value; + if (config == null) return; - var lastRefreshTime = State.MethodFeeFreeAllowancesLastRefreshTimeMap[address]; - if (lastRefreshTime != null && config.RefreshSeconds > (Context.CurrentBlockTime - lastRefreshTime).Seconds) + foreach (var symbol in config.Symbols) { - return; - } + if (State.Balances[address][symbol] < + State.TransactionFeeFreeAllowancesConfigMap[symbol].Threshold) continue; + var lastRefreshTime = State.TransactionFeeFreeAllowancesLastRefreshTimes[address][symbol]; + + if (lastRefreshTime != null && State.TransactionFeeFreeAllowancesConfigMap[symbol].RefreshSeconds > + (Context.CurrentBlockTime - lastRefreshTime).Seconds) continue; - State.MethodFeeFreeAllowancesLastRefreshTimeMap[address] = Context.CurrentBlockTime; - State.MethodFeeFreeAllowancesMap[address] = config.FreeAllowances; + State.TransactionFeeFreeAllowancesLastRefreshTimes[address][symbol] = Context.CurrentBlockTime; + State.TransactionFeeFreeAllowances[address][symbol] = + State.TransactionFeeFreeAllowancesConfigMap[symbol].FreeAllowances.Clone(); + } } private Dictionary GetBaseFeeDictionary(MethodFees methodFees) @@ -283,12 +333,15 @@ private Dictionary GetUserContractFeeDictionary(UserContractMethod } private bool ChargeBaseFee(Dictionary methodFeeMap, Address fromAddress, ref TransactionFeeBill bill, - MethodFeeFreeAllowances freeAllowances, ref TransactionFreeFeeAllowanceBill allowanceBill, + TransactionFeeFreeAllowancesMap transactionFeeFreeAllowancesMap, + ref TransactionFreeFeeAllowanceBill allowanceBill, TransactionFeeDelegations delegations = null) { + Context.LogDebug(() => "ChargeBaseFee Start"); // Fail to charge if (!ChargeFirstSufficientToken(methodFeeMap, fromAddress, out var symbolToChargeBaseFee, - out var amountToChargeBaseFee, out var existingBalance, out var existingAllowance, freeAllowances, + out var amountToChargeBaseFee, out var existingBalance, out var existingAllowance, + transactionFeeFreeAllowancesMap, delegations)) { Context.LogDebug(() => "Failed to charge first sufficient token."); @@ -314,18 +367,80 @@ private bool ChargeBaseFee(Dictionary methodFeeMap, Address fromAd bill.FeesMap.Add(symbolToChargeBaseFee, amountToChargeBaseFee.Sub(existingAllowance)); } + Context.LogDebug(() => "ChargeBaseFee End"); return true; } private bool ChargeSizeFee(ChargeTransactionFeesInput input, Address fromAddress, ref TransactionFeeBill bill, - MethodFeeFreeAllowances freeAllowances, ref TransactionFreeFeeAllowanceBill allowanceBill, + TransactionFeeFreeAllowancesMap transactionFeeFreeAllowancesMap, + ref TransactionFreeFeeAllowanceBill allowanceBill, TransactionFeeDelegations delegations = null) { - string symbolChargedForBaseFee = null; - var amountChargedForBaseFee = 0L; - var amountChargedForBaseAllowance = 0L; - // Size Fee is charged in primary token, aelf. + + Context.LogDebug(() => "ChargeSizeFee Start"); + + //If delegation != null,from address->delegateeAddress + // Size Fee is charged in primary token, elf. var symbolToPayTxFee = State.ChainPrimaryTokenSymbol.Value; + //Get primary token balance + GetAvailableBalance(symbolToPayTxFee, fromAddress, bill, transactionFeeFreeAllowancesMap, allowanceBill, + out var symbolChargedForBaseFee, out var amountChargedForBaseFee, out var amountChargedForBaseAllowance, + out var availableBalance, out var availableAllowance); + var txSizeFeeAmount = input.TransactionSizeFee; + + // SymbolsToPayTxSizeFee is set of all available token can be charged, and with the ratio of primary token and another. + if (input.SymbolsToPayTxSizeFee.Any()) + { + var allSymbolToTxFee = input.SymbolsToPayTxSizeFee.ToList(); + var availableSymbol = GetAvailableSymbolToPayTxFee(allSymbolToTxFee, fromAddress, txSizeFeeAmount, + transactionFeeFreeAllowancesMap, symbolChargedForBaseFee, amountChargedForBaseFee, + amountChargedForBaseAllowance, delegations); + + if (availableSymbol != null && availableSymbol.TokenSymbol != symbolToPayTxFee) + { + symbolToPayTxFee = availableSymbol.TokenSymbol; + txSizeFeeAmount = txSizeFeeAmount.Mul(availableSymbol.AddedTokenWeight) + .Div(availableSymbol.BaseTokenWeight); + GetAvailableBalance(symbolToPayTxFee, fromAddress, bill, transactionFeeFreeAllowancesMap, allowanceBill, + out symbolChargedForBaseFee, out amountChargedForBaseFee, out amountChargedForBaseAllowance, + out availableBalance, out availableAllowance); + } + + //For delegation,if there is no available token,return false,no need to generate bill + if (delegations != null && availableSymbol == null) + { + return false; + } + } + + var chargeResult = availableBalance.Add(availableAllowance) >= txSizeFeeAmount; + if (delegations != null) + { + chargeResult = chargeResult && IsDelegationEnough(symbolToPayTxFee, symbolChargedForBaseFee, + amountChargedForBaseFee.Add(amountChargedForBaseAllowance), txSizeFeeAmount, delegations); + if (!chargeResult) + { + return false; + } + } + + GenerateBill(txSizeFeeAmount, symbolToPayTxFee, symbolChargedForBaseFee, availableBalance, availableAllowance, + ref bill, ref allowanceBill); + + Context.LogDebug(() => "ChargeSizeFee End"); + + return chargeResult; + } + + private void GetAvailableBalance(string symbolToPayTxFee, Address fromAddress, TransactionFeeBill bill, + TransactionFeeFreeAllowancesMap transactionFeeFreeAllowancesMap, TransactionFreeFeeAllowanceBill allowanceBill, + out string symbolChargedForBaseFee, out long amountChargedForBaseFee, out long amountChargedForBaseAllowance, + out long availableBalance, out long availableAllowance) + { + symbolChargedForBaseFee = null; + amountChargedForBaseFee = 0L; + amountChargedForBaseAllowance = 0L; + if (bill.FeesMap.Any()) { symbolChargedForBaseFee = bill.FeesMap.First().Key; @@ -333,92 +448,98 @@ private bool ChargeSizeFee(ChargeTransactionFeesInput input, Address fromAddress amountChargedForBaseAllowance = allowanceBill.FreeFeeAllowancesMap[symbolChargedForBaseFee]; } - var availableBalance = symbolChargedForBaseFee == symbolToPayTxFee + availableBalance = symbolChargedForBaseFee == symbolToPayTxFee // Available balance need to deduct amountChargedForBaseFee, if base fee is charged in the same token. ? GetBalance(fromAddress, symbolToPayTxFee).Sub(amountChargedForBaseFee) : GetBalance(fromAddress, symbolToPayTxFee); - var availableAllowance = symbolChargedForBaseFee == symbolToPayTxFee - ? GetFreeFeeAllowanceAmount(freeAllowances, symbolToPayTxFee).Sub(amountChargedForBaseAllowance) - : GetFreeFeeAllowanceAmount(freeAllowances, symbolToPayTxFee); - var txSizeFeeAmount = input.TransactionSizeFee; + availableAllowance = symbolChargedForBaseFee == symbolToPayTxFee + ? GetFreeFeeAllowanceAmount(transactionFeeFreeAllowancesMap, symbolToPayTxFee) + .Sub(amountChargedForBaseAllowance) + : GetFreeFeeAllowanceAmount(transactionFeeFreeAllowancesMap, symbolToPayTxFee); + } + private SymbolToPayTxSizeFee GetAvailableSymbolToPayTxFee(List allSymbolToTxFee, + Address fromAddress, long txSizeFeeAmount, TransactionFeeFreeAllowancesMap transactionFeeFreeAllowancesMap, + string symbolChargedForBaseFee, long amountChargedForBaseFee, long amountChargedForBaseAllowance, + TransactionFeeDelegations delegations = null) + { + SymbolToPayTxSizeFee availableSymbol = null; + SymbolToPayTxSizeFee availableSymbolWithAnything = null; + SymbolToPayTxSizeFee availableSymbolWithEnoughBalance = null; + SymbolToPayTxSizeFee availableSymbolWithEnoughBalancePlusAllowance = null; - // SymbolsToPayTxSizeFee is set of all available token can be charged, and with the ratio of primary token and another. - if (input.SymbolsToPayTxSizeFee.Any()) + // get 1st Allowance > size fee, else, get 1st Balance + Allowance > 0, else get 1st > 0 + foreach (var symbolToPlayTxSizeFee in allSymbolToTxFee) { - var allSymbolToTxFee = input.SymbolsToPayTxSizeFee; - // get 1st Balance + Allowance > size fee, else, get 1st > 0 - var availableSymbol = allSymbolToTxFee.FirstOrDefault(x => - GetBalancePlusAllowanceCalculatedBaseOnPrimaryToken(fromAddress, x, symbolChargedForBaseFee, - amountChargedForBaseFee, freeAllowances, amountChargedForBaseAllowance) >= txSizeFeeAmount && - IsDelegationEnoughBaseOnPrimaryToken(x, symbolChargedForBaseFee, - amountChargedForBaseFee.Add(amountChargedForBaseAllowance), txSizeFeeAmount, delegations)); + if (delegations != null) + { + var delegationEnough = IsDelegationEnoughBaseOnPrimaryToken(symbolToPlayTxSizeFee, + symbolChargedForBaseFee, amountChargedForBaseFee.Add(amountChargedForBaseAllowance), + txSizeFeeAmount, delegations); + if (!delegationEnough) break; + } - if (delegations != null && availableSymbol == null) + var allowance = GetAllowanceCalculatedBaseOnPrimaryToken(symbolToPlayTxSizeFee, + transactionFeeFreeAllowancesMap, symbolChargedForBaseFee, amountChargedForBaseAllowance); + var balance = GetBalanceCalculatedBaseOnPrimaryToken(fromAddress, symbolToPlayTxSizeFee, + symbolChargedForBaseFee, amountChargedForBaseFee); + + var balancePlusAllowance = balance.Add(allowance); + + if (allowance >= txSizeFeeAmount) { - return false; + availableSymbol = symbolToPlayTxSizeFee; + break; } - if (delegations == null) + if (delegations == null && balancePlusAllowance > 0) { - availableSymbol ??= allSymbolToTxFee.FirstOrDefault(x => - GetBalancePlusAllowanceCalculatedBaseOnPrimaryToken(fromAddress, x, symbolChargedForBaseFee, - amountChargedForBaseFee, freeAllowances, amountChargedForBaseAllowance) > 0); + availableSymbolWithAnything ??= symbolToPlayTxSizeFee; } - if (availableSymbol != null && availableSymbol.TokenSymbol != symbolToPayTxFee) + if (balancePlusAllowance < txSizeFeeAmount) continue; + + if (allowance > 0) { - symbolToPayTxFee = availableSymbol.TokenSymbol; - txSizeFeeAmount = txSizeFeeAmount.Mul(availableSymbol.AddedTokenWeight) - .Div(availableSymbol.BaseTokenWeight); - availableBalance = symbolChargedForBaseFee == symbolToPayTxFee - ? GetBalance(fromAddress, symbolToPayTxFee).Sub(amountChargedForBaseFee) - : GetBalance(fromAddress, symbolToPayTxFee); - availableAllowance = symbolChargedForBaseFee == symbolToPayTxFee - ? GetFreeFeeAllowanceAmount(freeAllowances, symbolToPayTxFee).Sub(amountChargedForBaseAllowance) - : GetFreeFeeAllowanceAmount(freeAllowances, symbolToPayTxFee); + availableSymbolWithEnoughBalancePlusAllowance ??= symbolToPlayTxSizeFee; + } + else + { + availableSymbolWithEnoughBalance ??= symbolToPlayTxSizeFee; } } - // Default token is primary token, so if no token can be charged in input.SymbolsToPayTxSizeFee, primary token will be the last one. - // So, we have to take primary token delegation quota into account. + availableSymbol ??= availableSymbolWithEnoughBalancePlusAllowance ?? + availableSymbolWithEnoughBalance ?? availableSymbolWithAnything; + + return availableSymbol; + } - // start to charge + private void GenerateBill(long txSizeFeeAmount, string symbolToPayTxFee, string symbolChargedForBaseFee, + long availableBalance, long availableAllowance, ref TransactionFeeBill bill, + ref TransactionFreeFeeAllowanceBill allowanceBill) + { var chargeAmount = 0L; var chargeAllowanceAmount = 0L; - - if (delegations == null - || (delegations.Delegations.Keys.Contains(symbolToPayTxFee) - && (symbolChargedForBaseFee == symbolToPayTxFee - ? delegations.Delegations[symbolToPayTxFee] - .Sub(amountChargedForBaseFee.Add(amountChargedForBaseAllowance)) - : delegations.Delegations[symbolToPayTxFee]) >= txSizeFeeAmount)) + if (availableBalance.Add(availableAllowance) > txSizeFeeAmount) { - // Balance + Allowance > size fee - if (availableBalance.Add(availableAllowance) > txSizeFeeAmount) + // Allowance > size fee, all allowance + if (availableAllowance > txSizeFeeAmount) { - // Allowance > size fee, all allowance - if (availableAllowance > txSizeFeeAmount) - { - chargeAllowanceAmount = txSizeFeeAmount; - } - else - { - // Allowance is not enough - chargeAllowanceAmount = availableAllowance; - chargeAmount = txSizeFeeAmount.Sub(chargeAllowanceAmount); - } + chargeAllowanceAmount = txSizeFeeAmount; } else { + // Allowance is not enough chargeAllowanceAmount = availableAllowance; - chargeAmount = availableBalance; + chargeAmount = txSizeFeeAmount.Sub(chargeAllowanceAmount); } } - - // Warning, currently, if the delegation quato is not enough, we will not charge delegatee bill, so we don't calculate it now. - if (delegations == null && symbolToPayTxFee == null) - return availableBalance.Add(availableAllowance) >= txSizeFeeAmount; + else + { + chargeAllowanceAmount = availableAllowance; + chargeAmount = availableBalance; + } if (symbolChargedForBaseFee == symbolToPayTxFee) { @@ -439,20 +560,6 @@ private bool ChargeSizeFee(ChargeTransactionFeesInput input, Address fromAddress allowanceBill.FreeFeeAllowancesMap.Add(symbolToPayTxFee, chargeAllowanceAmount); } } - - if (delegations == null - || (delegations.Delegations.Keys.Contains(symbolToPayTxFee) - && (symbolChargedForBaseFee == symbolToPayTxFee - ? delegations.Delegations[symbolToPayTxFee] - .Sub(amountChargedForBaseFee.Add(amountChargedForBaseAllowance)) - : delegations.Delegations[symbolToPayTxFee]) >= txSizeFeeAmount)) - { - return availableBalance.Add(availableAllowance) >= txSizeFeeAmount; - } - else - { - return false; - } } public override Empty ChargeResourceToken(ChargeResourceTokenInput input) @@ -591,72 +698,170 @@ public override Empty SetSymbolsToPayTxSizeFee(SymbolListToPayTxSizeFee input) /// /// /// - /// + /// /// /// private bool ChargeFirstSufficientToken(Dictionary symbolToAmountMap, Address fromAddress, - out string symbol, - out long amount, out long existingBalance, out long existingAllowance, MethodFeeFreeAllowances freeAllowances, - TransactionFeeDelegations delegations = null) + out string symbol, out long amount, out long existingBalance, out long existingAllowance, + TransactionFeeFreeAllowancesMap transactionFeeFreeAllowancesMap, TransactionFeeDelegations delegations = null) { symbol = null; amount = 0L; existingBalance = 0L; existingAllowance = 0L; + bool chargeResult; + + if (delegations != null) + { + //from address -> delegatee + chargeResult = TryToChargeDelegateBaseFee(symbolToAmountMap, fromAddress, transactionFeeFreeAllowancesMap, + delegations, out amount, out symbol, out existingBalance, out existingAllowance); + return chargeResult; + } - //var fromAddress = Context.Sender; - string symbolOfValidBalance = null; + chargeResult = TryToChargeUserBaseFee(symbolToAmountMap, fromAddress, transactionFeeFreeAllowancesMap, + out amount, out symbol, out existingBalance, out existingAllowance); - // Traverse available token symbols, check balance one by one - // until there's balance of one certain token is enough to pay the fee. - foreach (var symbolToAmount in symbolToAmountMap) + if (symbol != null) + { + existingBalance = GetBalance(fromAddress, symbol); + existingAllowance = GetFreeFeeAllowanceAmount(transactionFeeFreeAllowancesMap, symbol); + amount = symbolToAmountMap[symbol]; + } + + //For user, if charge failed and delegation is null, priority charge primary token + if (!chargeResult) + { + var primaryTokenSymbol = GetPrimaryTokenSymbol(new Empty()).Value; + if (symbolToAmountMap.ContainsKey(primaryTokenSymbol)) + { + symbol = primaryTokenSymbol; + existingBalance = GetBalance(fromAddress, symbol); + existingAllowance = GetFreeFeeAllowanceAmount(transactionFeeFreeAllowancesMap, symbol); + } + } + + return chargeResult; + } + + private bool TryToChargeUserBaseFee(Dictionary symbolToAmountMap, Address fromAddress, + TransactionFeeFreeAllowancesMap transactionFeeFreeAllowancesMap, out long amount, + out string symbolOfValidBalance, out long existingBalance, out long existingAllowance) + { + // priority: enough allowance -> symbolWithEnoughBalancePlusAllowance -> symbolWithEnoughBalance -> symbolWithAnything + symbolOfValidBalance = null; + string symbolWithAnything = null; + string symbolWithEnoughBalance = null; + string symbolWithEnoughBalancePlusAllowance = null; + + amount = 0; + existingBalance = 0; + existingAllowance = 0; + //For user + //Find the token that satisfies the balance of the fee,if there is no token that satisfies the balance of the fee, find the token that balance > 0 + foreach (var (symbol, value) in symbolToAmountMap) { // current token symbol - existingBalance = GetBalance(fromAddress, symbolToAmount.Key); - symbol = symbolToAmount.Key; - amount = symbolToAmount.Value; + amount = value; + existingBalance = GetBalance(fromAddress, symbol); + existingAllowance = GetFreeFeeAllowanceAmount(transactionFeeFreeAllowancesMap, symbol); - // free allowance in current token symbol - existingAllowance = GetFreeFeeAllowanceAmount(freeAllowances, symbol); + var existingBalancePlusAllowance = existingBalance.Add(existingAllowance); - // if delegations is null, that means no delegation is involved. - if (delegations == null - || (delegations.Delegations.ContainsKey(symbol) && delegations.Delegations[symbol] >= amount)) + + // allowance is enough to cover the base fee + if (existingAllowance >= amount) { - if (existingBalance.Add(existingAllowance) > 0) - { - symbolOfValidBalance = symbol; - } + symbolOfValidBalance = symbol; + return true; + } - if (existingBalance.Add(existingAllowance) >= amount) break; + if (existingBalancePlusAllowance <= 0) continue; + + // find symbol: balance + allowance > 0 + symbolWithAnything ??= symbol; + + if (existingBalancePlusAllowance < amount) continue; + + if (existingAllowance > 0) + { + // find symbol: balance plus allowance is enough to cover the base fee and allowance is greater than 0 + symbolWithEnoughBalancePlusAllowance ??= symbol; + } + else + { + // find symbol: balance is enough to cover the base fee and no allowance + symbolWithEnoughBalance ??= symbol; } } - if (delegations == null - || (symbolOfValidBalance != null && delegations.Delegations.ContainsKey(symbolOfValidBalance) && - delegations.Delegations[symbolOfValidBalance] >= amount)) + if (symbolWithEnoughBalancePlusAllowance == null && symbolWithEnoughBalance == null) { - if (existingBalance.Add(existingAllowance) >= amount) return true; - } + symbolOfValidBalance = symbolWithAnything; - var primaryTokenSymbol = GetPrimaryTokenSymbol(new Empty()).Value; - if (symbolToAmountMap.Keys.Contains(primaryTokenSymbol) && delegations == null) - { - symbol = primaryTokenSymbol; - existingBalance = GetBalance(fromAddress, primaryTokenSymbol); - existingAllowance = GetFreeFeeAllowanceAmount(freeAllowances, symbol); + return false; } - else + + symbolOfValidBalance = symbolWithEnoughBalancePlusAllowance ?? symbolWithEnoughBalance; + + return true; + } + + private bool TryToChargeDelegateBaseFee(Dictionary symbolToAmountMap, Address fromAddress, + TransactionFeeFreeAllowancesMap transactionFeeFreeAllowancesMap, TransactionFeeDelegations delegations, + out long amount, out string symbolOfValidBalance, out long existingBalance, out long existingAllowance) + { + symbolOfValidBalance = null; + amount = 0; + existingBalance = 0; + existingAllowance = 0; + string symbolWithEnoughBalance = null; + string symbolWithEnoughBalancePlusAllowance = null; + + //Find the token that satisfies the delegate limit and satisfies the balance of the fee + foreach (var (symbol, value) in symbolToAmountMap) { - symbol = symbolOfValidBalance; - if (symbol != null) + // current token symbol + amount = value; + existingBalance = GetBalance(fromAddress, symbol); + existingAllowance = GetFreeFeeAllowanceAmount(transactionFeeFreeAllowancesMap, symbol); + // is unlimited delegate is true || is unlimited delegate is false and delegation is enough + if (delegations.IsUnlimitedDelegate || (!delegations.IsUnlimitedDelegate && + delegations.Delegations.ContainsKey(symbol) && + delegations.Delegations[symbol] >= amount)) { - existingBalance = GetBalance(fromAddress, symbolOfValidBalance); - existingAllowance = GetFreeFeeAllowanceAmount(freeAllowances, symbol); + //If allowance is enough,return true + if (existingAllowance >= amount) + { + symbolOfValidBalance = symbol; + return true; + } + + //Find symbol which balance+allowance >= amount + if (existingBalance.Add(existingAllowance) < amount) continue; + + if (existingAllowance > 0) + { + //If balance+allowance is enough,priority find the symbol which allowance > 0 + symbolWithEnoughBalancePlusAllowance ??= symbol; + } + else + { + symbolWithEnoughBalance ??= symbol; + } } } - return false; + symbolOfValidBalance = symbolWithEnoughBalancePlusAllowance ?? symbolWithEnoughBalance; + + if (symbolOfValidBalance != null) + { + existingBalance = GetBalance(fromAddress, symbolOfValidBalance); + existingAllowance = GetFreeFeeAllowanceAmount(transactionFeeFreeAllowancesMap, symbolOfValidBalance); + amount = symbolToAmountMap[symbolOfValidBalance]; + } + + return symbolOfValidBalance != null; } public override Empty ClaimTransactionFees(TotalTransactionFeesMap input) @@ -674,6 +879,13 @@ public override Empty ClaimTransactionFees(TotalTransactionFeesMap input) var symbol = bill.Key; var amount = bill.Value; ModifyBalance(Context.Self, symbol, amount); + Context.Fire(new TransactionFeeClaimed + { + Symbol = symbol, + Amount = amount, + Receiver = Context.Self + }); + TransferTransactionFeesToFeeReceiver(symbol, amount); } @@ -772,11 +984,12 @@ private void PayResourceTokens(TotalResourceTokensMaps billMaps, bool isMainChai if (amount > 0) { ModifyBalance(bill.ContractAddress, symbol, -amount); + var receiver = Context.Self; if (isMainChain) { Context.LogDebug(() => $"Adding {amount} of {symbol}s to dividend pool."); // Main Chain. - ModifyBalance(Context.Self, symbol, amount); + ModifyBalance(receiver, symbol, amount); State.DividendPoolContract.Donate.Send(new DonateInput { Symbol = symbol, @@ -787,10 +1000,17 @@ private void PayResourceTokens(TotalResourceTokensMaps billMaps, bool isMainChai { Context.LogDebug(() => $"Adding {amount} of {symbol}s to consensus address account."); // Side Chain - var consensusContractAddress = + receiver = Context.GetContractAddressByName(SmartContractConstants.ConsensusContractSystemName); - ModifyBalance(consensusContractAddress, symbol, amount); + ModifyBalance(receiver, symbol, amount); } + Context.Fire(new ResourceTokenClaimed + { + Symbol = symbol, + Amount = amount, + Payer = bill.ContractAddress, + Receiver = receiver + }); } } } @@ -815,7 +1035,7 @@ private void PayRental() } // Update LastPayRentTime if it is ready to charge rental. - State.LastPayRentTime.Value += new Duration {Seconds = duration.Mul(60)}; + State.LastPayRentTime.Value += new Duration { Seconds = duration.Mul(60) }; foreach (var symbol in Context.Variables.GetStringArray(TokenContractConstants.PayRentalSymbolListName)) { @@ -869,7 +1089,9 @@ private void PayRental() Context.Fire(new RentalCharged() { Symbol = symbol, - Amount = donates + Amount = donates, + Payer = creator, + Receiver = consensusContractAddress }); } } @@ -999,55 +1221,138 @@ public override Address GetFeeReceiver(Empty input) return State.FeeReceiver.Value; } - public override Empty ConfigMethodFeeFreeAllowances(MethodFeeFreeAllowancesConfig input) + public override Empty ConfigTransactionFeeFreeAllowances(ConfigTransactionFeeFreeAllowancesInput input) + { + AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); + Assert(input.Value != null && input.Value.Count > 0, "Invalid input"); + + State.TransactionFeeFreeAllowancesSymbolList.Value ??= new TransactionFeeFreeAllowancesSymbolList + { + Symbols = { new RepeatedField() } + }; + + foreach (var allowances in input.Value!) + { + ValidateToken(allowances.Symbol); + Assert( + allowances.TransactionFeeFreeAllowances?.Value != null && + allowances.TransactionFeeFreeAllowances.Value.Count > 0, + "Invalid input allowances"); + Assert(allowances.Threshold >= 0, "Invalid input threshold"); + Assert(allowances.RefreshSeconds >= 0, "Invalid input refresh seconds"); + + var config = new TransactionFeeFreeAllowanceConfig + { + Symbol = allowances.Symbol, + Threshold = allowances.Threshold, + RefreshSeconds = allowances.RefreshSeconds, + FreeAllowances = new TransactionFeeFreeAllowanceMap() + }; + + foreach (var allowance in allowances.TransactionFeeFreeAllowances!.Value!) + { + config.FreeAllowances.Map.TryAdd(allowance.Symbol, allowance); + } + + State.TransactionFeeFreeAllowancesConfigMap[allowances.Symbol] = config; + + if (!State.TransactionFeeFreeAllowancesSymbolList.Value.Symbols.Contains(allowances.Symbol)) + { + State.TransactionFeeFreeAllowancesSymbolList.Value.Symbols.Add(allowances.Symbol); + } + } + + return new Empty(); + } + + private void ValidateToken(string symbol) + { + Assert(!string.IsNullOrWhiteSpace(symbol), "Invalid input symbol"); + Assert(State.TokenInfos[symbol] != null, $"Symbol {symbol} not exist"); + } + + public override Empty RemoveTransactionFeeFreeAllowancesConfig(RemoveTransactionFeeFreeAllowancesConfigInput input) { AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); - State.MethodFeeFreeAllowancesConfig.Value = input; + Assert(input.Symbols != null && input.Symbols.Count > 0, "Invalid input"); + Assert(State.TransactionFeeFreeAllowancesSymbolList.Value != null, "Method fee free allowances config not set"); + + var symbols = input.Symbols!.Distinct(); + + foreach (var symbol in symbols) + { + if (State.TransactionFeeFreeAllowancesSymbolList.Value!.Symbols.Contains(symbol)) + { + State.TransactionFeeFreeAllowancesSymbolList.Value.Symbols.Remove(symbol); + } + } + return new Empty(); } - public override MethodFeeFreeAllowancesConfig GetMethodFeeFreeAllowancesConfig(Empty input) + public override GetTransactionFeeFreeAllowancesConfigOutput GetTransactionFeeFreeAllowancesConfig(Empty input) { - return State.MethodFeeFreeAllowancesConfig.Value; + var symbols = State.TransactionFeeFreeAllowancesSymbolList.Value?.Symbols; + if (symbols == null) return new GetTransactionFeeFreeAllowancesConfigOutput(); + + var output = new GetTransactionFeeFreeAllowancesConfigOutput(); + + foreach (var symbol in symbols) + { + output.Value.Add(State.TransactionFeeFreeAllowancesConfigMap[symbol]); + } + + return output; } - public override MethodFeeFreeAllowances GetMethodFeeFreeAllowances(Address input) + public override TransactionFeeFreeAllowancesMap GetTransactionFeeFreeAllowances(Address input) { - return CalculateMethodFeeFreeAllowances(input); + return CalculateTransactionFeeFreeAllowances(input); } - private MethodFeeFreeAllowances CalculateMethodFeeFreeAllowances(Address input) + private TransactionFeeFreeAllowancesMap CalculateTransactionFeeFreeAllowances(Address input) { - var freeAllowances = State.MethodFeeFreeAllowancesMap[input]; - var freeAllowancesConfig = State.MethodFeeFreeAllowancesConfig.Value; + var freeAllowanceMap = State.TransactionFeeFreeAllowances[input]; + + var freeAllowancesConfig = State.TransactionFeeFreeAllowancesSymbolList.Value; if (freeAllowancesConfig == null) { - return new MethodFeeFreeAllowances(); + return new TransactionFeeFreeAllowancesMap(); } - var config = freeAllowancesConfig.Clone(); + var transactionFeeFreeAllowancesMap = new TransactionFeeFreeAllowancesMap(); + + foreach (var symbol in freeAllowancesConfig.Symbols) + { + var balance = State.Balances[input][symbol]; + if (balance < State.TransactionFeeFreeAllowancesConfigMap[symbol].Threshold) continue; - var balance = State.Balances[input][Context.Variables.NativeSymbol]; - if (balance < config.Threshold) return new MethodFeeFreeAllowances(); + var lastRefreshTime = State.TransactionFeeFreeAllowancesLastRefreshTimes[input][symbol]; - var lastRefreshTime = State.MethodFeeFreeAllowancesLastRefreshTimeMap[input]; + var freeAllowances = freeAllowanceMap[symbol]; - if (freeAllowances == null) - { - if (balance >= config.Threshold) + if (freeAllowances == null) { - return new MethodFeeFreeAllowances {Value = {config.FreeAllowances.Value}}; + transactionFeeFreeAllowancesMap.Map.Add(symbol, + State.TransactionFeeFreeAllowancesConfigMap[symbol].FreeAllowances.Clone()); + continue; } - } - if (lastRefreshTime == null) - { - return freeAllowances; + if (lastRefreshTime == null) + { + transactionFeeFreeAllowancesMap.Map.Add(symbol, freeAllowances); + } + else + { + transactionFeeFreeAllowancesMap.Map[symbol] = + (Context.CurrentBlockTime - lastRefreshTime).Seconds > + State.TransactionFeeFreeAllowancesConfigMap[symbol].RefreshSeconds + ? State.TransactionFeeFreeAllowancesConfigMap[symbol].FreeAllowances.Clone() + : freeAllowances; + } } - return (Context.CurrentBlockTime - lastRefreshTime).Seconds > config.RefreshSeconds - ? new MethodFeeFreeAllowances {Value = {config.FreeAllowances.Value}} - : freeAllowances; + return transactionFeeFreeAllowancesMap; } private long GetBalanceCalculatedBaseOnPrimaryToken(Address fromAddress, SymbolToPayTxSizeFee tokenInfo, @@ -1063,18 +1368,19 @@ private long GetBalanceCalculatedBaseOnPrimaryToken(Address fromAddress, SymbolT private long GetBalancePlusAllowanceCalculatedBaseOnPrimaryToken(Address fromAddress, SymbolToPayTxSizeFee tokenInfo, string baseSymbol, - long cost, MethodFeeFreeAllowances freeAllowances, long allowanceCost) + long cost, TransactionFeeFreeAllowancesMap transactionFeeFreeAllowancesMap, long allowanceCost) { return GetBalanceCalculatedBaseOnPrimaryToken(fromAddress, tokenInfo, baseSymbol, cost).Add( - GetAllowanceCalculatedBaseOnPrimaryToken(tokenInfo, freeAllowances, baseSymbol, allowanceCost)); + GetAllowanceCalculatedBaseOnPrimaryToken(tokenInfo, transactionFeeFreeAllowancesMap, baseSymbol, + allowanceCost)); } private long GetAllowanceCalculatedBaseOnPrimaryToken(SymbolToPayTxSizeFee tokenInfo, - MethodFeeFreeAllowances freeAllowances, string baseSymbol, + TransactionFeeFreeAllowancesMap transactionFeeFreeAllowancesMap, string baseSymbol, long allowanceCost) { var availableAllowance = - GetFreeFeeAllowanceAmount(freeAllowances, + GetFreeFeeAllowanceAmount(transactionFeeFreeAllowancesMap, tokenInfo.TokenSymbol); //GetBalance(Context.Sender, tokenInfo.TokenSymbol); if (tokenInfo.TokenSymbol == baseSymbol) availableAllowance = availableAllowance.Sub(allowanceCost); @@ -1083,29 +1389,25 @@ private long GetAllowanceCalculatedBaseOnPrimaryToken(SymbolToPayTxSizeFee token } private bool IsDelegationEnoughBaseOnPrimaryToken(SymbolToPayTxSizeFee tokenInfo, string baseSymbol, long cost, - long txSizeFeeAmount, TransactionFeeDelegations delegations = null) + long txSizeFeeAmount, TransactionFeeDelegations delegations) { - if (delegations == null) - { - return true; - } - - if (!delegations.Delegations.ContainsKey(tokenInfo.TokenSymbol)) return false; - txSizeFeeAmount = txSizeFeeAmount.Mul(tokenInfo.AddedTokenWeight) .Div(tokenInfo.BaseTokenWeight); + return IsDelegationEnough(tokenInfo.TokenSymbol, baseSymbol, cost, txSizeFeeAmount, delegations); + } - // If current symbol is base fee symbol, it should be taken into account too. - if (tokenInfo.TokenSymbol != baseSymbol) - { - return delegations.Delegations[tokenInfo.TokenSymbol] >= txSizeFeeAmount; - } - else + private bool IsDelegationEnough(string txSymbol, string baseSymbol, long cost, + long txSizeFeeAmount, TransactionFeeDelegations delegations) + { + if (!delegations.IsUnlimitedDelegate) { - return delegations.Delegations[tokenInfo.TokenSymbol].Sub(cost) >= txSizeFeeAmount; + return delegations.Delegations.ContainsKey(txSymbol) && (baseSymbol == txSymbol + ? delegations.Delegations[txSymbol].Sub(cost) + : delegations.Delegations[txSymbol]) >= txSizeFeeAmount; } - } + return true; + } private void AssertSymbolToPayTxFeeIsValid(string tokenSymbol, out long totalSupply) { diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs index d11e25b7de..ed210282d0 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Linq; using System.Text; using AElf.Contracts.Parliament; @@ -15,7 +16,8 @@ public partial class TokenContract { private static bool IsValidSymbolChar(char character) { - return (character >= 'A' && character <= 'Z') || (character >= '0' && character <= '9') || character == TokenContractConstants.NFTSymbolSeparator; + return (character >= 'A' && character <= 'Z') || (character >= '0' && character <= '9') || + character == TokenContractConstants.NFTSymbolSeparator; } private bool IsValidItemIdChar(char character) @@ -49,6 +51,11 @@ private void AssertValidMemo(string memo) "Invalid memo size."); } + private void AssertValidInputAddress(Address input) + { + Assert(input != null && !input.Value.IsNullOrEmpty(), "Invalid input address."); + } + private void DoTransfer(Address from, Address to, string symbol, long amount, string memo = null) { Assert(from != to, "Can't do transfer to sender itself."); @@ -76,41 +83,74 @@ private void ModifyBalance(Address address, string symbol, long addAmount) State.Balances[address][symbol] = target; } - private void ModifyFreeFeeAllowanceAmount(MethodFeeFreeAllowances freeAllowances, string symbol, long addAmount) + private void ModifyFreeFeeAllowanceAmount(Address fromAddress, + TransactionFeeFreeAllowancesMap transactionFeeFreeAllowancesMap, string symbol, + long addAmount) { - var freeAllowance = GetFreeFeeAllowance(freeAllowances, symbol); - if (freeAllowance != null) + var freeAllowanceAmount = GetFreeFeeAllowanceAmount(transactionFeeFreeAllowancesMap, symbol); + if (addAmount < 0 && freeAllowanceAmount < -addAmount) + { + Assert(false, + $"Insufficient amount of {symbol} for free fee allowance. Need amount: {-addAmount}; Current amount: {freeAllowanceAmount}"); + } + + // Sort symbols by expiration time + var symbolList = GetSymbolListSortedByExpirationTime(transactionFeeFreeAllowancesMap, fromAddress); + + foreach (var s in symbolList) { - var before = freeAllowance.Amount; - if (addAmount < 0 && before < -addAmount) - Assert(false, - $"Insufficient amount of {symbol} for free fee allowance. Need amount: {-addAmount}; Current amount: {before}"); + if (addAmount >= 0) break; + + if (!transactionFeeFreeAllowancesMap.Map[s].Map.ContainsKey(symbol)) continue; + + var currentAllowance = transactionFeeFreeAllowancesMap.Map[s].Map[symbol].Amount; + + if (currentAllowance == 0) continue; + + addAmount += currentAllowance; - var target = before.Add(addAmount); - freeAllowance.Amount = target; + transactionFeeFreeAllowancesMap.Map[s].Map[symbol].Amount = addAmount >= 0 ? addAmount : 0; } } - private long GetBalance(Address address, string symbol) + private List GetSymbolListSortedByExpirationTime(TransactionFeeFreeAllowancesMap transactionFeeFreeAllowancesMap, + Address fromAddress) { - return State.Balances[address][symbol]; + return transactionFeeFreeAllowancesMap.Map.Keys.OrderBy(t => + State.TransactionFeeFreeAllowancesConfigMap[t].RefreshSeconds - (Context.CurrentBlockTime - + State.TransactionFeeFreeAllowancesLastRefreshTimes[ + fromAddress][t]).Seconds).ToList(); } - private MethodFeeFreeAllowance GetFreeFeeAllowance(MethodFeeFreeAllowances freeAllowances, string symbol) + + private long GetBalance(Address address, string symbol) { - return freeAllowances?.Value.FirstOrDefault(a => a.Symbol == symbol); + AssertValidInputAddress(address); + Assert(!string.IsNullOrWhiteSpace(symbol), "Invalid symbol."); + + return State.Balances[address][symbol]; } - private long GetFreeFeeAllowanceAmount(MethodFeeFreeAllowances freeAllowances, string symbol) + // private MethodFeeFreeAllowance GetFreeFeeAllowance(MethodFeeFreeAllowances freeAllowances, string symbol) + // { + // return freeAllowances?.Value.FirstOrDefault(a => a.Symbol == symbol); + // } + + private long GetFreeFeeAllowanceAmount(TransactionFeeFreeAllowancesMap transactionFeeFreeAllowancesMap, string symbol) { - var existingAllowance = 0L; - var freeAllowance = GetFreeFeeAllowance(freeAllowances, symbol); - if (freeAllowance != null) + var allowance = 0L; + var map = transactionFeeFreeAllowancesMap.Map; + + if (map == null) return allowance; + + foreach (var freeAllowances in map.Values) { - existingAllowance = freeAllowance.Amount; + freeAllowances.Map.TryGetValue(symbol, out var freeAllowance); + + allowance = allowance.Add(freeAllowance?.Amount ?? 0L); } - return existingAllowance; + return allowance; } private void AssertSystemContractOrLockWhiteListAddress(string symbol) @@ -143,13 +183,13 @@ private void AssertCrossChainTransaction(Transaction originalTransaction, Addres private void RegisterTokenInfo(TokenInfo tokenInfo) { - var existing = State.TokenInfos[tokenInfo.Symbol]; - Assert(existing == null || existing.Equals(new TokenInfo()), "Token already exists."); + CheckTokenExists(tokenInfo.Symbol); Assert(!string.IsNullOrEmpty(tokenInfo.Symbol) && tokenInfo.Symbol.All(IsValidSymbolChar), "Invalid 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."); + Assert(tokenInfo.Owner != null, "Invalid owner address."); State.TokenInfos[tokenInfo.Symbol] = tokenInfo; } @@ -195,10 +235,36 @@ private void AssertValidCreateInput(CreateInput input, SymbolType symbolType) && input.Symbol.Length > 0 && input.Decimals >= 0 && input.Decimals <= TokenContractConstants.MaxDecimals, "Invalid input."); + + CheckSymbolLength(input.Symbol, symbolType); + if (symbolType == SymbolType.Nft) return; + CheckTokenAndCollectionExists(input.Symbol); + if (IsAddressInCreateWhiteList(Context.Sender)) CheckSymbolSeed(input.Symbol); + } + + private void CheckTokenAndCollectionExists(string symbol) + { + var symbols = symbol.Split(TokenContractConstants.NFTSymbolSeparator); + var tokenSymbol = symbols.First(); + CheckTokenExists(tokenSymbol); + var collectionSymbol = symbols.First() + TokenContractConstants.NFTSymbolSeparator + + TokenContractConstants.CollectionSymbolSuffix; + CheckTokenExists(collectionSymbol); + } + + private void CheckTokenExists(string symbol) + { + var empty = new TokenInfo(); + var existing = State.TokenInfos[symbol]; + Assert(existing == null || existing.Equals(empty), "Token already exists."); + } + + private void CheckSymbolLength(string symbol, SymbolType symbolType) + { if (symbolType == SymbolType.Token) - Assert(input.Symbol.Length <= TokenContractConstants.SymbolMaxLength, "Invalid token symbol length"); + Assert(symbol.Length <= TokenContractConstants.SymbolMaxLength, "Invalid token symbol length"); if (symbolType == SymbolType.Nft || symbolType == SymbolType.NftCollection) - Assert(input.Symbol.Length <= TokenContractConstants.NFTSymbolMaxLength, "Invalid NFT symbol length"); + Assert(symbol.Length <= TokenContractConstants.NFTSymbolMaxLength, "Invalid NFT symbol length"); } private void CheckCrossChainTokenContractRegistrationControllerAuthority() @@ -265,4 +331,31 @@ private void FireExternalLogEvent(TokenInfo tokenInfo, TransferFromInput input) NonIndexed = input.ToByteString() }); } + + private bool IsInLockWhiteList(Address address) + { + return address == GetElectionContractAddress() || address == GetVoteContractAddress(); + } + + private Address GetElectionContractAddress() + { + if (State.ElectionContractAddress.Value == null) + { + State.ElectionContractAddress.Value = + Context.GetContractAddressByName(SmartContractConstants.ElectionContractSystemName); + } + + return State.ElectionContractAddress.Value; + } + + private Address GetVoteContractAddress() + { + if (State.VoteContractAddress.Value == null) + { + State.VoteContractAddress.Value = + Context.GetContractAddressByName(SmartContractConstants.VoteContractSystemName); + } + + return State.VoteContractAddress.Value; + } } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs index 3652a25f91..bddfb444bf 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs @@ -10,7 +10,7 @@ private SymbolType GetCreateInputSymbolType(string symbol) 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] == "0" ? SymbolType.NftCollection : SymbolType.Nft; + return words[1] == TokenContractConstants.CollectionSymbolSuffix ? SymbolType.NftCollection : SymbolType.Nft; } private void AssertNFTCreateInput(CreateInput input) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs index 8d52fba4bd..1926727e68 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs @@ -1,7 +1,5 @@ using System.Linq; using AElf.CSharp.Core; -using AElf.Sdk.CSharp; -using AElf.Standards.ACS1; using AElf.Types; using Google.Protobuf.WellKnownTypes; @@ -20,31 +18,49 @@ private Empty CreateNFTInfo(CreateInput input) AssertNFTCreateInput(input); var nftCollectionInfo = AssertNftCollectionExist(input.Symbol); input.IssueChainId = input.IssueChainId == 0 ? nftCollectionInfo.IssueChainId : input.IssueChainId; - Assert(input.IssueChainId == nftCollectionInfo.IssueChainId, "NFT create ChainId must be collection's issue chainId"); - Assert(Context.Sender == nftCollectionInfo.Issuer && nftCollectionInfo.Issuer == input.Issuer, "NFT issuer must be collection's issuer"); + Assert(input.IssueChainId == nftCollectionInfo.IssueChainId, + "NFT create ChainId must be collection's issue chainId"); + + var owner = nftCollectionInfo.Owner ?? nftCollectionInfo.Issuer; + Assert(Context.Sender == owner && owner == input.Owner, "NFT owner must be collection's owner"); + + if (nftCollectionInfo.Symbol == TokenContractConstants.SeedCollectionSymbol) + { + Assert(input.ExternalInfo.Value.TryGetValue(TokenContractConstants.SeedOwnedSymbolExternalInfoKey, + out var ownedSymbol), "OwnedSymbol does not exist."); + Assert(input.ExternalInfo.Value.TryGetValue(TokenContractConstants.SeedExpireTimeExternalInfoKey, + out var expirationTime) + && long.TryParse(expirationTime, out var expirationTimeLong) && + Context.CurrentBlockTime.Seconds <= expirationTimeLong, "Invalid ownedSymbol."); + var ownedSymbolType = GetCreateInputSymbolType(ownedSymbol); + Assert(ownedSymbolType != SymbolType.Nft, "Invalid OwnedSymbol."); + CheckSymbolLength(ownedSymbol, ownedSymbolType); + CheckTokenAndCollectionExists(ownedSymbol); + CheckSymbolSeed(ownedSymbol); + State.SymbolSeedMap[ownedSymbol] = input.Symbol; + } + return CreateToken(input, SymbolType.Nft); } - private void ChargeCreateFees() + private void CheckSymbolSeed(string ownedSymbol) { - if (Context.Sender == Context.Origin) return; - if (IsAddressInCreateWhiteList(Context.Sender)) return; + var oldSymbolSeed = State.SymbolSeedMap[ownedSymbol]; - var fee = GetCreateMethodFee(); - Assert(fee != null, "not enough balance for create"); - DoTransferFrom(Context.Sender, Context.Self, Context.Self, fee.Symbol, fee.BasicFee, ""); - - ModifyBalance(Context.Self, fee.Symbol, -fee.BasicFee); - Context.Fire(new TransactionFeeCharged() - { - Symbol = fee.Symbol, - Amount = fee.BasicFee, - ChargingAddress = Context.Self - }); + Assert(oldSymbolSeed == null || !State.TokenInfos[oldSymbolSeed].ExternalInfo.Value + .TryGetValue(TokenContractConstants.SeedExpireTimeExternalInfoKey, + out var oldSymbolSeedExpireTime) || + !long.TryParse(oldSymbolSeedExpireTime, out var symbolSeedExpireTime) + || Context.CurrentBlockTime.Seconds > symbolSeedExpireTime, + "OwnedSymbol has been created"); } + private void DoTransferFrom(Address from, Address to, Address spender, string symbol, long amount, string memo) { + AssertValidInputAddress(from); + AssertValidInputAddress(to); + // First check allowance. var allowance = State.Allowances[from][spender][symbol]; if (allowance < amount) @@ -52,7 +68,8 @@ private void DoTransferFrom(Address from, Address to, Address spender, string sy if (IsInWhiteList(new IsInWhiteListInput { Symbol = symbol, Address = spender }).Value) { DoTransfer(from, to, symbol, amount, memo); - DealWithExternalInfoDuringTransfer(new TransferFromInput() { From = from, To = to, Symbol = symbol, Amount = amount, Memo = memo }); + DealWithExternalInfoDuringTransfer(new TransferFromInput() + { From = from, To = to, Symbol = symbol, Amount = amount, Memo = memo }); return; } @@ -62,16 +79,11 @@ private void DoTransferFrom(Address from, Address to, Address spender, string sy } DoTransfer(from, to, symbol, amount, memo); - DealWithExternalInfoDuringTransfer(new TransferFromInput() { From = from, To = to, Symbol = symbol, Amount = amount, Memo = memo }); + DealWithExternalInfoDuringTransfer(new TransferFromInput() + { From = from, To = to, Symbol = symbol, Amount = amount, Memo = memo }); State.Allowances[from][spender][symbol] = allowance.Sub(amount); } - private MethodFee GetCreateMethodFee() - { - var fee = State.TransactionFees[nameof(Create)]; - if (fee == null || fee.Fees.Count <= 0) return new MethodFee { Symbol = Context.Variables.NativeSymbol, BasicFee = 10000_00000000 }; - return fee.Fees.FirstOrDefault(f => GetBalance(Context.Sender, f.Symbol) >= f.BasicFee); - } private string GetNftCollectionSymbol(string inputSymbol) { diff --git a/contract/AElf.Contracts.Profit/ProfitContract.cs b/contract/AElf.Contracts.Profit/ProfitContract.cs index 5cd5514a71..c3c09b3aba 100644 --- a/contract/AElf.Contracts.Profit/ProfitContract.cs +++ b/contract/AElf.Contracts.Profit/ProfitContract.cs @@ -719,16 +719,6 @@ public override Empty ContributeProfits(ContributeProfitsInput input) return new Empty(); } - - public override Empty IncreaseBackupSubsidyTotalShare(Hash schemeId) - { - Assert(!State.BackupSubsidyTotalShareIncreased.Value, "Already increased"); - State.BackupSubsidyTotalShareIncreased.Value = true; - var scheme = State.SchemeInfos[schemeId]; - scheme.TotalShares = scheme.TotalShares.Add(1); - State.SchemeInfos[schemeId] = scheme; - return new Empty(); - } public override Empty ResetManager(ResetManagerInput input) { diff --git a/contract/AElf.Contracts.Profit/ProfitContractState.cs b/contract/AElf.Contracts.Profit/ProfitContractState.cs index 1648619f80..7a525e3ff6 100644 --- a/contract/AElf.Contracts.Profit/ProfitContractState.cs +++ b/contract/AElf.Contracts.Profit/ProfitContractState.cs @@ -17,6 +17,4 @@ public partial class ProfitContractState : ContractState public MappedState TransactionFees { get; set; } public SingletonState MethodFeeController { get; set; } - - public BoolState BackupSubsidyTotalShareIncreased { get; set; } } \ No newline at end of file diff --git a/contract/AElf.Contracts.Treasury/TreasuryContract.cs b/contract/AElf.Contracts.Treasury/TreasuryContract.cs index 7f0c1c3ecc..6a75b5f448 100644 --- a/contract/AElf.Contracts.Treasury/TreasuryContract.cs +++ b/contract/AElf.Contracts.Treasury/TreasuryContract.cs @@ -606,7 +606,7 @@ public override Empty SetProfitsReceiver(SetProfitsReceiverInput input) var pubkey = ByteString.CopyFrom(ByteArrayHelper.HexStringToByteArray(input.Pubkey)); var admin = State.ElectionContract.GetCandidateAdmin.Call(new StringValue {Value = input.Pubkey}); - Assert(Context.Origin == admin , "No permission."); + Assert(Context.Sender == admin , "No permission."); var candidateList = State.ElectionContract.GetCandidates.Call(new Empty()); Assert(candidateList.Value.Contains(pubkey),"Pubkey is not a candidate."); diff --git a/protobuf/aedpos_contract.proto b/protobuf/aedpos_contract.proto index 73742d45fe..d06f5d5ccf 100644 --- a/protobuf/aedpos_contract.proto +++ b/protobuf/aedpos_contract.proto @@ -31,11 +31,11 @@ service AEDPoSContract { } // Update consensus information, create a new round. - rpc NextRound (Round) returns (google.protobuf.Empty) { + rpc NextRound (NextRoundInput) returns (google.protobuf.Empty) { } // Update consensus information, create a new term. - rpc NextTerm (Round) returns (google.protobuf.Empty) { + rpc NextTerm (NextTermInput) returns (google.protobuf.Empty) { } // Update consensus tiny block information. @@ -216,6 +216,8 @@ message UpdateValueInput { map miners_previous_in_values = 11; // The irreversible block height that miner recorded. int64 implied_irreversible_block_height = 12; + // The random number. + bytes random_number = 13; } message MinerList { @@ -339,6 +341,8 @@ message AElfConsensusTriggerInformation { map decrypted_pieces = 6; // The revealed InValues. map revealed_in_values = 7; + // The random number. + bytes random_number = 8; } message TermInfo { @@ -360,6 +364,8 @@ message TinyBlockInput { google.protobuf.Timestamp actual_mining_time = 2; // Count of blocks currently produced int64 produced_blocks = 3; + // The random number. + bytes random_number = 4; } message VoteMinersCountInput { @@ -446,4 +452,56 @@ message MinerReplaced { message RecordCandidateReplacementInput { string old_pubkey = 1; string new_pubkey = 2; +} + +// For compatibility, it is the same as the Round with the addition of the random_number property. +message NextRoundInput { + // The round number. + int64 round_number = 1; + // Current miner information, miner public key -> miner information. + map real_time_miners_information = 2; + // The round number on the main chain + int64 main_chain_miners_round_number = 3; + // The time from chain start to current round (seconds). + int64 blockchain_age = 4; + // The miner public key that produced the extra block in the previous round. + string extra_block_producer_of_previous_round = 5; + // The current term number. + int64 term_number = 6; + // The height of the confirmed irreversible block. + int64 confirmed_irreversible_block_height = 7; + // The round number of the confirmed irreversible block. + int64 confirmed_irreversible_block_round_number = 8; + // Is miner list different from the the miner list in the previous round. + bool is_miner_list_just_changed = 9; + // The round id, calculated by summing block producers’ expecting time (second). + int64 round_id_for_validation = 10; + // The random number. + bytes random_number = 11; +} + +// For compatibility, it is the same as the Round with the addition of the random_number property. +message NextTermInput { + // The round number. + int64 round_number = 1; + // Current miner information, miner public key -> miner information. + map real_time_miners_information = 2; + // The round number on the main chain + int64 main_chain_miners_round_number = 3; + // The time from chain start to current round (seconds). + int64 blockchain_age = 4; + // The miner public key that produced the extra block in the previous round. + string extra_block_producer_of_previous_round = 5; + // The current term number. + int64 term_number = 6; + // The height of the confirmed irreversible block. + int64 confirmed_irreversible_block_height = 7; + // The round number of the confirmed irreversible block. + int64 confirmed_irreversible_block_round_number = 8; + // Is miner list different from the the miner list in the previous round. + bool is_miner_list_just_changed = 9; + // The round id, calculated by summing block producers’ expecting time (second). + int64 round_id_for_validation = 10; + // The random number. + bytes random_number = 11; } \ No newline at end of file diff --git a/protobuf/election_contract.proto b/protobuf/election_contract.proto index 099a56590f..9720786297 100644 --- a/protobuf/election_contract.proto +++ b/protobuf/election_contract.proto @@ -341,6 +341,8 @@ message ElectorVote { repeated ElectionVotingRecord withdrawn_votes_records = 6; // Public key for voter. bytes pubkey = 7; + // Address for voter + aelf.Address address = 8; } message CandidateVote { diff --git a/protobuf/profit_contract.proto b/protobuf/profit_contract.proto index b2fa18e712..1326da62ff 100644 --- a/protobuf/profit_contract.proto +++ b/protobuf/profit_contract.proto @@ -60,10 +60,6 @@ service ProfitContract { rpc RemoveSubScheme (RemoveSubSchemeInput) returns (google.protobuf.Empty) { } - // Increase backup subsidy TotalShare - rpc IncreaseBackupSubsidyTotalShare (aelf.Hash) returns (google.protobuf.Empty) { - } - // Reset the manager of a scheme. rpc ResetManager (ResetManagerInput) returns (google.protobuf.Empty) { } diff --git a/protobuf/test_mock_parliament_contract.proto b/protobuf/test_mock_parliament_contract.proto new file mode 100644 index 0000000000..1024e5cd7d --- /dev/null +++ b/protobuf/test_mock_parliament_contract.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; + +import "aelf/options.proto"; +import "aelf/core.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/wrappers.proto"; + +option csharp_namespace = "AElf.Contracts.TestContract.MockParliament"; + +service MockParliamentContract { + option (aelf.csharp_state) = "AElf.Contracts.TestContract.MockParliament.MockParliamentContractState"; + + rpc Initialize(InitializeInput) returns (google.protobuf.Empty) { + } + + rpc GetDefaultOrganizationAddress (google.protobuf.Empty) returns (aelf.Address) { + option (aelf.is_view) = true; + } +} + +message InitializeInput{ + aelf.Address privileged_proposer = 1; + bool proposer_authority_required = 2; +} diff --git a/protobuf/test_virtual_address_contract.proto b/protobuf/test_virtual_address_contract.proto new file mode 100644 index 0000000000..cdfb14fa05 --- /dev/null +++ b/protobuf/test_virtual_address_contract.proto @@ -0,0 +1,51 @@ +syntax = "proto3"; + +import "aelf/options.proto"; +import "aelf/core.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/wrappers.proto"; +import "acs1.proto"; + +option csharp_namespace = "AElf.Contracts.TestContract.VirtualAddress"; + +service VirtualAddressContract { + option (aelf.csharp_state) = "AElf.Contracts.TestContract.VirtualAddress.State"; + option (aelf.base) = "acs1.proto"; + + rpc VirtualAddressVote(VirtualAddressVoteInput) returns (google.protobuf.Empty); + rpc VirtualAddressWithdraw(aelf.Hash) returns (google.protobuf.Empty); + rpc VirtualAddressChangeVotingOption(VirtualAddressChangeVotingOptionInput) returns (google.protobuf.Empty); + rpc VirtualAddressClaimProfit(VirtualAddressClaimProfitInput) returns (google.protobuf.Empty); + rpc ForwardCall(ForwardCallInput) returns (google.protobuf.Empty); + + rpc GetVirtualAddress(google.protobuf.Empty) returns (aelf.Address) { + option (aelf.is_view) = true; + } +} + +message VirtualAddressVoteInput { + string pub_key = 1; + int64 amount = 2; + google.protobuf.Timestamp end_timestamp = 3; + // Used to generate vote id. + aelf.Hash token = 4; +} + +message VirtualAddressChangeVotingOptionInput { + bool is_reset = 1; + aelf.Hash vote_id = 2; + string pub_key = 3; +} + +message VirtualAddressClaimProfitInput { + aelf.Hash scheme_id = 1; + aelf.Address beneficiary = 2; +} + +message ForwardCallInput { + aelf.Hash virtual_address = 1; + aelf.Address contract_address = 2; + string method_name = 3; + bytes args = 4; +} \ No newline at end of file diff --git a/protobuf/test_vote_contract.proto b/protobuf/test_vote_contract.proto new file mode 100644 index 0000000000..bc30611354 --- /dev/null +++ b/protobuf/test_vote_contract.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +import "aelf/core.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/wrappers.proto"; +import "acs1.proto"; + +option csharp_namespace = "AElf.Contracts.TestContract.Vote"; + +service VoteContract { + option (aelf.csharp_state) = "AElf.Contracts.TestContract.Vote.VoteContractState"; + option (aelf.base) = "acs1.proto"; + + rpc AddOption (AddOptionInput) returns (google.protobuf.Empty) { + } +} + +message AddOptionInput { + aelf.Hash voting_item_id = 1; + aelf.Hash option = 2; +} \ No newline at end of file diff --git a/protobuf/token_contract.proto b/protobuf/token_contract.proto index 6aea317b43..5082990a99 100644 --- a/protobuf/token_contract.proto +++ b/protobuf/token_contract.proto @@ -16,20 +16,20 @@ service TokenContract { // Create a new token. rpc Create (CreateInput) returns (google.protobuf.Empty) { } - + // Issuing some amount of tokens to an address is the action of increasing that addresses balance // for the given token. The total amount of issued tokens must not exceed the total supply of the token // and only the issuer (creator) of the token can issue tokens. // Issuing tokens effectively increases the circulating supply. rpc Issue (IssueInput) returns (google.protobuf.Empty) { } - + // Transferring tokens simply is the action of transferring a given amount of tokens from one address to another. // The origin or source address is the signer of the transaction. // The balance of the sender must be higher than the amount that is transferred. rpc Transfer (TransferInput) returns (google.protobuf.Empty) { } - + // The TransferFrom action will transfer a specified amount of tokens from one address to another. // For this operation to succeed the from address needs to have approved (see allowances) enough tokens // to Sender of this transaction. If successful the amount will be removed from the allowance. @@ -40,43 +40,39 @@ service TokenContract { // enabling the Spender to call TransferFrom. rpc Approve (ApproveInput) returns (google.protobuf.Empty) { } - + // This is the reverse operation for Approve, it will decrease the allowance. rpc UnApprove (UnApproveInput) returns (google.protobuf.Empty) { } - + // This method can be used to lock tokens. rpc Lock (LockInput) returns (google.protobuf.Empty) { } - + // This is the reverse operation of locking, it un-locks some previously locked tokens. rpc Unlock (UnlockInput) returns (google.protobuf.Empty) { } - + // This action will burn the specified amount of tokens, removing them from the token’s Supply. rpc Burn (BurnInput) returns (google.protobuf.Empty) { } - - // Change the issuer of the specified token. Only the original issuer can change it. - rpc ChangeTokenIssuer (ChangeTokenIssuerInput) returns (google.protobuf.Empty) { - } // Set the primary token of side chain. rpc SetPrimaryTokenSymbol (SetPrimaryTokenSymbolInput) returns (google.protobuf.Empty) { } - + // This interface is used for cross-chain transfer. rpc CrossChainTransfer (CrossChainTransferInput) returns (google.protobuf.Empty) { } - + // This method is used to receive cross-chain transfers. rpc CrossChainReceiveToken (CrossChainReceiveTokenInput) returns (google.protobuf.Empty) { } - + // The side chain creates tokens. - rpc CrossChainCreateToken(CrossChainCreateTokenInput) returns (google.protobuf.Empty) { + rpc CrossChainCreateToken(CrossChainCreateTokenInput) returns (google.protobuf.Empty) { } - + // When the side chain is started, the side chain is initialized with the parent chain information. rpc InitializeFromParentChain (InitializeFromParentChainInput) returns (google.protobuf.Empty) { } @@ -84,13 +80,13 @@ service TokenContract { // Handle the transaction fees charged by ChargeTransactionFees. rpc ClaimTransactionFees (TotalTransactionFeesMap) returns (google.protobuf.Empty) { } - + // Used to collect transaction fees. rpc ChargeTransactionFees (ChargeTransactionFeesInput) returns (ChargeTransactionFeesOutput) { } - + rpc ChargeUserContractTransactionFees(ChargeTransactionFeesInput) returns(ChargeTransactionFeesOutput){ - + } // Check the token threshold. @@ -100,108 +96,102 @@ service TokenContract { // Initialize coefficients of every type of tokens supporting charging fee. rpc InitialCoefficients (google.protobuf.Empty) returns (google.protobuf.Empty){ } - + // Processing resource token received. rpc DonateResourceToken (TotalResourceTokensMaps) returns (google.protobuf.Empty) { } - + // A transaction resource fee is charged to implement the ACS8 standards. rpc ChargeResourceToken (ChargeResourceTokenInput) returns (google.protobuf.Empty) { } - + // Verify that the resource token are sufficient. rpc CheckResourceToken (google.protobuf.Empty) returns (google.protobuf.Empty) { } - + // Set the list of tokens to pay transaction fees. rpc SetSymbolsToPayTxSizeFee (SymbolListToPayTxSizeFee) returns (google.protobuf.Empty){ } - + // Update the coefficient of the transaction fee calculation formula. rpc UpdateCoefficientsForSender (UpdateCoefficientsInput) returns (google.protobuf.Empty) { } - + // Update the coefficient of the transaction fee calculation formula. rpc UpdateCoefficientsForContract (UpdateCoefficientsInput) returns (google.protobuf.Empty) { } - + // This method is used to initialize the governance organization for some functions, // including: the coefficient of the user transaction fee calculation formula, // the coefficient of the contract developer resource fee calculation formula, and the side chain rental fee. rpc InitializeAuthorizedController (google.protobuf.Empty) returns (google.protobuf.Empty){ } - rpc ResetExternalInfo (ResetExternalInfoInput) returns (google.protobuf.Empty){ - } - rpc AddAddressToCreateTokenWhiteList (aelf.Address) returns (google.protobuf.Empty) { } rpc RemoveAddressFromCreateTokenWhiteList (aelf.Address) returns (google.protobuf.Empty) { } - - rpc ConfigMethodFeeFreeAllowances (MethodFeeFreeAllowancesConfig) returns (google.protobuf.Empty) { - } - + rpc SetTransactionFeeDelegations (SetTransactionFeeDelegationsInput) returns (SetTransactionFeeDelegationsOutput){ } - + rpc RemoveTransactionFeeDelegator (RemoveTransactionFeeDelegatorInput) returns (google.protobuf.Empty){ } rpc RemoveTransactionFeeDelegatee (RemoveTransactionFeeDelegateeInput) returns (google.protobuf.Empty){ } - + // Get all delegatees' address of delegator from input rpc GetTransactionFeeDelegatees (GetTransactionFeeDelegateesInput) returns (GetTransactionFeeDelegateesOutput) { - + option (aelf.is_view) = true; } // Query token information. rpc GetTokenInfo (GetTokenInfoInput) returns (TokenInfo) { option (aelf.is_view) = true; } - + // Query native token information. rpc GetNativeTokenInfo (google.protobuf.Empty) returns (TokenInfo) { option (aelf.is_view) = true; } - + // Query resource token information. rpc GetResourceTokenInfo (google.protobuf.Empty) returns (TokenInfoList) { option (aelf.is_view) = true; } - + // Query the balance at the specified address. rpc GetBalance (GetBalanceInput) returns (GetBalanceOutput) { option (aelf.is_view) = true; } - + // Query the account's allowance for other addresses rpc GetAllowance (GetAllowanceInput) returns (GetAllowanceOutput) { option (aelf.is_view) = true; } - + // Check whether the token is in the whitelist of an address, // which can be called TransferFrom to transfer the token under the condition of not being credited. rpc IsInWhiteList (IsInWhiteListInput) returns (google.protobuf.BoolValue) { option (aelf.is_view) = true; } - + // Query the information for a lock. rpc GetLockedAmount (GetLockedAmountInput) returns (GetLockedAmountOutput) { option (aelf.is_view) = true; } - + // Query the address of receiving token in cross-chain transfer. rpc GetCrossChainTransferTokenContractAddress (GetCrossChainTransferTokenContractAddressInput) returns (aelf.Address) { option (aelf.is_view) = true; } - + // Query the name of the primary Token. rpc GetPrimaryTokenSymbol (google.protobuf.Empty) returns (google.protobuf.StringValue) { option (aelf.is_view) = true; } - + // Query the coefficient of the transaction fee calculation formula. rpc GetCalculateFeeCoefficientsForContract (google.protobuf.Int32Value) returns (CalculateFeeCoefficients) { option (aelf.is_view) = true; @@ -211,17 +201,17 @@ service TokenContract { rpc GetCalculateFeeCoefficientsForSender (google.protobuf.Empty) returns (CalculateFeeCoefficients) { option (aelf.is_view) = true; } - + // Query tokens that can pay transaction fees. rpc GetSymbolsToPayTxSizeFee (google.protobuf.Empty) returns (SymbolListToPayTxSizeFee){ option (aelf.is_view) = true; } - + // Query the hash of the last input of ClaimTransactionFees. rpc GetLatestTotalTransactionFeesMapHash (google.protobuf.Empty) returns (aelf.Hash){ option (aelf.is_view) = true; } - + // Query the hash of the last input of DonateResourceToken. rpc GetLatestTotalResourceTokensMapsHash (google.protobuf.Empty) returns (aelf.Hash){ option (aelf.is_view) = true; @@ -232,15 +222,9 @@ service TokenContract { rpc GetReservedExternalInfoKeyList (google.protobuf.Empty) returns (StringList) { option (aelf.is_view) = true; } - rpc GetMethodFeeFreeAllowances (aelf.Address) returns (MethodFeeFreeAllowances) { - option (aelf.is_view) = true; - } - rpc GetMethodFeeFreeAllowancesConfig (google.protobuf.Empty) returns (MethodFeeFreeAllowancesConfig) { - option (aelf.is_view) = true; - } - + rpc GetTransactionFeeDelegationsOfADelegatee(GetTransactionFeeDelegationsOfADelegateeInput) returns(TransactionFeeDelegations){ - option (aelf.is_view) = true; + option (aelf.is_view) = true; } } @@ -255,7 +239,7 @@ message TokenInfo { int64 total_supply = 4; // The precision of the token. int32 decimals = 5; - // The address that created the token. + // The address that has permission to issue the token. aelf.Address issuer = 6; // A flag indicating if this token is burnable. bool is_burnable = 7; @@ -265,6 +249,8 @@ message TokenInfo { int64 issued = 9; // The external information of the token. ExternalInfo external_info = 10; + // The address that owns the token. + aelf.Address owner = 11; } message ExternalInfo { @@ -280,7 +266,7 @@ message CreateInput { int64 total_supply = 3; // The precision of the token int32 decimals = 4; - // The address that created the token. + // The address that has permission to issue the token. aelf.Address issuer = 5; // A flag indicating if this token is burnable. bool is_burnable = 6; @@ -290,6 +276,8 @@ message CreateInput { int32 issue_chain_id = 8; // The external information of the token. ExternalInfo external_info = 9; + // The address that owns the token. + aelf.Address owner = 10; } message SetPrimaryTokenSymbolInput { @@ -321,7 +309,7 @@ message TransferInput { message LockInput { // The one want to lock his token. - aelf.Address address = 1; + aelf.Address address = 1; // Id of the lock. aelf.Hash lock_id = 2; // The symbol of the token to lock. @@ -661,33 +649,20 @@ message StringList { repeated string value = 1; } -message MethodFeeFreeAllowancesConfig { - MethodFeeFreeAllowances free_allowances = 1; - int64 refresh_seconds = 2; - int64 threshold = 3; -} - -message MethodFeeFreeAllowances { - repeated MethodFeeFreeAllowance value = 1; -} - -message MethodFeeFreeAllowance { - string symbol = 1; - int64 amount = 2; -} - message TransactionFeeDelegations{ // delegation, symbols and its' amount map delegations = 1; // height when added int64 block_height = 2; + //Whether to pay transaction fee continuously + bool isUnlimitedDelegate = 3; } message TransactionFeeDelegatees{ map delegatees = 1; } -message SetTransactionFeeDelegationsInput { +message SetTransactionFeeDelegationsInput { // the delegator address aelf.Address delegator_address = 1; // delegation, symbols and its' amount @@ -790,6 +765,10 @@ message RentalCharged { string symbol = 1; // The amount of rental fee charged. int64 amount = 2; + // The payer of rental fee. + aelf.Address payer = 3; + // The receiver of rental fee. + aelf.Address receiver = 4; } message RentalAccountBalanceInsufficient { @@ -810,7 +789,7 @@ message TokenCreated { int64 total_supply = 3; // The precision of the token. int32 decimals = 4; - // The address that created the token. + // The address that has permission to issue the token. aelf.Address issuer = 5; // A flag indicating if this token is burnable. bool is_burnable = 6; @@ -818,6 +797,8 @@ message TokenCreated { int32 issue_chain_id = 7; // The external information of the token. ExternalInfo external_info = 8; + // The address that owns the token. + aelf.Address owner = 9; } message Issued { diff --git a/protobuf/token_contract_impl.proto b/protobuf/token_contract_impl.proto index 939c15344f..97acd5b439 100644 --- a/protobuf/token_contract_impl.proto +++ b/protobuf/token_contract_impl.proto @@ -77,6 +77,24 @@ service TokenContractImpl { rpc ChangeDeveloperController (AuthorityInfo) returns (google.protobuf.Empty) { } + rpc ConfigTransactionFeeFreeAllowances (ConfigTransactionFeeFreeAllowancesInput) returns (google.protobuf.Empty) { + } + + rpc RemoveTransactionFeeFreeAllowancesConfig (RemoveTransactionFeeFreeAllowancesConfigInput) returns (google.protobuf.Empty) { + } + + // Delegatee sets the delegation and related information of the delegator based on a transaction. + rpc SetTransactionFeeDelegateInfos (SetTransactionFeeDelegateInfosInput) returns (google.protobuf.Empty){ + } + + // Delegatee remove delegator info based on a transaction. + rpc RemoveTransactionFeeDelegatorInfos (RemoveTransactionFeeDelegatorInfosInput) returns (google.protobuf.Empty){ + } + + // Delegator remove delegatee info based on a transaction. + rpc RemoveTransactionFeeDelegateeInfos (RemoveTransactionFeeDelegateeInfosInput) returns (google.protobuf.Empty){ + } + // Get the address of fee receiver. rpc GetFeeReceiver (google.protobuf.Empty) returns (aelf.Address){ option (aelf.is_view) = true; @@ -128,6 +146,23 @@ service TokenContractImpl { rpc GetOwningRentalUnitValue (google.protobuf.Empty) returns (OwningRentalUnitValue) { option (aelf.is_view) = true; } + + // Query + rpc GetTransactionFeeFreeAllowances (aelf.Address) returns (TransactionFeeFreeAllowancesMap) { + option (aelf.is_view) = true; + } + rpc GetTransactionFeeFreeAllowancesConfig (google.protobuf.Empty) returns (GetTransactionFeeFreeAllowancesConfigOutput) { + option (aelf.is_view) = true; + } + + // Get delegatee info list according to the delegator and transaction. + rpc GetTransactionFeeDelegateeList (GetTransactionFeeDelegateeListInput) returns (GetTransactionFeeDelegateeListOutput) { + option (aelf.is_view) = true; + } + // Get delegation according to the delegator,transaction and delegatee. + rpc GetTransactionFeeDelegateInfo(GetTransactionFeeDelegateInfoInput) returns (token.TransactionFeeDelegations){ + option (aelf.is_view) = true; + } } message AdvanceResourceTokenInput { @@ -170,7 +205,7 @@ message ValidateTokenInfoExistsInput{ int64 total_supply = 3; // The precision of the token. int32 decimals = 4; - // The address that created the token. + // The address that has permission to issue the token. aelf.Address issuer = 5; // A flag indicating if this token is burnable. bool is_burnable = 6; @@ -178,6 +213,8 @@ message ValidateTokenInfoExistsInput{ int32 issue_chain_id = 7; // The external information of the token. map external_info = 8; + // The address that owns the token. + aelf.Address owner = 9; } message UpdateRentalInput { @@ -244,3 +281,138 @@ enum SymbolType { NFT = 1; NFT_COLLECTION = 2; } + +message MethodFeeFreeAllowancesConfig { + MethodFeeFreeAllowances free_allowances = 1; + int64 refresh_seconds = 2; + int64 threshold = 3; +} + +message MethodFeeFreeAllowances{ + repeated MethodFeeFreeAllowance value = 1; +} + +message MethodFeeFreeAllowance { + string symbol = 1; + int64 amount = 2; +} + +message TransactionFeeFreeAllowances{ + repeated TransactionFeeFreeAllowance value = 1; +} + +message TransactionFeeFreeAllowance { + string symbol = 1; + int64 amount = 2; +} + +message TransactionFeeFreeAllowancesSymbolList { + repeated string symbols = 1; +} + +message ConfigTransactionFeeFreeAllowancesInput { + repeated ConfigTransactionFeeFreeAllowance value = 1; +} + +message ConfigTransactionFeeFreeAllowance { + string symbol = 1; + TransactionFeeFreeAllowances transaction_fee_free_allowances = 2; + int64 refresh_seconds = 3; + int64 threshold = 4; +} + +message RemoveTransactionFeeFreeAllowancesConfigInput { + repeated string symbols = 1; +} + +message GetTransactionFeeFreeAllowancesConfigOutput { + repeated TransactionFeeFreeAllowanceConfig value = 1; +} + +message TransactionFeeFreeAllowanceConfig { + string symbol = 1; + TransactionFeeFreeAllowanceMap free_allowances = 2; + int64 refresh_seconds = 3; + int64 threshold = 4; +} + +message TransactionFeeFreeAllowanceMap { + map map = 1; +} + +message TransactionFeeFreeAllowancesMap { + map map = 1; +} + +message SetTransactionFeeDelegateInfosInput{ + // the delegator address + aelf.Address delegator_address = 1; + //delegate info list (support batch) + repeated DelegateInfo delegate_info_list = 2; +} +message DelegateInfo{ + //symbol->amount + map delegations = 1; + aelf.Address contract_address = 2; + string method_name = 3; + //Whether to pay transaction fee continuously + bool isUnlimitedDelegate = 4; +} + +message RemoveTransactionFeeDelegatorInfosInput{ + // the delegator address + aelf.Address delegator_address = 1; + // delegate transaction info (support batch) + repeated DelegateTransaction delegate_transaction_list = 2; +} +message DelegateTransaction{ + aelf.Address contract_address = 1; + string method_name = 2; +} +message DelegateTransactionList{ + repeated DelegateTransaction value = 1; +} + +message RemoveTransactionFeeDelegateeInfosInput { + // the delegatee address + aelf.Address delegatee_address = 1; + // delegate transaction info (support batch) + repeated DelegateTransaction delegate_transaction_list = 2; +} + +message GetTransactionFeeDelegateInfoInput { + aelf.Address delegator_address = 1; + aelf.Address delegatee_address = 2; + aelf.Address contract_address = 3; + string method_name = 4; +} +message GetTransactionFeeDelegateeListInput { + aelf.Address delegator_address = 1; + aelf.Address contract_address = 2; + string method_name = 3; +} +message GetTransactionFeeDelegateeListOutput { + repeated aelf.Address delegatee_addresses = 1; +} +message TransactionFeeDelegateInfoAdded { + option (aelf.is_event) = true; + aelf.Address delegator = 1; + aelf.Address delegatee = 2; + aelf.Address caller = 3; + DelegateTransactionList delegate_transaction_list = 4; +} +message TransactionFeeDelegateInfoUpdated { + option (aelf.is_event) = true; + aelf.Address delegator = 1 ; + aelf.Address delegatee = 2 ; + aelf.Address caller = 3 ; + DelegateTransactionList delegate_transaction_list = 4; +} + +message TransactionFeeDelegateInfoCancelled { + option (aelf.is_event) = true; + aelf.Address delegator = 1 ; + aelf.Address delegatee = 2 ; + aelf.Address caller = 3 ; + DelegateTransactionList delegate_transaction_list = 4; +} diff --git a/protobuf/transaction_fee.proto b/protobuf/transaction_fee.proto index 47f9f4af24..57f7c16d04 100644 --- a/protobuf/transaction_fee.proto +++ b/protobuf/transaction_fee.proto @@ -43,4 +43,19 @@ message ResourceTokenOwned { string symbol = 1; int64 amount = 2; aelf.Address contract_address = 3; +} + +message TransactionFeeClaimed { + option (aelf.is_event) = true; + string symbol = 1; + int64 amount = 2; + aelf.Address receiver = 3; +} + +message ResourceTokenClaimed { + option (aelf.is_event) = true; + string symbol = 1; + int64 amount = 2; + aelf.Address payer = 3; + aelf.Address receiver = 4; } \ No newline at end of file diff --git a/src/AElf.ContractTestBase/AElf.ContractTestBase.csproj b/src/AElf.ContractTestBase/AElf.ContractTestBase.csproj index 40a637defe..cb38e8bf85 100644 --- a/src/AElf.ContractTestBase/AElf.ContractTestBase.csproj +++ b/src/AElf.ContractTestBase/AElf.ContractTestBase.csproj @@ -18,6 +18,11 @@ + + false + Contract + PreserveNewest + @@ -39,6 +44,15 @@ Protobuf\Proto\basic_contract_zero.proto + + Protobuf\Proto\parliament_contract.proto + + + Protobuf\Proto\parliament_contract_impl.proto + + + Protobuf\Proto\acs3.proto + diff --git a/src/AElf.ContractTestBase/ContractTestKit/ContractTestBase.cs b/src/AElf.ContractTestBase/ContractTestKit/ContractTestBase.cs index 765afaa90e..f9c435327f 100644 --- a/src/AElf.ContractTestBase/ContractTestKit/ContractTestBase.cs +++ b/src/AElf.ContractTestBase/ContractTestKit/ContractTestBase.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using AElf.Contracts.Parliament; using AElf.CrossChain; using AElf.Cryptography.ECDSA; using AElf.CSharp.Core; @@ -16,7 +17,9 @@ using AElf.Kernel.SmartContract.Application; using AElf.Kernel.Token; using AElf.Standards.ACS0; +using AElf.Standards.ACS3; using AElf.Types; +using Google.Protobuf; using Google.Protobuf.WellKnownTypes; using Microsoft.Extensions.DependencyInjection; using Volo.Abp; @@ -45,6 +48,9 @@ public ContractTestBase() protected IReadOnlyList Accounts => SampleAccount.Accounts; protected int InitialCoreDataCenterCount => 5; + + protected List InitialCoreDataCenterKeyPairs => + Accounts.Take(InitialCoreDataCenterCount).Select(a => a.KeyPair).ToList(); protected Dictionary SystemContractAddresses { get; } = new(); @@ -155,4 +161,34 @@ private IAbpApplication CreateApplication(ChainInitializationDto dto) where T application.Initialize(); return application; } + + protected async Task SubmitAndApproveProposalOfDefaultParliament(Address contractAddress, string methodName, + IMessage message) + { + var parliamentContractStub = GetTester(ParliamentContractAddress, DefaultAccount.KeyPair); + var defaultParliamentAddress = + await parliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); + + var proposal = new CreateProposalInput + { + OrganizationAddress = defaultParliamentAddress, + ContractMethodName = methodName, + ExpiredTime = TimestampHelper.GetUtcNow().AddHours(1), + Params = message.ToByteString(), + ToAddress = contractAddress + }; + var createResult = await parliamentContractStub.CreateProposal.SendAsync(proposal); + var proposalId = createResult.Output; + await ApproveWithMinersAsync(proposalId); + await parliamentContractStub.Release.SendAsync(proposalId); + } + + private async Task ApproveWithMinersAsync(Hash proposalId) + { + foreach (var bp in InitialCoreDataCenterKeyPairs) + { + var tester = GetTester(ParliamentContractAddress, bp); + await tester.Approve.SendAsync(proposalId); + } + } } \ No newline at end of file diff --git a/src/AElf.ContractTestKit.AEDPoSExtension/AElf.ContractTestKit.AEDPoSExtension.csproj b/src/AElf.ContractTestKit.AEDPoSExtension/AElf.ContractTestKit.AEDPoSExtension.csproj index 515b666c92..8230cdb5e4 100644 --- a/src/AElf.ContractTestKit.AEDPoSExtension/AElf.ContractTestKit.AEDPoSExtension.csproj +++ b/src/AElf.ContractTestKit.AEDPoSExtension/AElf.ContractTestKit.AEDPoSExtension.csproj @@ -10,9 +10,9 @@
- - - + + + @@ -49,10 +49,10 @@ - - + + - +
\ No newline at end of file diff --git a/src/AElf.ContractTestKit.AEDPoSExtension/BlockMiningService.cs b/src/AElf.ContractTestKit.AEDPoSExtension/BlockMiningService.cs index 87cc138f45..85e43156e9 100644 --- a/src/AElf.ContractTestKit.AEDPoSExtension/BlockMiningService.cs +++ b/src/AElf.ContractTestKit.AEDPoSExtension/BlockMiningService.cs @@ -7,6 +7,8 @@ using System.Threading.Tasks; using AElf.ContractDeployer; using AElf.Contracts.Consensus.AEDPoS; +using AElf.Cryptography; +using AElf.Cryptography.ECDSA; using AElf.CSharp.Core.Extension; using AElf.Kernel; using AElf.Kernel.Blockchain.Application; @@ -34,6 +36,7 @@ public class BlockMiningService : IBlockMiningService private readonly ISmartContractAddressService _smartContractAddressService; private readonly ITestDataProvider _testDataProvider; private readonly ITransactionResultService _transactionResultService; + private readonly IBlockchainService _blockchainService; private Address _consensusContractAddress; @@ -52,6 +55,7 @@ public BlockMiningService(IServiceProvider serviceProvider) _testDataProvider = serviceProvider.GetRequiredService(); _transactionResultService = serviceProvider.GetRequiredService(); _chainTypeProvider = serviceProvider.GetRequiredService(); + _blockchainService = serviceProvider.GetRequiredService(); } /// @@ -130,7 +134,8 @@ public async Task MineBlockAsync(List transactions = null, bool wit throw new InitializationFailedException("Can't find current round information."); } - var triggerInformation = await GetConsensusTriggerInfoAsync(contractStub, pubkey); + var randomNumber = await GenerateRandomProofAsync(); + var triggerInformation = await GetConsensusTriggerInfoAsync(contractStub, pubkey, ByteString.CopyFrom(randomNumber)); var consensusTransaction = await contractStub.GenerateConsensusTransactions.CallAsync(new BytesValue { Value = triggerInformation.ToByteString() @@ -157,6 +162,17 @@ public async Task MineBlockAsync(List transactions = null, bool wit _isSkipped = false; } + private async Task GenerateRandomProofAsync() + { + var blockHeight = (await _blockchainService.GetChainAsync()).BestChainHeight; + var previousRandomHash = + blockHeight <= 1 + ? Hash.Empty + : await _contractStubs.First().GetRandomHash.CallAsync(new Int64Value + { Value = blockHeight }); + return CryptoHelper.ECVrfProve((ECKeyPair)_testDataProvider.GetKeyPair(), previousRandomHash.ToByteArray()); + } + public async Task MineBlockToNextRoundAsync() { var consensusStub = _contractTesterFactory.Create( @@ -294,16 +310,16 @@ await contractStub.UpdateValue.SendWithExceptionAsync( break; case nameof(AEDPoSContractImplContainer.AEDPoSContractImplStub.NextRound): if (withException) - await contractStub.NextRound.SendWithExceptionAsync(Round.Parser.ParseFrom(transaction.Params)); + await contractStub.NextRound.SendWithExceptionAsync(NextRoundInput.Parser.ParseFrom(transaction.Params)); else - await contractStub.NextRound.SendAsync(Round.Parser.ParseFrom(transaction.Params)); + await contractStub.NextRound.SendAsync(NextRoundInput.Parser.ParseFrom(transaction.Params)); break; case nameof(AEDPoSContractImplContainer.AEDPoSContractImplStub.NextTerm): if (withException) - await contractStub.NextTerm.SendWithExceptionAsync(Round.Parser.ParseFrom(transaction.Params)); + await contractStub.NextTerm.SendWithExceptionAsync(NextTermInput.Parser.ParseFrom(transaction.Params)); else - await contractStub.NextTerm.SendAsync(Round.Parser.ParseFrom(transaction.Params)); + await contractStub.NextTerm.SendAsync(NextTermInput.Parser.ParseFrom(transaction.Params)); break; } @@ -378,7 +394,7 @@ await contractStub.UpdateValue.SendWithExceptionAsync( } private async Task GetConsensusTriggerInfoAsync( - AEDPoSContractImplContainer.AEDPoSContractImplStub contractStub, BytesValue pubkey) + AEDPoSContractImplContainer.AEDPoSContractImplStub contractStub, BytesValue pubkey, ByteString randomNumber) { var command = await contractStub.GetConsensusCommand.CallAsync(pubkey); var hint = AElfConsensusHint.Parser.ParseFrom(command.Hint); @@ -388,7 +404,8 @@ private async Task GetConsensusTriggerInfoAsync // It doesn't matter for testing. InValue = HashHelper.ComputeFrom($"InValueOf{pubkey}"), PreviousInValue = HashHelper.ComputeFrom($"InValueOf{pubkey}"), - Pubkey = pubkey.Value + Pubkey = pubkey.Value, + RandomNumber = randomNumber }; var consensusExtraData = await contractStub.GetConsensusExtraData.CallAsync(new BytesValue diff --git a/src/AElf.ContractTestKit/ContractTestBase.cs b/src/AElf.ContractTestKit/ContractTestBase.cs index 21c26e6aff..170ac0a9b9 100644 --- a/src/AElf.ContractTestKit/ContractTestBase.cs +++ b/src/AElf.ContractTestKit/ContractTestBase.cs @@ -1,18 +1,24 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using AElf.ContractDeployer; using AElf.Contracts.Genesis; +using AElf.Contracts.Parliament; using AElf.Cryptography.ECDSA; using AElf.CSharp.Core; +using AElf.CSharp.Core.Extension; using AElf.Kernel; using AElf.Kernel.Blockchain.Application; using AElf.Kernel.Blockchain.Domain; using AElf.Kernel.Infrastructure; +using AElf.Kernel.Proposal; using AElf.Kernel.SmartContract.Application; using AElf.Standards.ACS0; +using AElf.Standards.ACS3; using AElf.Types; using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; using MartinCostello.Logging.XUnit; using Microsoft.Extensions.DependencyInjection; using Volo.Abp; diff --git a/src/AElf.Cryptography/AElf.Cryptography.csproj b/src/AElf.Cryptography/AElf.Cryptography.csproj index 0ac287552c..65f0ded71e 100644 --- a/src/AElf.Cryptography/AElf.Cryptography.csproj +++ b/src/AElf.Cryptography/AElf.Cryptography.csproj @@ -1,5 +1,5 @@ - + net6.0 AElf.Cryptography @@ -7,12 +7,12 @@ Cryptographic primitives used in AElf. - - - - + + + + - + diff --git a/src/AElf.Cryptography/Core/Exceptions.cs b/src/AElf.Cryptography/Core/Exceptions.cs new file mode 100644 index 0000000000..99bf00dd80 --- /dev/null +++ b/src/AElf.Cryptography/Core/Exceptions.cs @@ -0,0 +1,28 @@ +using System; + +namespace AElf.Cryptography.Core; + +public class InvalidSerializedPublicKeyException : Exception +{ +} +public class FailedToSerializePointException : Exception +{ +} +public class FailedToCreatePointFromScalarException : Exception +{ +} +public class FailedToNegatePublicKeyException : Exception +{ +} + +public class FailedToCombinePublicKeysException : Exception +{ +} + +public class FailedToMultiplyScalarException : Exception +{ +} + +public class FailedToGetNonceException : Exception +{ +} \ No newline at end of file diff --git a/src/AElf.Cryptography/Core/Helpers.cs b/src/AElf.Cryptography/Core/Helpers.cs new file mode 100644 index 0000000000..0faa2f68c7 --- /dev/null +++ b/src/AElf.Cryptography/Core/Helpers.cs @@ -0,0 +1,63 @@ +using System; +using System.Linq; +using Org.BouncyCastle.Math; + +namespace AElf.Cryptography.Core; + +public static class Helpers +{ + public static byte[] AddLeadingZeros(byte[] data, int requiredLength) + { + var zeroBytesLength = requiredLength - data.Length; + if (zeroBytesLength <= 0) return data; + var output = new byte[requiredLength]; + Buffer.BlockCopy(data, 0, output, zeroBytesLength, data.Length); + for (int i = zeroBytesLength - 1; i >= 0; i--) + { + output[i] = 0x0; + } + + return output; + } + + + public static byte[] Int2Bytes(BigInteger v, int rolen) + { + var result = v.ToByteArray(); + if (result.Length < rolen) + { + return AddLeadingZeros(result, rolen); + } + + if (result.Length > rolen) + { + var skipLength = result.Length - rolen; + return result.Skip(skipLength).ToArray(); + } + + return result; + } + + public static BigInteger Bits2Int(byte[] inputBytes, int qlen) + { + var output = new BigInteger(1, inputBytes); + if (inputBytes.Length * 8 > qlen) + { + return output.ShiftRight(inputBytes.Length * 8 - qlen); + } + + return output; + } + + public static byte[] Bits2Bytes(byte[] input, BigInteger q, int rolen) + { + var z1 = Bits2Int(input, q.BitLength); + var z2 = z1.Subtract(q); + if (z2.SignValue == -1) + { + return Int2Bytes(z1, rolen); + } + + return Int2Bytes(z2, rolen); + } +} \ No newline at end of file diff --git a/src/AElf.Cryptography/Core/IECCurve.cs b/src/AElf.Cryptography/Core/IECCurve.cs new file mode 100644 index 0000000000..5fef5efed6 --- /dev/null +++ b/src/AElf.Cryptography/Core/IECCurve.cs @@ -0,0 +1,15 @@ +using System; + +namespace AElf.Cryptography.Core; + +public interface IECCurve : IDisposable +{ + IECPoint MultiplyScalar(IECPoint point, IECScalar scalar); + IECPoint GetPoint(IECScalar scalar); + byte[] SerializePoint(IECPoint point, bool compressed); + IECPoint Add(IECPoint point1, IECPoint point2); + IECPoint Sub(IECPoint point1, IECPoint point2); + IECPoint DeserializePoint(byte[] input); + IECScalar DeserializeScalar(byte[] input); + byte[] GetNonce(IECScalar privateKey, byte[] hash); +} \ No newline at end of file diff --git a/src/AElf.Cryptography/Core/IECPoint.cs b/src/AElf.Cryptography/Core/IECPoint.cs new file mode 100644 index 0000000000..5fc6b82132 --- /dev/null +++ b/src/AElf.Cryptography/Core/IECPoint.cs @@ -0,0 +1,6 @@ +namespace AElf.Cryptography.Core; + +public interface IECPoint +{ + byte[] Representation { get; } +} \ No newline at end of file diff --git a/src/AElf.Cryptography/Core/IECScalar.cs b/src/AElf.Cryptography/Core/IECScalar.cs new file mode 100644 index 0000000000..071d09f11a --- /dev/null +++ b/src/AElf.Cryptography/Core/IECScalar.cs @@ -0,0 +1,6 @@ +namespace AElf.Cryptography.Core; + +public interface IECScalar +{ + byte[] Representation { get; } +} \ No newline at end of file diff --git a/src/AElf.Cryptography/Core/Secp256k1Curve.cs b/src/AElf.Cryptography/Core/Secp256k1Curve.cs new file mode 100644 index 0000000000..6f59c49e5e --- /dev/null +++ b/src/AElf.Cryptography/Core/Secp256k1Curve.cs @@ -0,0 +1,121 @@ +using System; +using System.Linq; +using Secp256k1Net; + +namespace AElf.Cryptography.Core; + +public sealed class Secp256k1Curve : IECCurve +{ + private Secp256k1 _inner; + + public Secp256k1Curve() + { + _inner = new Secp256k1(); + } + + public IECPoint MultiplyScalar(IECPoint point, IECScalar scalar) + { + var output = point.Representation; + if (!_inner.PublicKeyMultiply(output, scalar.Representation)) + { + throw new FailedToMultiplyScalarException(); + } + + return Secp256k1Point.FromNative(output); + } + + public IECPoint GetPoint(IECScalar scalar) + { + var pkBytes = new byte[Secp256k1.PUBKEY_LENGTH]; + if (!_inner.PublicKeyCreate(pkBytes, scalar.Representation)) + { + throw new FailedToCreatePointFromScalarException(); + } + + return Secp256k1Point.FromNative(pkBytes); + } + + public byte[] SerializePoint(IECPoint point, bool compressed) + { + var repr = point.Representation; + if (compressed) + { + var serialized = new byte[Secp256k1.SERIALIZED_COMPRESSED_PUBKEY_LENGTH]; + if (!_inner.PublicKeySerialize(serialized, repr, Flags.SECP256K1_EC_COMPRESSED)) + { + throw new FailedToSerializePointException(); + } + + return serialized; + } + else + { + var serialized = new byte[Secp256k1.SERIALIZED_UNCOMPRESSED_PUBKEY_LENGTH]; + if (!_inner.PublicKeySerialize(serialized, repr, Flags.SECP256K1_EC_UNCOMPRESSED)) + { + throw new FailedToSerializePointException(); + } + + return serialized; + } + } + + + public IECPoint Add(IECPoint point1, IECPoint point2) + { + var output = new byte[Secp256k1.PUBKEY_LENGTH]; + + if (!_inner.PublicKeysCombine(output, point1.Representation, point2.Representation)) + { + throw new FailedToCombinePublicKeysException(); + } + + return Secp256k1Point.FromNative(output); + } + + public IECPoint Sub(IECPoint point1, IECPoint point2) + { + var point2Neg = point2.Representation; + + if (!_inner.PublicKeyNegate(point2Neg)) + { + throw new FailedToNegatePublicKeyException(); + } + + return Add(point1, Secp256k1Point.FromNative(point2Neg)); + } + + public IECPoint DeserializePoint(byte[] input) + { + var pkBytes = new byte[Secp256k1.PUBKEY_LENGTH]; + if (!_inner.PublicKeyParse(pkBytes, input)) + { + throw new InvalidSerializedPublicKeyException(); + } + + return Secp256k1Point.FromNative(pkBytes); + } + + public IECScalar DeserializeScalar(byte[] input) + { + var normalized = Helpers.AddLeadingZeros(input, Secp256k1.PRIVKEY_LENGTH) + .TakeLast(Secp256k1.PRIVKEY_LENGTH).ToArray(); + return Secp256k1Scalar.FromNative(normalized); + } + + public byte[] GetNonce(IECScalar privateKey, byte[] hash) + { + var nonce = new byte[Secp256k1.NONCE_LENGTH]; + if (!_inner.Rfc6979Nonce(nonce, hash, privateKey.Representation, null, null, 0)) + { + throw new FailedToGetNonceException(); + } + + return nonce; + } + + public void Dispose() + { + _inner.Dispose(); + } +} \ No newline at end of file diff --git a/src/AElf.Cryptography/Core/Secp256k1Point.cs b/src/AElf.Cryptography/Core/Secp256k1Point.cs new file mode 100644 index 0000000000..e972727fd8 --- /dev/null +++ b/src/AElf.Cryptography/Core/Secp256k1Point.cs @@ -0,0 +1,27 @@ +using System; + +namespace AElf.Cryptography.Core; + +public class Secp256k1Point : IECPoint +{ + private readonly byte[] _nativeRep; + + private Secp256k1Point(byte[] nativeRep) + { + _nativeRep = nativeRep; + } + + public static Secp256k1Point FromNative(byte[]nativeRep) + { + return new Secp256k1Point(nativeRep); + } + public byte[] Representation + { + get + { + var output = new byte[_nativeRep.Length]; + Buffer.BlockCopy(_nativeRep, 0, output, 0, _nativeRep.Length); + return output; + } + } +} \ No newline at end of file diff --git a/src/AElf.Cryptography/Core/Secp256k1Scalar.cs b/src/AElf.Cryptography/Core/Secp256k1Scalar.cs new file mode 100644 index 0000000000..7851503439 --- /dev/null +++ b/src/AElf.Cryptography/Core/Secp256k1Scalar.cs @@ -0,0 +1,28 @@ +using System; + +namespace AElf.Cryptography.Core; + +public class Secp256k1Scalar:IECScalar +{ + private readonly byte[] _nativeRep; + + private Secp256k1Scalar(byte[] nativeRep) + { + _nativeRep = nativeRep; + } + + public static Secp256k1Scalar FromNative(byte[]nativeRep) + { + return new Secp256k1Scalar(nativeRep); + } + public byte[] Representation + { + get + { + var output = new byte[_nativeRep.Length]; + Buffer.BlockCopy(_nativeRep, 0, output, 0, _nativeRep.Length); + return output; + } + } + +} \ No newline at end of file diff --git a/src/AElf.Cryptography/Core/Sha256HasherFactory.cs b/src/AElf.Cryptography/Core/Sha256HasherFactory.cs new file mode 100644 index 0000000000..48638040a6 --- /dev/null +++ b/src/AElf.Cryptography/Core/Sha256HasherFactory.cs @@ -0,0 +1,12 @@ +using System.Security.Cryptography; +using AElf.Cryptography.ECVRF; + +namespace AElf.Cryptography.Core; + +public class Sha256HasherFactory:IHasherFactory +{ + public HashAlgorithm Create() + { + return SHA256.Create(); + } +} \ No newline at end of file diff --git a/src/AElf.Cryptography/CryptoHelper.cs b/src/AElf.Cryptography/CryptoHelper.cs index 91abb3a64d..b274636f31 100644 --- a/src/AElf.Cryptography/CryptoHelper.cs +++ b/src/AElf.Cryptography/CryptoHelper.cs @@ -2,10 +2,13 @@ using System.Linq; using System.Security.Cryptography; using System.Threading; +using AElf.Cryptography.Core; using AElf.Cryptography.ECDSA; +using AElf.Cryptography.ECVRF; using AElf.Cryptography.Exceptions; using Secp256k1Net; using Virgil.Crypto; +using ECParameters = AElf.Cryptography.ECDSA.ECParameters; namespace AElf.Cryptography; @@ -16,6 +19,8 @@ public static class CryptoHelper // ReaderWriterLock for thread-safe with Secp256k1 APIs private static readonly ReaderWriterLock Lock = new(); + private static readonly Vrf Vrf = new(new VrfConfig(0xfe, ECParameters.Curve)); + static CryptoHelper() { AppDomain.CurrentDomain.ProcessExit += (sender, arg) => { Secp256K1.Dispose(); }; @@ -160,4 +165,30 @@ public static byte[] Ecdh(byte[] privateKey, byte[] publicKey) Lock.ReleaseWriterLock(); } } + + public static byte[] ECVrfProve(ECKeyPair keyPair, byte[] alpha) + { + try + { + Lock.AcquireWriterLock(Timeout.Infinite); + return Vrf.Prove(keyPair, alpha); + } + finally + { + Lock.ReleaseWriterLock(); + } + } + + public static byte[] ECVrfVerify(byte[] publicKey, byte[] alpha, byte[] pi) + { + try + { + Lock.AcquireWriterLock(Timeout.Infinite); + return Vrf.Verify(publicKey, alpha, pi); + } + finally + { + Lock.ReleaseWriterLock(); + } + } } \ No newline at end of file diff --git a/src/AElf.Cryptography/ECVRF/Exceptions.cs b/src/AElf.Cryptography/ECVRF/Exceptions.cs new file mode 100644 index 0000000000..471f15a394 --- /dev/null +++ b/src/AElf.Cryptography/ECVRF/Exceptions.cs @@ -0,0 +1,15 @@ +using System; + +namespace AElf.Cryptography.ECVRF; + +public class FailedToHashToCurveException : Exception +{ +} + +public class InvalidProofLengthException : Exception +{ +} + +public class InvalidProofException : Exception +{ +} \ No newline at end of file diff --git a/src/AElf.Cryptography/ECVRF/Types.cs b/src/AElf.Cryptography/ECVRF/Types.cs new file mode 100644 index 0000000000..d9a9975ae4 --- /dev/null +++ b/src/AElf.Cryptography/ECVRF/Types.cs @@ -0,0 +1,38 @@ +using System.Security.Cryptography; +using AElf.Cryptography.Core; +using AElf.Cryptography.ECDSA; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Math; + +namespace AElf.Cryptography.ECVRF; + +public struct ProofInput +{ + public IECPoint Gamma { get; set; } + public BigInteger C { get; set; } + public BigInteger S { get; set; } +} + +public struct VrfConfig +{ + public byte SuiteString { get; private set; } + + public X9ECParameters EcParameters { get; private set; } + + public VrfConfig(byte suiteString, X9ECParameters ecParameters) + { + SuiteString = suiteString; + EcParameters = ecParameters; + } +} + +public interface IHasherFactory +{ + HashAlgorithm Create(); +} + +public interface IVrf +{ + byte[] Prove(ECKeyPair keyPair, byte[] alpha); + byte[] Verify(byte[] publicKey, byte[] alpha, byte[] pi); +} \ No newline at end of file diff --git a/src/AElf.Cryptography/ECVRF/Vrf.cs b/src/AElf.Cryptography/ECVRF/Vrf.cs new file mode 100644 index 0000000000..bbebe61a93 --- /dev/null +++ b/src/AElf.Cryptography/ECVRF/Vrf.cs @@ -0,0 +1,204 @@ +using System; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using AElf.Cryptography.Core; +using AElf.Cryptography.ECDSA; +using Org.BouncyCastle.Math; +using Secp256k1Net; + +namespace AElf.Cryptography.ECVRF; + +public class Vrf : IVrf where TCurve : IECCurve, new() + where THasherFactory : IHasherFactory, new() +{ + private int BitSize => _config.EcParameters.Curve.FieldSize; + private int QBitsLength => _config.EcParameters.N.BitLength; + private int N => ((BitSize + 1) / 2 + 7) / 8; + + private readonly VrfConfig _config; + private readonly IHasherFactory _hasherFactory; + + public Vrf(VrfConfig config) + { + _config = config; + _hasherFactory = new THasherFactory(); + } + + public byte[] Prove(ECKeyPair keyPair, byte[] alpha) + { + using var curve = new TCurve(); + var point = curve.DeserializePoint(keyPair.PublicKey); + var hashPoint = HashToCurveTryAndIncrement(point, alpha); + var gamma = curve.MultiplyScalar(hashPoint, curve.DeserializeScalar(keyPair.PrivateKey)); + + var nonce = Rfc6979Nonce(keyPair, hashPoint); + var kB = curve.GetPoint(nonce); + var kH = curve.MultiplyScalar(hashPoint, nonce); + var c = HashPoints(hashPoint, gamma, kB, kH); + var cX = c.Multiply(new BigInteger(1, keyPair.PrivateKey)); + var s = cX.Add(new BigInteger(1, nonce.Representation)).Mod(_config.EcParameters.N); + return EncodeProof(gamma, c, s); + } + + public byte[] Verify(byte[] publicKey, byte[] alpha, byte[] pi) + { + using var curve = new TCurve(); + var proofInput = DecodeProof(pi); + + var pkPoint = curve.DeserializePoint(publicKey); + var hashPoint = HashToCurveTryAndIncrement(pkPoint, alpha); + var s = curve.DeserializeScalar(proofInput.S.ToByteArray()); + var c = curve.DeserializeScalar(proofInput.C.ToByteArray()); + + var sB = curve.GetPoint(s); + var cY = curve.MultiplyScalar(pkPoint, c); + var u = curve.Sub(sB, cY); + + var sH = curve.MultiplyScalar(hashPoint, s); + var cGamma = curve.MultiplyScalar(proofInput.Gamma, c); + var v = curve.Sub(sH, cGamma); + + var derivedC = HashPoints(hashPoint, proofInput.Gamma, u, v); + if (!derivedC.Equals(proofInput.C)) + { + throw new InvalidProofException(); + } + + return GammaToHash(proofInput.Gamma); + } + + public IECPoint HashToCurveTryAndIncrement(IECPoint point, byte[] alpha) + { + using var curve = new TCurve(); + + // Step 1: ctr = 0 + var ctr = 0; + + // Step 2: PK_string = point_to_string(Y) + var pkString = curve.SerializePoint(point, true); + + // Steps 3 ~ 6 + byte oneString = 0x01; + for (; ctr < 256; ctr++) + { + using var hasher = _hasherFactory.Create(); + using var stream = new MemoryStream(); + stream.WriteByte(_config.SuiteString); + stream.WriteByte(oneString); + stream.Write(pkString); + stream.Write(alpha); + stream.WriteByte((byte)ctr); + stream.Seek(0, SeekOrigin.Begin); + var hash = hasher.ComputeHash(stream); + var pkSerialized = new byte[Secp256k1.SERIALIZED_COMPRESSED_PUBKEY_LENGTH]; + pkSerialized[0] = 0x02; + Buffer.BlockCopy(hash, 0, pkSerialized, 1, hash.Length); + try + { + var outputPoint = curve.DeserializePoint(pkSerialized); + if (_config.EcParameters.Curve.Cofactor.CompareTo(BigInteger.One) > 0) + { + return curve.MultiplyScalar(outputPoint, + curve.DeserializeScalar(_config.EcParameters.Curve.Cofactor.ToByteArray())); + } + + return outputPoint; + } + catch (InvalidSerializedPublicKeyException ex) + { + // Ignore this exception and try the next ctr + } + } + + throw new FailedToHashToCurveException(); + } + + private BigInteger HashPoints(params IECPoint[] points) + { + using var curve = new TCurve(); + using var hasher = _hasherFactory.Create(); + using var stream = new MemoryStream(); + stream.WriteByte(_config.SuiteString); + stream.WriteByte(0x02); + foreach (var point in points) + { + stream.Write(curve.SerializePoint(point, true)); + } + + stream.Seek(0, SeekOrigin.Begin); + var hash = hasher.ComputeHash(stream); + var hashTruncated = hash.Take(N).ToArray(); + return new BigInteger(1, hashTruncated); + } + + private byte[] EncodeProof(IECPoint gamma, BigInteger c, BigInteger s) + { + using var curve = new TCurve(); + var gammaBytes = curve.SerializePoint(gamma, true); + var cBytes = Helpers.Int2Bytes(c, N); + var sBytes = Helpers.Int2Bytes(s, (QBitsLength + 7) / 8); + var output = new byte[gammaBytes.Length + cBytes.Length + sBytes.Length]; + Buffer.BlockCopy(gammaBytes, 0, output, 0, gammaBytes.Length); + Buffer.BlockCopy(cBytes, 0, output, gammaBytes.Length, cBytes.Length); + Buffer.BlockCopy(sBytes, 0, output, gammaBytes.Length + cBytes.Length, sBytes.Length); + return output; + } + + private ProofInput DecodeProof(byte[] pi) + { + using var curve = new TCurve(); + var ptLength = (BitSize + 7) / 8 + 1; + var cLength = N; + var sLength = (QBitsLength + 7) / 8; + if (pi.Length != ptLength + cLength + sLength) + { + throw new InvalidProofLengthException(); + } + + var gammaPoint = curve.DeserializePoint(pi.Take(ptLength).ToArray()); + var c = new BigInteger(1, pi.Skip(ptLength).Take(cLength).ToArray()); + var s = new BigInteger(1, pi.TakeLast(sLength).ToArray()); + return new ProofInput() + { + Gamma = gammaPoint, + C = c, + S = s + }; + } + + private byte[] GammaToHash(IECPoint gamma) + { + using var curve = new TCurve(); + + var gammaCof = gamma; + if (_config.EcParameters.Curve.Cofactor.CompareTo(BigInteger.One) > 0) + { + gammaCof = curve.MultiplyScalar(gamma, + curve.DeserializeScalar(_config.EcParameters.Curve.Cofactor.ToByteArray())); + } + + var gammaCofBytes = curve.SerializePoint(gammaCof, true); + using var hasher = _hasherFactory.Create(); + using var stream = new MemoryStream(); + stream.WriteByte(_config.SuiteString); + stream.WriteByte(0x03); + stream.Write(gammaCofBytes); + stream.Seek(0, SeekOrigin.Begin); + return hasher.ComputeHash(stream); + } + + private IECScalar Rfc6979Nonce(ECKeyPair keyPair, IECPoint hashPoint) + { + using var curve = new TCurve(); + using var hasher = _hasherFactory.Create(); + var roLen = (QBitsLength + 7) / 8; + var hBytes = curve.SerializePoint(hashPoint, true); + + var hash = hasher.ComputeHash(hBytes); + var bh = Helpers.Bits2Bytes(hash, _config.EcParameters.N, roLen); + + var nonce = curve.GetNonce(curve.DeserializeScalar(keyPair.PrivateKey), bh); + return curve.DeserializeScalar(nonce); + } +} \ No newline at end of file diff --git a/src/AElf.Kernel.Consensus.AEDPoS/AElf.Kernel.Consensus.AEDPoS.csproj b/src/AElf.Kernel.Consensus.AEDPoS/AElf.Kernel.Consensus.AEDPoS.csproj index beb55cf877..fc3fc47069 100644 --- a/src/AElf.Kernel.Consensus.AEDPoS/AElf.Kernel.Consensus.AEDPoS.csproj +++ b/src/AElf.Kernel.Consensus.AEDPoS/AElf.Kernel.Consensus.AEDPoS.csproj @@ -1,5 +1,5 @@ - + net6.0 @@ -15,7 +15,7 @@ - + @@ -31,7 +31,7 @@ - + diff --git a/src/AElf.Kernel.Consensus.AEDPoS/Application/AEDPoSInformationProvider.cs b/src/AElf.Kernel.Consensus.AEDPoS/Application/AEDPoSInformationProvider.cs index a893dd3eb5..971ed6bead 100644 --- a/src/AElf.Kernel.Consensus.AEDPoS/Application/AEDPoSInformationProvider.cs +++ b/src/AElf.Kernel.Consensus.AEDPoS/Application/AEDPoSInformationProvider.cs @@ -4,6 +4,7 @@ using AElf.Contracts.Consensus.AEDPoS; using AElf.Kernel.Consensus.Application; using AElf.Kernel.SmartContract.Application; +using AElf.Types; using Google.Protobuf.WellKnownTypes; namespace AElf.Kernel.Consensus.AEDPoS.Application; @@ -22,7 +23,7 @@ public AEDPoSInformationProvider( _consensusReaderContextService = consensusReaderContextService; } - public async Task> GetCurrentMinerList(ChainContext chainContext) + public async Task> GetCurrentMinerListAsync(ChainContext chainContext) { var contractReaderContext = await _consensusReaderContextService.GetContractReaderContextAsync(chainContext); diff --git a/src/AElf.Kernel.Consensus.AEDPoS/Application/AEDPoSTriggerInformationProvider.cs b/src/AElf.Kernel.Consensus.AEDPoS/Application/AEDPoSTriggerInformationProvider.cs index 255d1549f0..4801f78af1 100644 --- a/src/AElf.Kernel.Consensus.AEDPoS/Application/AEDPoSTriggerInformationProvider.cs +++ b/src/AElf.Kernel.Consensus.AEDPoS/Application/AEDPoSTriggerInformationProvider.cs @@ -1,6 +1,8 @@ +using System.Threading.Tasks; using AElf.Contracts.Consensus.AEDPoS; using AElf.Kernel.Account.Application; using AElf.Kernel.Consensus.Application; +using AElf.Types; using Google.Protobuf; using Google.Protobuf.WellKnownTypes; using Microsoft.Extensions.Logging; @@ -14,13 +16,15 @@ internal class AEDPoSTriggerInformationProvider : ITriggerInformationProvider private readonly IAccountService _accountService; private readonly IInValueCache _inValueCache; private readonly ISecretSharingService _secretSharingService; + private readonly IRandomNumberProvider _randomNumberProvider; public AEDPoSTriggerInformationProvider(IAccountService accountService, - ISecretSharingService secretSharingService, IInValueCache inValueCache) + ISecretSharingService secretSharingService, IInValueCache inValueCache, IRandomNumberProvider randomNumberProvider) { _accountService = accountService; _secretSharingService = secretSharingService; _inValueCache = inValueCache; + _randomNumberProvider = randomNumberProvider; Logger = NullLogger.Instance; } @@ -70,13 +74,16 @@ public BytesValue GetTriggerInformationForBlockHeaderExtraData(BytesValue consen }.ToBytesValue(); } - public BytesValue GetTriggerInformationForConsensusTransactions(BytesValue consensusCommandBytes) + public BytesValue GetTriggerInformationForConsensusTransactions(IChainContext chainContext, BytesValue consensusCommandBytes) { + var randomProof = AsyncHelper.RunSync(async ()=> await _randomNumberProvider.GenerateRandomProofAsync(chainContext)); + if (consensusCommandBytes == null) return new AElfConsensusTriggerInformation { Pubkey = Pubkey, - Behaviour = AElfConsensusBehaviour.UpdateValue + Behaviour = AElfConsensusBehaviour.UpdateValue, + RandomNumber = ByteString.CopyFrom(randomProof) }.ToBytesValue(); var command = consensusCommandBytes.ToConsensusCommand(); @@ -90,7 +97,8 @@ public BytesValue GetTriggerInformationForConsensusTransactions(BytesValue conse Pubkey = Pubkey, InValue = inValue, PreviousInValue = _inValueCache.GetInValue(hint.PreviousRoundId), - Behaviour = hint.Behaviour + Behaviour = hint.Behaviour, + RandomNumber = ByteString.CopyFrom(randomProof) }; var secretPieces = _secretSharingService.GetEncryptedPieces(hint.RoundId); @@ -111,7 +119,8 @@ public BytesValue GetTriggerInformationForConsensusTransactions(BytesValue conse return new AElfConsensusTriggerInformation { Pubkey = Pubkey, - Behaviour = hint.Behaviour + Behaviour = hint.Behaviour, + RandomNumber = ByteString.CopyFrom(randomProof) }.ToBytesValue(); } } \ No newline at end of file diff --git a/src/AElf.Kernel.Consensus.AEDPoS/Application/IAEDPoSInformationProvider.cs b/src/AElf.Kernel.Consensus.AEDPoS/Application/IAEDPoSInformationProvider.cs index 0eb4d0f7cc..73dcb641fe 100644 --- a/src/AElf.Kernel.Consensus.AEDPoS/Application/IAEDPoSInformationProvider.cs +++ b/src/AElf.Kernel.Consensus.AEDPoS/Application/IAEDPoSInformationProvider.cs @@ -1,10 +1,11 @@ using System.Collections.Generic; using System.Threading.Tasks; +using AElf.Types; namespace AElf.Kernel.Consensus.AEDPoS.Application; // ReSharper disable once InconsistentNaming public interface IAEDPoSInformationProvider { - Task> GetCurrentMinerList(ChainContext chainContext); + Task> GetCurrentMinerListAsync(ChainContext chainContext); } \ No newline at end of file diff --git a/src/AElf.Kernel.Consensus.AEDPoS/Application/IRandomNumberProvider.cs b/src/AElf.Kernel.Consensus.AEDPoS/Application/IRandomNumberProvider.cs new file mode 100644 index 0000000000..cd21577534 --- /dev/null +++ b/src/AElf.Kernel.Consensus.AEDPoS/Application/IRandomNumberProvider.cs @@ -0,0 +1,47 @@ +using System.Threading.Tasks; +using AElf.Contracts.Consensus.AEDPoS; +using AElf.Kernel.Account.Application; +using AElf.Kernel.Consensus.Application; +using AElf.Kernel.SmartContract.Application; +using AElf.Types; +using Google.Protobuf.WellKnownTypes; +using Volo.Abp.DependencyInjection; + +namespace AElf.Kernel.Consensus.AEDPoS.Application; + +public interface IRandomNumberProvider +{ + Task GenerateRandomProofAsync(IChainContext chainContext); +} + +internal class RandomNumberProvider : IRandomNumberProvider, ITransientDependency +{ + private readonly IAccountService _accountService; + private readonly IConsensusReaderContextService _consensusReaderContextService; + private readonly IContractReaderFactory _contractReaderFactory; + + public RandomNumberProvider(IAccountService accountService, + IConsensusReaderContextService consensusReaderContextService, + IContractReaderFactory contractReaderFactory) + { + _accountService = accountService; + _consensusReaderContextService = consensusReaderContextService; + _contractReaderFactory = contractReaderFactory; + } + + public async Task GenerateRandomProofAsync(IChainContext chainContext) + { + var previousRandomHash = chainContext.BlockHeight == AElfConstants.GenesisBlockHeight + ? Hash.Empty + : await GetRandomHashAsync(chainContext, chainContext.BlockHeight); + return await _accountService.ECVrfProveAsync(previousRandomHash.ToByteArray()); + } + + private async Task GetRandomHashAsync(IChainContext chainContext, long blockHeight) + { + var contractReaderContext = + await _consensusReaderContextService.GetContractReaderContextAsync(chainContext); + return await _contractReaderFactory + .Create(contractReaderContext).GetRandomHash.CallAsync(new Int64Value { Value = blockHeight }); + } +} diff --git a/src/AElf.Kernel.Consensus.Core/Application/ConsensusService.cs b/src/AElf.Kernel.Consensus.Core/Application/ConsensusService.cs index 680a36dfb5..50a75d9f90 100644 --- a/src/AElf.Kernel.Consensus.Core/Application/ConsensusService.cs +++ b/src/AElf.Kernel.Consensus.Core/Application/ConsensusService.cs @@ -215,7 +215,7 @@ public async Task> GenerateConsensusTransactionsAsync(ChainCon .Create(contractReaderContext) .GenerateConsensusTransactions .CallAsync(_triggerInformationProvider.GetTriggerInformationForConsensusTransactions( - _consensusCommand.ToBytesValue()))) + chainContext, _consensusCommand.ToBytesValue()))) .Transactions .ToList(); diff --git a/src/AElf.Kernel.Consensus.Core/Application/ITriggerInformationProvider.cs b/src/AElf.Kernel.Consensus.Core/Application/ITriggerInformationProvider.cs index 26567a68b5..7ba7d31e5c 100644 --- a/src/AElf.Kernel.Consensus.Core/Application/ITriggerInformationProvider.cs +++ b/src/AElf.Kernel.Consensus.Core/Application/ITriggerInformationProvider.cs @@ -7,5 +7,5 @@ public interface ITriggerInformationProvider { BytesValue GetTriggerInformationForConsensusCommand(BytesValue consensusCommandBytes); BytesValue GetTriggerInformationForBlockHeaderExtraData(BytesValue consensusCommandBytes); - BytesValue GetTriggerInformationForConsensusTransactions(BytesValue consensusCommandBytes); + BytesValue GetTriggerInformationForConsensusTransactions(IChainContext chainContext, BytesValue consensusCommandBytes); } \ No newline at end of file diff --git a/src/AElf.Kernel.Core/Account/Application/IAccountService.cs b/src/AElf.Kernel.Core/Account/Application/IAccountService.cs index fa47a8ecc5..523dee30cb 100644 --- a/src/AElf.Kernel.Core/Account/Application/IAccountService.cs +++ b/src/AElf.Kernel.Core/Account/Application/IAccountService.cs @@ -1,4 +1,6 @@ using AElf.Cryptography; +using AElf.Cryptography.ECDSA; +using AElf.Cryptography.ECVRF; using AElf.Kernel.Account.Infrastructure; namespace AElf.Kernel.Account.Application; @@ -9,6 +11,7 @@ public interface IAccountService Task GetPublicKeyAsync(); Task EncryptMessageAsync(byte[] receiverPublicKey, byte[] plainMessage); Task DecryptMessageAsync(byte[] senderPublicKey, byte[] cipherMessage); + Task ECVrfProveAsync(byte[] message); } public static class AccountServiceExtensions @@ -53,4 +56,9 @@ public Task DecryptMessageAsync(byte[] senderPublicKey, byte[] cipherMes return Task.FromResult(CryptoHelper.DecryptMessage(senderPublicKey, _ecKeyPairProvider.GetKeyPair().PrivateKey, cipherMessage)); } + + public Task ECVrfProveAsync(byte[] message) + { + return Task.FromResult(CryptoHelper.ECVrfProve((ECKeyPair)_ecKeyPairProvider.GetKeyPair(), message)); + } } \ No newline at end of file diff --git a/src/AElf.Kernel.SmartContract.Shared/ISmartContractBridgeContext.cs b/src/AElf.Kernel.SmartContract.Shared/ISmartContractBridgeContext.cs index a7f5fbcf99..473ccfc932 100644 --- a/src/AElf.Kernel.SmartContract.Shared/ISmartContractBridgeContext.cs +++ b/src/AElf.Kernel.SmartContract.Shared/ISmartContractBridgeContext.cs @@ -88,6 +88,8 @@ Address ConvertVirtualAddressToContractAddressWithContractHashName(Hash virtualA long ConvertHashToInt64(Hash hash, long start = 0, long end = long.MaxValue); object ValidateStateSize(object obj); + + bool ECVrfVerify(byte[] pubKey, byte[] alpha, byte[] pi, out byte[] beta); } [Serializable] diff --git a/src/AElf.Kernel.SmartContract/HostSmartContractBridgeContext.cs b/src/AElf.Kernel.SmartContract/HostSmartContractBridgeContext.cs index 144f1aa0a5..53cd03ed12 100644 --- a/src/AElf.Kernel.SmartContract/HostSmartContractBridgeContext.cs +++ b/src/AElf.Kernel.SmartContract/HostSmartContractBridgeContext.cs @@ -349,4 +349,19 @@ public byte[] RecoverPublicKey(byte[] signature, byte[] hash) var cabBeRecovered = CryptoHelper.RecoverPublicKey(signature, hash, out var publicKey); return !cabBeRecovered ? null : publicKey; } + + public bool ECVrfVerify(byte[] pubKey, byte[] alpha, byte[] pi, out byte[] beta) + { + try + { + beta = CryptoHelper.ECVrfVerify(pubKey, alpha, pi); + } + catch + { + beta = null; + return false; + } + + return true; + } } \ No newline at end of file diff --git a/src/AElf.OS/Account/Application/AccountService.cs b/src/AElf.OS/Account/Application/AccountService.cs index 9e2e2400d4..ec1193c564 100644 --- a/src/AElf.OS/Account/Application/AccountService.cs +++ b/src/AElf.OS/Account/Application/AccountService.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using AElf.Cryptography; using AElf.Cryptography.ECDSA; +using AElf.Cryptography.ECVRF; using AElf.Kernel.Account.Application; using AElf.OS.Account.Infrastructure; using AElf.Types; @@ -43,6 +44,12 @@ public async Task DecryptMessageAsync(byte[] senderPublicKey, byte[] cip cipherMessage); } + public async Task ECVrfProveAsync(byte[] message) + { + var keyPair = await GetAccountKeyPairAsync(); + return CryptoHelper.ECVrfProve(keyPair, message); + } + public async Task
GetAccountAsync() { var publicKey = (await GetAccountKeyPairAsync()).PublicKey; diff --git a/src/AElf.Sdk.CSharp/CSharpSmartContractContext.cs b/src/AElf.Sdk.CSharp/CSharpSmartContractContext.cs index 2f56f80931..ae91d612d8 100644 --- a/src/AElf.Sdk.CSharp/CSharpSmartContractContext.cs +++ b/src/AElf.Sdk.CSharp/CSharpSmartContractContext.cs @@ -372,4 +372,9 @@ public Address ConvertVirtualAddressToContractAddressWithContractHashName(Hash v return SmartContractBridgeContextImplementation.ConvertVirtualAddressToContractAddressWithContractHashName( virtualAddress); } + + public bool ECVrfVerify(byte[] pubKey, byte[] alpha, byte[] pi, out byte[] beta) + { + return SmartContractBridgeContextImplementation.ECVrfVerify(pubKey, alpha, pi, out beta); + } } \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs index 1c5fdabe4c..08552f42e5 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs @@ -101,19 +101,9 @@ public async Task GetTransactionResultAsync(string transac return output; } - - var methodDescriptor = - await GetContractMethodDescriptorAsync(transaction!.To, transaction.MethodName, false); - - if (methodDescriptor != null) - { - var parameters = methodDescriptor.InputType.Parser.ParseFrom(transaction.Params); - if (!IsValidMessage(parameters)) - throw new UserFriendlyException(Error.Message[Error.InvalidParams], Error.InvalidParams.ToString()); - - output.Transaction.Params = JsonFormatter.ToDiagnosticString(parameters); - } - + + await FormatTransactionParamsAsync(output.Transaction, transaction.Params); + return output; } @@ -244,17 +234,7 @@ private async Task GetTransactionResultDto(Hash transactio transactionResultDto.Transaction = _objectMapper.Map(transaction); transactionResultDto.TransactionSize = transaction.CalculateSize(); - var methodDescriptor = - await GetContractMethodDescriptorAsync(transaction.To, transaction.MethodName, false); - - if (methodDescriptor != null) - { - var parameters = methodDescriptor.InputType.Parser.ParseFrom(transaction.Params); - if (!IsValidMessage(parameters)) - throw new UserFriendlyException(Error.Message[Error.InvalidParams], Error.InvalidParams.ToString()); - - transactionResultDto.Transaction.Params = JsonFormatter.ToDiagnosticString(parameters); - } + await FormatTransactionParamsAsync(transactionResultDto.Transaction, transaction.Params); return transactionResultDto; } @@ -304,20 +284,6 @@ private Hash GetHashCombiningTransactionAndStatus(Hash txId, return HashHelper.ComputeFrom(rawBytes); } - private bool IsValidMessage(IMessage message) - { - try - { - JsonFormatter.ToDiagnosticString(message); - } - catch - { - return false; - } - - return true; - } - private async Task GetContractMethodDescriptorAsync(Address contractAddress, string methodName, bool throwException = true) { @@ -331,4 +297,23 @@ private async Task GetContractMethodDescriptorAsync(Address co return await _transactionReadOnlyExecutionService.GetContractMethodDescriptorAsync(chainContext, contractAddress, methodName, throwException); } + + private async Task FormatTransactionParamsAsync(TransactionDto transaction, ByteString @params) + { + var methodDescriptor = + await GetContractMethodDescriptorAsync(Address.FromBase58(transaction.To), transaction.MethodName, false); + + if (methodDescriptor == null) + return; + + try + { + var parameters = methodDescriptor.InputType.Parser.ParseFrom(@params); + transaction.Params = JsonFormatter.ToDiagnosticString(parameters);; + } + catch (Exception exception) + { + Logger.LogError(exception, "Failed to parse transaction params: {params}", transaction.Params); + } + } } \ No newline at end of file diff --git a/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/AEDPoSExtensionDemoTestBase.cs b/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/AEDPoSExtensionDemoTestBase.cs index 368cc56084..a3550cb210 100644 --- a/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/AEDPoSExtensionDemoTestBase.cs +++ b/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/AEDPoSExtensionDemoTestBase.cs @@ -81,8 +81,12 @@ internal void InitialContracts() internal void InitialAcs3Stubs() { foreach (var initialKeyPair in MissionedECKeyPairs.InitialKeyPairs) - ParliamentStubs.Add(GetTester( - ContractAddresses[ParliamentSmartContractAddressNameProvider.Name], initialKeyPair)); + { + var parliamentStub = GetTester( + ContractAddresses[ParliamentSmartContractAddressNameProvider.Name], initialKeyPair); + + ParliamentStubs.Add(parliamentStub); + } } internal async Task diff --git a/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/AEDPoSExtensionTests.cs b/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/AEDPoSExtensionTests.cs index 8840133e89..5f7d8d1c5f 100644 --- a/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/AEDPoSExtensionTests.cs +++ b/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/AEDPoSExtensionTests.cs @@ -4,10 +4,14 @@ using System.Threading.Tasks; using AElf.Contracts.Consensus.AEDPoS; using AElf.Contracts.MultiToken; +using AElf.Contracts.Parliament; using AElf.ContractTestKit; using AElf.ContractTestKit.AEDPoSExtension; +using AElf.CSharp.Core.Extension; using AElf.Kernel; using AElf.Kernel.Consensus; +using AElf.Kernel.Token; +using AElf.Standards.ACS3; using AElf.Types; using Google.Protobuf; using Google.Protobuf.WellKnownTypes; @@ -23,6 +27,7 @@ public class AEDPoSExtensionTests : AEDPoSExtensionDemoTestBase public async Task Demo_Test() { InitialContracts(); + InitialAcs3Stubs(); // Check round information after initialization. { @@ -42,15 +47,31 @@ public async Task Demo_Test() // And this will produce one block with one transaction. // This transaction will call Create method of Token Contract. - var createTokenTransaction = TokenStub.Create.GetTransaction(new CreateInput + await ParliamentStubs.First().Initialize.SendAsync(new InitializeInput { - Symbol = "ELF", - Decimals = 8, - TokenName = "Test", - Issuer = Accounts[0].Address, - IsBurnable = true, - TotalSupply = 1_000_000_000_00000000 + ProposerAuthorityRequired = false, + PrivilegedProposer = Address.FromPublicKey(MissionedECKeyPairs.InitialKeyPairs.First().PublicKey) + }); + var defaultOrganizationAddress = + await ParliamentStubs.First().GetDefaultOrganizationAddress.CallAsync(new Empty()); + await ParliamentReachAnAgreementAsync(new CreateProposalInput + { + ToAddress = ContractAddresses[TokenSmartContractAddressNameProvider.Name], + ContractMethodName = nameof(TokenStub.Create), + Params = new CreateInput + { + Symbol = "ELF", + Decimals = 8, + TokenName = "Test", + Issuer = Accounts[0].Address, + Owner = Accounts[0].Address, + IsBurnable = true, + TotalSupply = 1_000_000_000_00000000 + }.ToByteString(), + ExpiredTime = TimestampHelper.GetUtcNow().AddDays(1), + OrganizationAddress = defaultOrganizationAddress }); + const long issueTokenAmount = 10_0000_00000000; var issueToAddress = Address.FromPublicKey(MissionedECKeyPairs.InitialKeyPairs.First().PublicKey); var issueTokenTransaction = TokenStub.Issue.GetTransaction(new IssueInput @@ -61,12 +82,11 @@ public async Task Demo_Test() }); await BlockMiningService.MineBlockAsync(new List { - createTokenTransaction, issueTokenTransaction }); var createTokenTransactionTrace = - TransactionTraceProvider.GetTransactionTrace(createTokenTransaction.GetHash()); + TransactionTraceProvider.GetTransactionTrace(issueTokenTransaction.GetHash()); createTokenTransactionTrace.ExecutionStatus.ShouldBe(ExecutionStatus.Executed); // Check whether previous Create transaction successfully executed. diff --git a/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/MaximumMinersCountTests.cs b/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/MaximumMinersCountTests.cs index 858b0d890f..69f4d0c7d9 100644 --- a/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/MaximumMinersCountTests.cs +++ b/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/MaximumMinersCountTests.cs @@ -3,11 +3,13 @@ using System.Threading.Tasks; using AElf.Contracts.Parliament; using AElf.ContractTestKit; +using AElf.ContractTestKit.AEDPoSExtension; using AElf.CSharp.Core.Extension; using AElf.Kernel; using AElf.Kernel.Consensus; using AElf.Kernel.Proposal; using AElf.Standards.ACS3; +using AElf.Types; using Google.Protobuf; using Google.Protobuf.WellKnownTypes; using Microsoft.Extensions.DependencyInjection; @@ -30,7 +32,11 @@ public async Task SetMaximumMinersCountTest(int targetMinersCount) await BlockMiningService.MineBlockToNextTermAsync(); InitialAcs3Stubs(); - await ParliamentStubs.First().Initialize.SendAsync(new InitializeInput()); + await ParliamentStubs.First().Initialize.SendAsync(new InitializeInput + { + ProposerAuthorityRequired = false, + PrivilegedProposer = Address.FromPublicKey(MissionedECKeyPairs.InitialKeyPairs.First().PublicKey) + }); var defaultOrganizationAddress = await ParliamentStubs.First().GetDefaultOrganizationAddress.CallAsync(new Empty()); await ParliamentReachAnAgreementAsync(new CreateProposalInput diff --git a/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/SideChainConsensusInformationTest.cs b/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/SideChainConsensusInformationTest.cs index dd1ef0f753..8686d6fbd0 100644 --- a/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/SideChainConsensusInformationTest.cs +++ b/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/SideChainConsensusInformationTest.cs @@ -3,10 +3,15 @@ using System.Threading.Tasks; using AElf.Contracts.Consensus.AEDPoS; using AElf.Contracts.MultiToken; +using AElf.Contracts.Parliament; using AElf.ContractTestKit; using AElf.ContractTestKit.AEDPoSExtension; +using AElf.CSharp.Core.Extension; +using AElf.Kernel; using AElf.Kernel.Consensus; +using AElf.Kernel.Token; using AElf.Standards.ACS10; +using AElf.Standards.ACS3; using AElf.Types; using Google.Protobuf; using Google.Protobuf.WellKnownTypes; @@ -31,6 +36,7 @@ public async Task UpdateInformationFromCrossChainTest() { SetToSideChain(); InitialContracts(); + InitialAcs3Stubs(); var mockedCrossChain = SampleAccount.Accounts.Last(); var mockedCrossChainStub = GetTester( @@ -51,6 +57,11 @@ public async Task UpdateInformationFromCrossChainTest() } }; + await ParliamentStubs.First().Initialize.SendAsync(new InitializeInput + { + ProposerAuthorityRequired = false, + PrivilegedProposer = Address.FromPublicKey(MissionedECKeyPairs.InitialKeyPairs.First().PublicKey) + }); await CreateAndIssueToken("ELF"); await CreateAndIssueToken("READ"); await TokenStub.Transfer.SendAsync(new TransferInput @@ -78,15 +89,26 @@ await mockedCrossChainStub.UpdateInformationFromCrossChain.SendAsync(new BytesVa private async Task CreateAndIssueToken(string symbol) { - var createTokenTransaction = TokenStub.Create.GetTransaction(new CreateInput + var defaultOrganizationAddress = + await ParliamentStubs.First().GetDefaultOrganizationAddress.CallAsync(new Empty()); + await ParliamentReachAnAgreementAsync(new CreateProposalInput { - Symbol = symbol, - Decimals = 8, - TokenName = "Test", - Issuer = Accounts[0].Address, - IsBurnable = true, - TotalSupply = 1_000_000_000_00000000 + ToAddress = ContractAddresses[TokenSmartContractAddressNameProvider.Name], + ContractMethodName = nameof(TokenStub.Create), + Params = new CreateInput + { + Symbol = symbol, + Decimals = 8, + TokenName = "Test", + Issuer = Accounts[0].Address, + Owner = Accounts[0].Address, + IsBurnable = true, + TotalSupply = 1_000_000_000_00000000 + }.ToByteString(), + ExpiredTime = TimestampHelper.GetUtcNow().AddDays(1), + OrganizationAddress = defaultOrganizationAddress }); + const long issueTokenAmount = 10_0000_00000000; var issueToAddress = Address.FromPublicKey(MissionedECKeyPairs.InitialKeyPairs.First().PublicKey); var issueTokenTransaction = TokenStub.Issue.GetTransaction(new IssueInput @@ -97,7 +119,7 @@ private async Task CreateAndIssueToken(string symbol) }); await BlockMiningService.MineBlockAsync(new List { - createTokenTransaction, + // createTokenTransaction, issueTokenTransaction }); } diff --git a/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/SideChainRentFeeTestBase.cs b/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/SideChainRentFeeTestBase.cs index 9d505d89d1..096c82d929 100644 --- a/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/SideChainRentFeeTestBase.cs +++ b/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/SideChainRentFeeTestBase.cs @@ -76,7 +76,7 @@ protected void DeployContracts() TokenSmartContractAddressNameProvider.Name, DefaultSenderKeyPair)); TokenContractStub = GetTokenContractTester(DefaultSenderKeyPair); - AsyncHelper.RunSync(async () => await InitializeTokenAsync()); + // AsyncHelper.RunSync(async () => await InitializeTokenAsync()); ParliamentContractAddress = AsyncHelper.RunSync(() => DeploySystemSmartContract( @@ -141,27 +141,6 @@ internal AEDPoSContractImplContainer.AEDPoSContractImplStub GetConsensusContract return GetTester(ConsensusContractAddress, keyPair); } - private async Task InitializeTokenAsync() - { - const long totalSupply = 100_000_000; - await TokenContractStub.Create.SendAsync(new CreateInput - { - Symbol = NativeTokenSymbol, - Decimals = 2, - IsBurnable = true, - TokenName = "elf token", - TotalSupply = totalSupply, - Issuer = DefaultSender - }); - await TokenContractStub.Issue.SendAsync(new IssueInput - { - Symbol = NativeTokenSymbol, - Amount = totalSupply - 20 * 100_000L, - To = DefaultSender, - Memo = "Issue token to default user." - }); - } - private async Task InitializeParliamentContract() { var initializeResult = await ParliamentContractStub.Initialize.SendAsync(new InitializeInput diff --git a/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/SideChainSideChainRentFeeTest.cs b/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/SideChainSideChainRentFeeTest.cs index e9b625eb32..ef56dc42c5 100644 --- a/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/SideChainSideChainRentFeeTest.cs +++ b/test/AElf.Contracts.AEDPoSExtension.Demo.Tests/SideChainSideChainRentFeeTest.cs @@ -293,12 +293,13 @@ await TokenContractStub.GetBalance.SendAsync(new GetBalanceInput // each tx set private async Task InitialTokenContractAsync(bool issueToken = true) { - await CreateTokenAsync("CPU", ResourceSupply, issueToken); - await CreateTokenAsync("RAM", ResourceSupply, issueToken); - await CreateTokenAsync("DISK", ResourceSupply, issueToken); - await CreateTokenAsync("NET", ResourceSupply, issueToken); var defaultParliamentOrganization = await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); + await CreateTokenAsync("ELF", ResourceSupply, issueToken, defaultParliamentOrganization); + await CreateTokenAsync("CPU", ResourceSupply, issueToken, defaultParliamentOrganization); + await CreateTokenAsync("RAM", ResourceSupply, issueToken, defaultParliamentOrganization); + await CreateTokenAsync("DISK", ResourceSupply, issueToken, defaultParliamentOrganization); + await CreateTokenAsync("NET", ResourceSupply, issueToken, defaultParliamentOrganization); var setSideChainCreatorProposalInput = new InitializeFromParentChainInput { ResourceAmount = @@ -331,18 +332,20 @@ await UpdateSideChainRentalDefaultProposalAsync( nameof(TokenContractImplContainer.TokenContractImplStub.UpdateRental), updateRentalInput); } - private async Task CreateTokenAsync(string symbol, long supply, bool issueToken) + private async Task CreateTokenAsync(string symbol, long supply, bool issueToken, Address organizationAddress) { - await TokenContractStub.Create.SendAsync(new CreateInput - { - Decimals = 8, - Issuer = Creator, - Symbol = symbol, - TotalSupply = supply, - IsBurnable = true, - TokenName = $"{symbol} token." - }); - + await ParliamentReachAnAgreementAsync(TokenContractAddress, organizationAddress, + nameof(TokenContractStub.Create), new CreateInput + { + Decimals = 8, + Issuer = Creator, + Symbol = symbol, + TotalSupply = supply, + IsBurnable = true, + TokenName = $"{symbol} token.", + Owner = Creator + }); + if (!issueToken) return; await TokenContractStub.Issue.SendAsync(new IssueInput diff --git a/test/AElf.Contracts.Association.Tests/AssociationContractTests.cs b/test/AElf.Contracts.Association.Tests/AssociationContractTests.cs index ad6ff414d9..0da11d0fa3 100644 --- a/test/AElf.Contracts.Association.Tests/AssociationContractTests.cs +++ b/test/AElf.Contracts.Association.Tests/AssociationContractTests.cs @@ -943,13 +943,22 @@ public async Task SetMethodFee_Fail_Test() var tokenSymbol = "DLS"; var invalidMethodFees = GetValidMethodFees(); invalidMethodFees.Fees[0].Symbol = tokenSymbol; - await TokenContractStub.Create.SendAsync(new CreateInput - { - Symbol = tokenSymbol, - TokenName = "name", - Issuer = DefaultSender, - TotalSupply = 1000_000 - }); + + var defaultOrganization = await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); + + var proposalId = await CreateFeeProposalAsync(TokenContractAddress, + defaultOrganization, nameof(TokenContractStub.Create), new CreateInput + { + Symbol = tokenSymbol, + TokenName = "name", + Issuer = DefaultSender, + TotalSupply = 1000_000, + Owner = DefaultSender, + }); + + await ApproveWithMinersAsync(proposalId); + await ParliamentContractStub.Release.SendAsync(proposalId); + var ret = await AssociationContractStub.SetMethodFee.SendWithExceptionAsync(invalidMethodFees); ret.TransactionResult.Error.ShouldContain($"Token {tokenSymbol} cannot set as method fee."); } diff --git a/test/AElf.Contracts.Association.Tests/UnitTestTokenContractInitializationProvider.cs b/test/AElf.Contracts.Association.Tests/UnitTestTokenContractInitializationProvider.cs index aa110100ca..99cd591ce1 100644 --- a/test/AElf.Contracts.Association.Tests/UnitTestTokenContractInitializationProvider.cs +++ b/test/AElf.Contracts.Association.Tests/UnitTestTokenContractInitializationProvider.cs @@ -38,6 +38,7 @@ public override List GetInitializeMethodList(b { Decimals = _economicOptions.Decimals, Issuer = address, + Owner = address, IsBurnable = _economicOptions.IsBurnable, Symbol = _economicOptions.Symbol, TokenName = _economicOptions.TokenName, diff --git a/test/AElf.Contracts.Configuration.Tests/ConfigurationContractTest.cs b/test/AElf.Contracts.Configuration.Tests/ConfigurationContractTest.cs index 2fb4b58217..f0876ac70f 100644 --- a/test/AElf.Contracts.Configuration.Tests/ConfigurationContractTest.cs +++ b/test/AElf.Contracts.Configuration.Tests/ConfigurationContractTest.cs @@ -345,13 +345,15 @@ public async Task SetMethodFee_Failed_Test() // token is not profitable { var tokenSymbol = "DLS"; + await CreateTokenAsync(tokenSymbol); await Tester.ExecuteContractWithMiningAsync(TokenContractAddress, nameof(TokenContractContainer.TokenContractStub.Create), new CreateInput { Symbol = tokenSymbol, TokenName = "name", Issuer = TokenContractAddress, - TotalSupply = 1000_000 + TotalSupply = 1000_000, + Owner = TokenContractAddress }); var result = await Tester.ExecuteContractWithMiningAsync(ConfigurationContractAddress, diff --git a/test/AElf.Contracts.Configuration.Tests/ConfigurationContractTestBase.cs b/test/AElf.Contracts.Configuration.Tests/ConfigurationContractTestBase.cs index aa42ed0d40..e15bd44ad2 100644 --- a/test/AElf.Contracts.Configuration.Tests/ConfigurationContractTestBase.cs +++ b/test/AElf.Contracts.Configuration.Tests/ConfigurationContractTestBase.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using AElf.Contracts.Configuration; +using AElf.Contracts.MultiToken; using AElf.Contracts.Parliament; using AElf.Contracts.TestBase; using AElf.Cryptography.ECDSA; @@ -168,4 +169,52 @@ protected async Task GetMethodFeeController(Address configuration new Empty()); return AuthorityInfo.Parser.ParseFrom(methodFeeControllerByteString); } + + protected async Task CreateTokenAsync(string symbol) + { + await Tester.ExecuteContractWithMiningAsync(TokenContractAddress, + nameof(TokenContractContainer.TokenContractStub.Create), + new CreateInput + { + Symbol = "SEED-0", + Decimals = 0, + IsBurnable = true, + TokenName = "seed Collection", + TotalSupply = 1, + Issuer = Tester.GetCallOwnerAddress(), + ExternalInfo = new ExternalInfo(), + Owner = Tester.GetCallOwnerAddress() + }); + await Tester.ExecuteContractWithMiningAsync(TokenContractAddress, + nameof(TokenContractContainer.TokenContractStub.Create), + new CreateInput + { + Symbol = "SEED-1", + Decimals = 0, + IsBurnable = true, + TokenName = "seed", + TotalSupply = 1, + Issuer = Tester.GetCallOwnerAddress(), + ExternalInfo = new ExternalInfo + { + Value = + { + { "__seed_owned_symbol", symbol }, + { "__seed_exp_time", TimestampHelper.GetUtcNow().AddDays(1).Seconds.ToString() } + } + }, + LockWhiteList = { TokenContractAddress }, + Owner = Tester.GetCallOwnerAddress() + }); + + await Tester.ExecuteContractWithMiningAsync(TokenContractAddress, + nameof(TokenContractContainer.TokenContractStub.Issue), + new IssueInput + { + Symbol = "SEED-1", + Amount = 1, + To = Tester.GetCallOwnerAddress(), + Memo = "test" + }); + } } \ No newline at end of file diff --git a/test/AElf.Contracts.Consensus.AEDPoS.Tests/AEDPoSContractTestBase.cs b/test/AElf.Contracts.Consensus.AEDPoS.Tests/AEDPoSContractTestBase.cs index 6cc536f844..161e9ae5ba 100644 --- a/test/AElf.Contracts.Consensus.AEDPoS.Tests/AEDPoSContractTestBase.cs +++ b/test/AElf.Contracts.Consensus.AEDPoS.Tests/AEDPoSContractTestBase.cs @@ -153,8 +153,9 @@ protected async Task BootMinerChangeRoundAsync(long nextRoundNumber = 2) .AddMilliseconds( ((long)currentRound.TotalMilliseconds(AEDPoSContractTestConstants.MiningInterval)).Mul( nextRoundNumber.Sub(1))); + var randomNumber = await GenerateRandomProofAsync(BootMinerKeyPair); currentRound.GenerateNextRoundInformation(expectedStartTime.ToTimestamp(), BlockchainStartTimestamp, - out var nextRound); + ByteString.CopyFrom(randomNumber), out var nextRound); await AEDPoSContractStub.NextRound.SendAsync(nextRound); } @@ -167,7 +168,7 @@ protected async Task CreateProposalAsync(Address contractAddress, Address ContractMethodName = methodName, ExpiredTime = TimestampHelper.GetUtcNow().AddHours(1), Params = input.ToByteString(), - ToAddress = ConsensusContractAddress + ToAddress = contractAddress }; var createResult = await ParliamentContractStub.CreateProposal.SendAsync(proposal); diff --git a/test/AElf.Contracts.Consensus.AEDPoS.Tests/AElf.Contracts.Consensus.AEDPoS.Tests.csproj b/test/AElf.Contracts.Consensus.AEDPoS.Tests/AElf.Contracts.Consensus.AEDPoS.Tests.csproj index 0a1b7fb34b..f9397a21b3 100644 --- a/test/AElf.Contracts.Consensus.AEDPoS.Tests/AElf.Contracts.Consensus.AEDPoS.Tests.csproj +++ b/test/AElf.Contracts.Consensus.AEDPoS.Tests/AElf.Contracts.Consensus.AEDPoS.Tests.csproj @@ -17,7 +17,7 @@ runtime; build; native; contentfiles; analyzers - + all @@ -29,10 +29,10 @@ - - - - + + + + false Contract @@ -43,7 +43,12 @@ Contract PreserveNewest - + + false + Contract + PreserveNewest + + @@ -116,5 +121,5 @@ - + diff --git a/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/ACS10ImplTest.cs b/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/ACS10ImplTest.cs index 17d18c9803..80a823f031 100644 --- a/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/ACS10ImplTest.cs +++ b/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/ACS10ImplTest.cs @@ -2,6 +2,7 @@ using AElf.Contracts.MultiToken; using AElf.Standards.ACS10; using AElf.Types; +using Google.Protobuf.WellKnownTypes; using Shouldly; using Xunit; @@ -14,14 +15,9 @@ public async Task Consensus_Donate_With_Invalid_Token_Test() { var tokenSymbol = "SEP"; + await CreateTokenAsync(tokenSymbol); + // Donate with token which is not profitable will fail - await TokenContractStub.Create.SendAsync(new CreateInput - { - Symbol = tokenSymbol, - TokenName = "name", - TotalSupply = 1000_000_000, - Issuer = BootMinerAddress - }); var issueAmount = 1000_000; await TokenContractStub.Issue.SendAsync(new IssueInput { @@ -48,4 +44,23 @@ await TokenContractStub.Issue.SendAsync(new IssueInput }); balanceAfterDonate.Balance.ShouldBe(issueAmount); } + + private async Task CreateTokenAsync(string symbol) + { + var defaultOrganization = await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); + + const string proposalCreationMethodName = nameof(TokenContractStub.Create); + var proposalId = await CreateProposalAsync(TokenContractAddress, defaultOrganization, + proposalCreationMethodName, new CreateInput + { + Symbol = symbol, + TokenName = "name", + TotalSupply = 1000_000_000, + Issuer = BootMinerAddress, + Owner = BootMinerAddress + }); + await ApproveWithMinersAsync(proposalId); + var result = await ParliamentContractStub.Release.SendAsync(proposalId); + var t = "t"; + } } \ No newline at end of file diff --git a/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/ACS1ImplTest.cs b/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/ACS1ImplTest.cs index 058f3afcd9..489f4d80ad 100644 --- a/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/ACS1ImplTest.cs +++ b/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/ACS1ImplTest.cs @@ -31,7 +31,7 @@ await ParliamentContractStub.CreateOrganization.SendAsync( const string proposalCreationMethodName = nameof(AEDPoSContractImplContainer.AEDPoSContractImplStub.ChangeMethodFeeController); - var proposalId = await CreateProposalAsync(methodFeeController.ContractAddress, + var proposalId = await CreateProposalAsync(ConsensusContractAddress, methodFeeController.OwnerAddress, proposalCreationMethodName, new AuthorityInfo { OwnerAddress = organizationAddress, diff --git a/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/ACS4ImplTest.cs b/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/ACS4ImplTest.cs index f567de3d69..8e1d56ce9c 100644 --- a/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/ACS4ImplTest.cs +++ b/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/ACS4ImplTest.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Threading.Tasks; using AElf.CSharp.Core; +using AElf.Kernel; using AElf.Standards.ACS4; using AElf.TestBase; using AElf.Types; @@ -74,10 +75,12 @@ private async Task AEDPoSContract_GenerateConsensusTransactions }); var triggerForCommand = - TriggerInformationProvider.GetTriggerInformationForConsensusTransactions( + TriggerInformationProvider.GetTriggerInformationForConsensusTransactions(new ChainContext(), consensusCommand.ToBytesValue()); + var trigger = AElfConsensusTriggerInformation.Parser.ParseFrom(triggerForCommand.Value); + trigger.RandomNumber = ByteString.CopyFrom(await GenerateRandomProofAsync(BootMinerKeyPair)); - var transactionList = await AEDPoSContractStub.GenerateConsensusTransactions.CallAsync(triggerForCommand); + var transactionList = await AEDPoSContractStub.GenerateConsensusTransactions.CallAsync(trigger.ToBytesValue()); transactionList.Transactions.Count.ShouldBe(1); transactionList.Transactions[0].MethodName.ShouldBe(nameof(AEDPoSContractStub.UpdateValue)); @@ -175,9 +178,11 @@ private async Task AEDPoSContract_GenerateConsensusTransactions }); var triggerForCommand = TriggerInformationProvider - .GetTriggerInformationForConsensusTransactions(consensusCommand.ToBytesValue()); + .GetTriggerInformationForConsensusTransactions(new ChainContext(), consensusCommand.ToBytesValue()); + var trigger = AElfConsensusTriggerInformation.Parser.ParseFrom(triggerForCommand.Value); + trigger.RandomNumber = ByteString.CopyFrom(await GenerateRandomProofAsync(usingKeyPair)); - var transactionList = await AEDPoSContractStub.GenerateConsensusTransactions.CallAsync(triggerForCommand); + var transactionList = await AEDPoSContractStub.GenerateConsensusTransactions.CallAsync(trigger.ToBytesValue()); transactionList.Transactions.Count.ShouldBe(1); transactionList.Transactions[0].MethodName.ShouldBe(nameof(AEDPoSContractStub.UpdateValue)); @@ -252,8 +257,10 @@ public async Task AEDPoSContract_GetInformationToUpdateConsensus_FirstRound_Extr var triggerForCommand = TriggerInformationProvider .GetTriggerInformationForBlockHeaderExtraData(consensusCommand.ToBytesValue()); + var trigger = AElfConsensusTriggerInformation.Parser.ParseFrom(triggerForCommand.Value); + trigger.RandomNumber = ByteString.CopyFrom(await GenerateRandomProofAsync(usingKeyPair)); - var extraDataBytes = await AEDPoSContractStub.GetConsensusExtraData.CallAsync(triggerForCommand); + var extraDataBytes = await AEDPoSContractStub.GetConsensusExtraData.CallAsync(trigger.ToBytesValue()); var extraData = extraDataBytes.ToConsensusHeaderInformation(); @@ -275,9 +282,11 @@ private async Task }); var triggerForCommand = TriggerInformationProvider - .GetTriggerInformationForConsensusTransactions(consensusCommand.ToBytesValue()); - - var transactionList = await AEDPoSContractStub.GenerateConsensusTransactions.CallAsync(triggerForCommand); + .GetTriggerInformationForConsensusTransactions(new ChainContext(), consensusCommand.ToBytesValue()); + var trigger = AElfConsensusTriggerInformation.Parser.ParseFrom(triggerForCommand.Value); + trigger.RandomNumber = ByteString.CopyFrom(await GenerateRandomProofAsync(usingKeyPair)); + + var transactionList = await AEDPoSContractStub.GenerateConsensusTransactions.CallAsync(trigger.ToBytesValue()); transactionList.Transactions.Count.ShouldBe(1); transactionList.Transactions[0].MethodName.ShouldBe(nameof(AEDPoSContractStub.NextRound)); @@ -298,7 +307,7 @@ public async Task AEDPoSContract_FirstRound_Terminate_Test() .Div(1000) }); - var nextRound = new Round(); + var nextRound = new NextRoundInput(); nextRound.MergeFrom(transaction.Params); await AEDPoSContractStub.NextRound.SendAsync(nextRound); @@ -323,10 +332,13 @@ public async Task AEDPoSContract_ConsensusTransactionValidation_Test() validateBeforeResult.Success.ShouldBeTrue(); var roundInfo = await AEDPoSContractStub.GetCurrentRoundInformation.CallAsync(new Empty()); - roundInfo.RoundNumber++; - roundInfo.IsMinerListJustChanged = false; - roundInfo.TermNumber++; - var transactionResult = await AEDPoSContractStub.NextRound.SendAsync(roundInfo); + var nextRoundInput = NextRoundInput.Parser.ParseFrom(roundInfo.ToByteArray()); + nextRoundInput.RoundNumber++; + nextRoundInput.IsMinerListJustChanged = false; + nextRoundInput.TermNumber++; + var randomNumber = await GenerateRandomProofAsync(BootMinerKeyPair); + nextRoundInput.RandomNumber = ByteString.CopyFrom(randomNumber); + var transactionResult = await AEDPoSContractStub.NextRound.SendAsync(nextRoundInput); transactionResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); var validateAfterResult = @@ -402,10 +414,12 @@ public async Task AEDPoSContract_GenerateConsensusTransaction_TinyBlock_Test() }); var triggerForCommand = TriggerInformationProvider - .GetTriggerInformationForConsensusTransactions(consensusCommand.ToBytesValue()); + .GetTriggerInformationForConsensusTransactions(new ChainContext(), consensusCommand.ToBytesValue()); + var trigger = AElfConsensusTriggerInformation.Parser.ParseFrom(triggerForCommand.Value); + trigger.RandomNumber = ByteString.CopyFrom(await GenerateRandomProofAsync(usingKeyPair)); var transactionList = - await AEDPoSContractStub.GenerateConsensusTransactions.CallAsync(triggerForCommand); + await AEDPoSContractStub.GenerateConsensusTransactions.CallAsync(trigger.ToBytesValue()); transactionList.Transactions.Count.ShouldBe(1); transactionList.Transactions[0].MethodName @@ -433,10 +447,12 @@ public async Task AEDPoSContract_GenerateConsensusTransaction_NextTerm_Test() }); var triggerForCommand = TriggerInformationProvider - .GetTriggerInformationForConsensusTransactions(consensusCommand.ToBytesValue()); + .GetTriggerInformationForConsensusTransactions(new ChainContext(), consensusCommand.ToBytesValue()); + var trigger = AElfConsensusTriggerInformation.Parser.ParseFrom(triggerForCommand.Value); + trigger.RandomNumber = ByteString.CopyFrom(await GenerateRandomProofAsync(usingKeyPair)); var transactionList = - await AEDPoSContractStub.GenerateConsensusTransactions.CallAsync(triggerForCommand); + await AEDPoSContractStub.GenerateConsensusTransactions.CallAsync(trigger.ToBytesValue()); transactionList.Transactions.Count.ShouldBe(1); transactionList.Transactions[0].MethodName.ShouldBe(nameof(AEDPoSContractStub.NextTerm)); diff --git a/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/MinersCountTest.cs b/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/MinersCountTest.cs index bbc2310b9f..f053759f68 100644 --- a/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/MinersCountTest.cs +++ b/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/MinersCountTest.cs @@ -52,6 +52,7 @@ public async Task AEDPoSContract_ChangeMinersCount_Test() voteResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); } + byte[] randomNumber; foreach (var minerInRound in firstRound.RealTimeMinersInformation.Values.OrderBy(m => m.Order)) { var currentKeyPair = InitialCoreDataCenterKeyPairs.First(p => p.PublicKey.ToHex() == minerInRound.Pubkey); @@ -65,8 +66,11 @@ public async Task AEDPoSContract_ChangeMinersCount_Test() (await AEDPoSContractStub.GetConsensusExtraData.CallAsync(triggers[minerInRound.Pubkey] .ToBytesValue())).ToConsensusHeaderInformation(); + randomNumber = await GenerateRandomProofAsync(currentKeyPair); // Update consensus information. - var toUpdate = headerInformation.Round.ExtractInformationToUpdateConsensus(minerInRound.Pubkey); + var toUpdate = + headerInformation.Round.ExtractInformationToUpdateConsensus(minerInRound.Pubkey, + ByteString.CopyFrom(randomNumber)); await tester.UpdateValue.SendAsync(toUpdate); } @@ -80,7 +84,10 @@ public async Task AEDPoSContract_ChangeMinersCount_Test() Pubkey = ByteString.CopyFrom(BootMinerKeyPair.PublicKey) }.ToBytesValue())).ToConsensusHeaderInformation(); - await AEDPoSContractStub.NextRound.SendAsync(nextTermInformation.Round); + var nextRoundInput = NextRoundInput.Parser.ParseFrom(nextTermInformation.Round.ToByteArray()); + randomNumber = await GenerateRandomProofAsync(BootMinerKeyPair); + nextRoundInput.RandomNumber = ByteString.CopyFrom(randomNumber); + await AEDPoSContractStub.NextRound.SendAsync(nextRoundInput); changeTermTime = BlockchainStartTimestamp.ToDateTime().AddMinutes(termIntervalMin).AddSeconds(10); BlockTimeProvider.SetBlockTime(changeTermTime.ToTimestamp()); @@ -91,7 +98,10 @@ public async Task AEDPoSContract_ChangeMinersCount_Test() Pubkey = ByteString.CopyFrom(BootMinerKeyPair.PublicKey) }.ToBytesValue())).ToConsensusHeaderInformation(); - var transactionResult = await AEDPoSContractStub.NextTerm.SendAsync(nextTermInformation.Round); + var nextTermInput = NextTermInput.Parser.ParseFrom(nextTermInformation.Round.ToByteArray()); + randomNumber = await GenerateRandomProofAsync(BootMinerKeyPair); + nextTermInput.RandomNumber = ByteString.CopyFrom(randomNumber); + var transactionResult = await AEDPoSContractStub.NextTerm.SendAsync(nextTermInput); transactionResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); var newMinerStub = GetAEDPoSContractStub(ValidationDataCenterKeyPairs[0]); @@ -101,8 +111,8 @@ public async Task AEDPoSContract_ChangeMinersCount_Test() { var currentRound = await newMinerStub.GetCurrentRoundInformation.CallAsync(new Empty()); var firstPubKey = currentRound.RealTimeMinersInformation.Keys.First(); - newMinerStub = - GetAEDPoSContractStub(ValidationDataCenterKeyPairs.First(o => o.PublicKey.ToHex() == firstPubKey)); + var keypair = ValidationDataCenterKeyPairs.First(o => o.PublicKey.ToHex() == firstPubKey); + newMinerStub = GetAEDPoSContractStub(keypair); minerCount = currentRound.RealTimeMinersInformation.Count; Assert.Equal(AEDPoSContractTestConstants.SupposedMinersCount.Add(termCount.Mul(2)), minerCount); @@ -117,8 +127,10 @@ public async Task AEDPoSContract_ChangeMinersCount_Test() Pubkey = ByteStringHelper.FromHexString(currentRound.RealTimeMinersInformation.ElementAt(0).Value .Pubkey) }.ToBytesValue())).ToConsensusHeaderInformation(); - - await newMinerStub.NextTerm.SendAsync(nextRoundInformation.Round); + nextTermInput = NextTermInput.Parser.ParseFrom(nextRoundInformation.Round.ToByteArray()); + randomNumber = await GenerateRandomProofAsync(keypair); + nextTermInput.RandomNumber = ByteString.CopyFrom(randomNumber); + await newMinerStub.NextTerm.SendAsync(nextTermInput); termCount++; } } diff --git a/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/MiningProcessTest.cs b/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/MiningProcessTest.cs index cb9dfcf1ff..43570af8f2 100644 --- a/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/MiningProcessTest.cs +++ b/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/MiningProcessTest.cs @@ -44,6 +44,8 @@ await voter.Vote.SendAsync(new VoteMinerInput EndTimestamp = TimestampHelper.GetUtcNow().AddDays(100) }); + var randomHash = Hash.Empty.ToByteArray(); + byte[] randomNumber; foreach (var minerInRound in firstRound.RealTimeMinersInformation.Values.OrderBy(m => m.Order)) { var currentKeyPair = @@ -57,9 +59,9 @@ await voter.Vote.SendAsync(new VoteMinerInput var headerInformation = (await AEDPoSContractStub.GetConsensusExtraData.CallAsync(triggers[minerInRound.Pubkey] .ToBytesValue())).ToConsensusHeaderInformation(); - + randomNumber = await GenerateRandomProofAsync(currentKeyPair); // Update consensus information. - var toUpdate = headerInformation.Round.ExtractInformationToUpdateConsensus(minerInRound.Pubkey); + var toUpdate = headerInformation.Round.ExtractInformationToUpdateConsensus(minerInRound.Pubkey, ByteString.CopyFrom(randomNumber)); await tester.UpdateValue.SendAsync(toUpdate); } @@ -73,24 +75,27 @@ await voter.Vote.SendAsync(new VoteMinerInput Behaviour = AElfConsensusBehaviour.NextTerm, Pubkey = ByteString.CopyFrom(BootMinerKeyPair.PublicKey) }.ToBytesValue())).ToConsensusHeaderInformation(); - - await AEDPoSContractStub.NextTerm.SendAsync(nextTermInformation.Round); + var nextTermInput = NextTermInput.Parser.ParseFrom(nextTermInformation.Round.ToByteArray()); + randomNumber = await GenerateRandomProofAsync(BootMinerKeyPair); + nextTermInput.RandomNumber = ByteString.CopyFrom(randomNumber); + await AEDPoSContractStub.NextTerm.SendAsync(nextTermInput); // First candidate cheat others with in value. var oneCandidate = GetAEDPoSContractStub(ValidationDataCenterKeyPairs[0]); var anotherCandidate = GetAEDPoSContractStub(ValidationDataCenterKeyPairs[1]); - var randomHash = HashHelper.ComputeFrom("hash5"); + var inValue = HashHelper.ComputeFrom("hash5"); var informationOfSecondRound = (await AEDPoSContractStub.GetConsensusExtraData.CallAsync( new AElfConsensusTriggerInformation { Behaviour = AElfConsensusBehaviour.UpdateValue, PreviousInValue = Hash.Empty, - InValue = randomHash, + InValue = inValue, Pubkey = ByteString.CopyFrom(ValidationDataCenterKeyPairs[0].PublicKey) }.ToBytesValue())).ToConsensusHeaderInformation(); + randomNumber = await GenerateRandomProofAsync(ValidationDataCenterKeyPairs[0]); var updateResult = await oneCandidate.UpdateValue.SendAsync( informationOfSecondRound.Round.ExtractInformationToUpdateConsensus(ValidationDataCenterKeyPairs[0] - .PublicKey.ToHex())); + .PublicKey.ToHex(), ByteString.CopyFrom(randomNumber))); var thirdRoundStartTime = changeTermTime.AddMinutes(AEDPoSContractTestConstants.PeriodSeconds + 2); BlockTimeProvider.SetBlockTime(thirdRoundStartTime.ToTimestamp()); @@ -100,20 +105,23 @@ await voter.Vote.SendAsync(new VoteMinerInput Behaviour = AElfConsensusBehaviour.NextRound, Pubkey = ByteString.CopyFrom(ValidationDataCenterKeyPairs[0].PublicKey) }.ToBytesValue())).ToConsensusHeaderInformation().Round; - - await oneCandidate.NextRound.SendAsync(thirdRound); + var nextRoundInput = NextRoundInput.Parser.ParseFrom(thirdRound.ToByteArray()); + randomNumber = await GenerateRandomProofAsync(ValidationDataCenterKeyPairs[0]); + nextRoundInput.RandomNumber = ByteString.CopyFrom(randomNumber); + await oneCandidate.NextRound.SendAsync(nextRoundInput); var cheatInformation = (await AEDPoSContractStub.GetConsensusExtraData.CallAsync( new AElfConsensusTriggerInformation { Behaviour = AElfConsensusBehaviour.UpdateValue, - PreviousInValue = HashHelper.ComputeFrom(randomHash), // Not same as before. + PreviousInValue = HashHelper.ComputeFrom(inValue), // Not same as before. InValue = HashHelper.ComputeFrom("InValue"), // Don't care this value in current test case. Pubkey = ByteString.CopyFrom(ValidationDataCenterKeyPairs[0].PublicKey) }.ToBytesValue())).ToConsensusHeaderInformation(); + randomNumber = await GenerateRandomProofAsync(ValidationDataCenterKeyPairs[0]); await oneCandidate.UpdateValue.SendAsync( cheatInformation.Round.ExtractInformationToUpdateConsensus(ValidationDataCenterKeyPairs[0].PublicKey - .ToHex())); + .ToHex(), ByteString.CopyFrom(randomNumber))); } [Fact] @@ -131,7 +139,8 @@ public async Task Update_TinyBlockInformation_Test() { RoundId = roundInfo.RoundId, ProducedBlocks = 4, - ActualMiningTime = BlockTimeProvider.GetBlockTime() + ActualMiningTime = BlockTimeProvider.GetBlockTime(), + RandomNumber = ByteString.CopyFrom(await GenerateRandomProofAsync(BootMinerKeyPair)) }; var transactionResult = await AEDPoSContractStub.UpdateTinyBlockInformation.SendAsync(input); transactionResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); diff --git a/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/ViewTests.cs b/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/ViewTests.cs index b75b324ccf..d23ca5613a 100644 --- a/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/ViewTests.cs +++ b/test/AElf.Contracts.Consensus.AEDPoS.Tests/BVT/ViewTests.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Threading.Tasks; +using AElf.Cryptography; using AElf.Types; using Google.Protobuf; using Google.Protobuf.WellKnownTypes; @@ -27,7 +28,10 @@ public async Task Query_RoundInformation_Test() Pubkey = ByteString.CopyFrom(BootMinerKeyPair.PublicKey) }.ToBytesValue())).ToConsensusHeaderInformation(); - var transactionResult = await AEDPoSContractStub.NextRound.SendAsync(nextTermInformation.Round); + var nextRoundInput = NextRoundInput.Parser.ParseFrom(nextTermInformation.Round.ToByteArray()); + var randomNumber = await GenerateRandomProofAsync(BootMinerKeyPair); + nextRoundInput.RandomNumber = ByteString.CopyFrom(randomNumber); + var transactionResult = await AEDPoSContractStub.NextRound.SendAsync(nextRoundInput); transactionResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); roundNumber = await AEDPoSContractStub.GetCurrentRoundNumber.CallAsync(new Empty()); diff --git a/test/AElf.Contracts.Consensus.AEDPoS.Tests/Types/Round.cs b/test/AElf.Contracts.Consensus.AEDPoS.Tests/Types/Round.cs index f0e6cee28e..6f6acaf2bf 100644 --- a/test/AElf.Contracts.Consensus.AEDPoS.Tests/Types/Round.cs +++ b/test/AElf.Contracts.Consensus.AEDPoS.Tests/Types/Round.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using AElf.Types; +using Google.Protobuf; using Google.Protobuf.WellKnownTypes; namespace AElf.Contracts.Consensus.AEDPoS; @@ -85,8 +86,9 @@ public int TotalMilliseconds(int miningInterval = 0) /// will record this purpose to their FinalOrderOfNextRound field. ///
/// + /// /// - public UpdateValueInput ExtractInformationToUpdateConsensus(string pubkey) + public UpdateValueInput ExtractInformationToUpdateConsensus(string pubkey, ByteString randomNumber) { if (!RealTimeMinersInformation.ContainsKey(pubkey)) return null; @@ -117,7 +119,8 @@ public UpdateValueInput ExtractInformationToUpdateConsensus(string pubkey) TuneOrderInformation = { tuneOrderInformation }, EncryptedPieces = { minerInRound.EncryptedPieces }, DecryptedPieces = { decryptedPreviousInValues }, - MinersPreviousInValues = { minersPreviousInValues } + MinersPreviousInValues = { minersPreviousInValues }, + RandomNumber = randomNumber }; } diff --git a/test/AElf.Contracts.Consensus.AEDPoS.Tests/Types/Round_Generation.cs b/test/AElf.Contracts.Consensus.AEDPoS.Tests/Types/Round_Generation.cs index 34830558d0..6965e4c874 100644 --- a/test/AElf.Contracts.Consensus.AEDPoS.Tests/Types/Round_Generation.cs +++ b/test/AElf.Contracts.Consensus.AEDPoS.Tests/Types/Round_Generation.cs @@ -1,16 +1,18 @@ using System.Collections.Generic; using System.Linq; using AElf.CSharp.Core; +using AElf.Types; +using Google.Protobuf; using Google.Protobuf.WellKnownTypes; namespace AElf.Contracts.Consensus.AEDPoS; internal partial class Round { - public bool GenerateNextRoundInformation(Timestamp currentBlockTimestamp, Timestamp blockchainStartTimestamp, - out Round nextRound) + public bool GenerateNextRoundInformation(Timestamp currentBlockTimestamp, Timestamp blockchainStartTimestamp, ByteString randomNumber, + out NextRoundInput nextRound) { - nextRound = new Round(); + nextRound = new NextRoundInput(); var minersMinedCurrentRound = GetMinedMiners(); var minersNotMinedCurrentRound = GetNotMinedMiners(); @@ -65,7 +67,8 @@ public bool GenerateNextRoundInformation(Timestamp currentBlockTimestamp, Timest nextRound.RealTimeMinersInformation.Values.First().IsExtraBlockProducer = true; else expectedExtraBlockProducer.IsExtraBlockProducer = true; - + nextRound.RandomNumber = randomNumber; + return true; } diff --git a/test/AElf.Contracts.CrossChain.Tests/CrossChainContractTestBase.cs b/test/AElf.Contracts.CrossChain.Tests/CrossChainContractTestBase.cs index 11e08408eb..dd09f11b48 100644 --- a/test/AElf.Contracts.CrossChain.Tests/CrossChainContractTestBase.cs +++ b/test/AElf.Contracts.CrossChain.Tests/CrossChainContractTestBase.cs @@ -160,17 +160,25 @@ private async Task InitializeTokenAsync() { const string symbol = "ELF"; const long totalSupply = 100_000_000; - await MineAsync(new List - { - TokenContractStub.Create.GetTransaction(new CreateInput + + var parliamentOrganizationAddress = + (await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty())); + var approveProposalId = await CreateParliamentProposalAsync(nameof(TokenContractStub.Create), + parliamentOrganizationAddress, new CreateInput { Symbol = symbol, Decimals = 2, IsBurnable = true, TokenName = "elf token", TotalSupply = totalSupply, - Issuer = DefaultSender - }), + Issuer = DefaultSender, + Owner = DefaultSender + }, TokenContractAddress); + await ApproveWithMinersAsync(approveProposalId); + await ParliamentContractStub.Release.SendAsync(approveProposalId); + + await MineAsync(new List + { TokenContractStub.Issue.GetTransaction(new IssueInput { Symbol = symbol, diff --git a/test/AElf.Contracts.Economic.TestBase/AElf.Contracts.Economic.TestBase.csproj b/test/AElf.Contracts.Economic.TestBase/AElf.Contracts.Economic.TestBase.csproj index 95287fb207..c0b204fe14 100644 --- a/test/AElf.Contracts.Economic.TestBase/AElf.Contracts.Economic.TestBase.csproj +++ b/test/AElf.Contracts.Economic.TestBase/AElf.Contracts.Economic.TestBase.csproj @@ -13,16 +13,16 @@ runtime; build; native; contentfiles; analyzers - + - all - runtime; build; native; contentfiles; analyzers - + all + runtime; build; native; contentfiles; analyzers + - all - runtime; build; native; contentfiles; analyzers - + all + runtime; build; native; contentfiles; analyzers + @@ -71,9 +71,9 @@ Contract PreserveNewest - - - + + + false Contract @@ -99,8 +99,13 @@ Contract PreserveNewest - - + + false + Contract + PreserveNewest + + + @@ -179,8 +184,11 @@ Protobuf\Proto\parliament_contract_impl.proto + + Protobuf\Proto\test_virtual_address_contract.proto + - + diff --git a/test/AElf.Contracts.Economic.TestBase/Contracts.cs b/test/AElf.Contracts.Economic.TestBase/Contracts.cs index 3e32bdd1cd..2bd259d94a 100644 --- a/test/AElf.Contracts.Economic.TestBase/Contracts.cs +++ b/test/AElf.Contracts.Economic.TestBase/Contracts.cs @@ -26,7 +26,8 @@ public enum TestContracts BasicSecurity, BasicUpdate, MethodCallThreshold, - ResourceSpender + ResourceSpender, + VirtualAddress } public enum ProfitType diff --git a/test/AElf.Contracts.Economic.TestBase/ContractsPreparation.cs b/test/AElf.Contracts.Economic.TestBase/ContractsPreparation.cs index 04437a46a8..cde20e76c9 100644 --- a/test/AElf.Contracts.Economic.TestBase/ContractsPreparation.cs +++ b/test/AElf.Contracts.Economic.TestBase/ContractsPreparation.cs @@ -10,6 +10,7 @@ using AElf.Contracts.Parliament; using AElf.Contracts.Profit; using AElf.Contracts.TestContract.MethodCallThreshold; +using AElf.Contracts.TestContract.VirtualAddress; using AElf.Contracts.TestContract.TransactionFeeCharging; using AElf.Contracts.TokenConverter; using AElf.Contracts.Treasury; @@ -102,6 +103,9 @@ public partial class EconomicContractsTestBase protected Address ConfigurationAddress => GetOrDeployContract(Contracts.Configuration, ref _configurationAddress); + + private Address _virtualAddressContractAddress; + protected Address VirtualAddressContractAddress => GetOrDeployContract(TestContracts.VirtualAddress, ref _virtualAddressContractAddress); #endregion @@ -144,6 +148,9 @@ internal TransactionFeeChargingContractContainer.TransactionFeeChargingContractS internal ConfigurationContainer.ConfigurationStub ConfigurationStub => GetConfigurationContractTester(BootMinerKeyPair); + + internal VirtualAddressContractContainer.VirtualAddressContractStub VirtualAddressContractStub => + GetVirtualAddressContractTester(BootMinerKeyPair); #endregion @@ -223,6 +230,11 @@ internal ConfigurationContainer.ConfigurationStub GetConfigurationContractTester { return GetTester(ConfigurationAddress, keyPair); } + + internal VirtualAddressContractContainer.VirtualAddressContractStub GetVirtualAddressContractTester(ECKeyPair keyPair) + { + return GetTester(VirtualAddressContractAddress, keyPair); + } #endregion @@ -308,6 +320,7 @@ protected void DeployAllContracts() _ = TokenContractAddress; _ = TokenHolderContractAddress; _ = AssociationContractAddress; + _ = VirtualAddressContractAddress; } #endregion @@ -500,7 +513,7 @@ protected async Task InitializeAElfConsensus() new MinerList { Pubkeys = { InitialCoreDataCenterKeyPairs.Select(p => ByteString.CopyFrom(p.PublicKey)) } - }.GenerateFirstRoundOfNewTerm(EconomicContractsTestConstants.MiningInterval, StartTimestamp)); + }.GenerateFirstRound(EconomicContractsTestConstants.MiningInterval, StartTimestamp)); CheckResult(result.TransactionResult); } } @@ -509,12 +522,35 @@ protected async Task InitializeTransactionFeeChargingContract() { await ExecuteProposalForParliamentTransaction(TokenContractAddress, nameof(TokenContractStub.AddAddressToCreateTokenWhiteList), TransactionFeeChargingContractAddress); - var result = await TransactionFeeChargingContractStub.InitializeTransactionFeeChargingContract.SendAsync( - new InitializeTransactionFeeChargingContractInput + + await ExecuteProposalForParliamentTransaction(TokenContractAddress, nameof(TokenContractStub.Create), new CreateInput + { + Symbol = EconomicContractsTestConstants.TransactionFeeChargingContractTokenSymbol, + TokenName = "Token of Transaction Fee Charging Contract", + Decimals = 2, + Issuer = BootMinerAddress, + IsBurnable = true, + TotalSupply = 1_000_000_000, + LockWhiteList = { - Symbol = EconomicContractsTestConstants.TransactionFeeChargingContractTokenSymbol - }); - CheckResult(result.TransactionResult); + TokenConverterContractAddress, + TreasuryContractAddress + }, + Owner = BootMinerAddress + }); + await TokenContractStub.Issue.SendAsync(new IssueInput + { + Symbol = EconomicContractsTestConstants.TransactionFeeChargingContractTokenSymbol, + Amount = 100_000, + To = TokenConverterContractAddress + }); + + // var result = await TransactionFeeChargingContractStub.InitializeTransactionFeeChargingContract.SendAsync( + // new InitializeTransactionFeeChargingContractInput + // { + // Symbol = EconomicContractsTestConstants.TransactionFeeChargingContractTokenSymbol + // }); + // CheckResult(result.TransactionResult); { var approveResult = await TokenContractStub.Approve.SendAsync(new ApproveInput diff --git a/test/AElf.Contracts.Economic.TestBase/EconomicContractsTestBase.cs b/test/AElf.Contracts.Economic.TestBase/EconomicContractsTestBase.cs index 60a5a0d350..b4f5419397 100644 --- a/test/AElf.Contracts.Economic.TestBase/EconomicContractsTestBase.cs +++ b/test/AElf.Contracts.Economic.TestBase/EconomicContractsTestBase.cs @@ -3,6 +3,7 @@ using AElf.ContractTestKit; using AElf.Cryptography.ECDSA; using AElf.Kernel; +using AElf.Kernel.Blockchain.Application; using AElf.Types; using Google.Protobuf.WellKnownTypes; using Microsoft.Extensions.DependencyInjection; @@ -13,6 +14,9 @@ public partial class EconomicContractsTestBase : ContractTestBase Application.ServiceProvider.GetRequiredService(); + + protected IBlockchainService BlockchainService => + Application.ServiceProvider.GetRequiredService(); protected Timestamp StartTimestamp => TimestampHelper.GetUtcNow(); diff --git a/test/AElf.Contracts.Economic.TestBase/EconomicContractsTestModule.cs b/test/AElf.Contracts.Economic.TestBase/EconomicContractsTestModule.cs index 158133ef8a..6d50eeb482 100644 --- a/test/AElf.Contracts.Economic.TestBase/EconomicContractsTestModule.cs +++ b/test/AElf.Contracts.Economic.TestBase/EconomicContractsTestModule.cs @@ -26,6 +26,7 @@ public override void ConfigureServices(ServiceConfigurationContext context) // context.Services.AddSingleton(); context.Services.AddSingleton(); context.Services.AddSingleton(); + context.Services.AddTransient(); context.Services.RemoveAll(); } } \ No newline at end of file diff --git a/test/AElf.Contracts.Economic.TestBase/MockRandomNumberProvider.cs b/test/AElf.Contracts.Economic.TestBase/MockRandomNumberProvider.cs new file mode 100644 index 0000000000..8bcd0d3d61 --- /dev/null +++ b/test/AElf.Contracts.Economic.TestBase/MockRandomNumberProvider.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; +using AElf.Kernel; +using AElf.Kernel.Account.Application; +using AElf.Kernel.Consensus.AEDPoS.Application; +using AElf.Types; + +namespace AElf.Contracts.Economic.TestBase; + +public class MockRandomNumberProvider : IRandomNumberProvider +{ + private readonly IAccountService _accountService; + + public MockRandomNumberProvider(IAccountService accountService) + { + _accountService = accountService; + } + + public async Task GenerateRandomProofAsync(IChainContext chainContext) + { + return await _accountService.ECVrfProveAsync(Hash.Empty.ToByteArray()); + } +} \ No newline at end of file diff --git a/test/AElf.Contracts.Economic.TestBase/OtherContractsOperation.cs b/test/AElf.Contracts.Economic.TestBase/OtherContractsOperation.cs index 76622df755..d89e73bd70 100644 --- a/test/AElf.Contracts.Economic.TestBase/OtherContractsOperation.cs +++ b/test/AElf.Contracts.Economic.TestBase/OtherContractsOperation.cs @@ -1,8 +1,10 @@ using System.Threading.Tasks; using AElf.Contracts.Consensus.AEDPoS; using AElf.Contracts.MultiToken; +using AElf.Cryptography; using AElf.Cryptography.ECDSA; using AElf.Types; +using Google.Protobuf; using Google.Protobuf.WellKnownTypes; using Shouldly; @@ -24,9 +26,10 @@ protected async Task NextTerm(ECKeyPair keyPair) victories.Value } }; + var randomNumber = await GenerateRandomProofAsync(keyPair); var firstRoundOfNextTerm = miners.GenerateFirstRoundOfNewTerm(EconomicContractsTestConstants.MiningInterval, - BlockTimeProvider.GetBlockTime(), round.RoundNumber, round.TermNumber); + randomNumber, BlockTimeProvider.GetBlockTime(), round.RoundNumber, round.TermNumber); var executionResult = (await miner.NextTerm.SendAsync(firstRoundOfNextTerm)).TransactionResult; executionResult.Error.ShouldBeNullOrEmpty(); executionResult.Status.ShouldBe(TransactionResultStatus.Mined); @@ -36,9 +39,10 @@ protected async Task NextRound(ECKeyPair keyPair) { var miner = GetConsensusContractTester(keyPair); var round = await miner.GetCurrentRoundInformation.CallAsync(new Empty()); + var randomNumber = await GenerateRandomProofAsync(keyPair); round.GenerateNextRoundInformation( StartTimestamp.ToDateTime().AddMilliseconds(round.TotalMilliseconds()).ToTimestamp(), StartTimestamp, - out var nextRound); + ByteString.CopyFrom(randomNumber), out var nextRound); await miner.NextRound.SendAsync(nextRound); } @@ -56,7 +60,8 @@ await miner.UpdateValue.SendAsync(new UpdateValueInput RoundId = round.RoundId, ProducedBlocks = minerInRound.ProducedBlocks + 1, ActualMiningTime = minerInRound.ExpectedMiningTime, - SupposedOrderOfNextRound = 1 + SupposedOrderOfNextRound = 1, + RandomNumber = ByteString.CopyFrom(await GenerateRandomProofAsync(keyPair)) }); } @@ -95,5 +100,17 @@ protected async Task GetVoteTokenBalance(byte[] publicKey) return balance; } + protected async Task GenerateRandomProofAsync(ECKeyPair keyPair) + { + var consensusContractStub = GetConsensusContractTester(keyPair); + var blockHeight = (await BlockchainService.GetChainAsync()).BestChainHeight; + var previousRandomHash = + blockHeight <= 1 + ? Hash.Empty + : await consensusContractStub.GetRandomHash.CallAsync(new Int64Value + { Value = blockHeight }); + return CryptoHelper.ECVrfProve(keyPair, previousRandomHash.ToByteArray()); + } + #endregion } \ No newline at end of file diff --git a/test/AElf.Contracts.Economic.TestBase/Types/MinerList.cs b/test/AElf.Contracts.Economic.TestBase/Types/MinerList.cs index ed9c3082b5..800ef0cb5a 100644 --- a/test/AElf.Contracts.Economic.TestBase/Types/MinerList.cs +++ b/test/AElf.Contracts.Economic.TestBase/Types/MinerList.cs @@ -8,7 +8,7 @@ namespace AElf.Contracts.Consensus.AEDPoS; internal partial class MinerList { - public Round GenerateFirstRoundOfNewTerm(int miningInterval, + public Round GenerateFirstRound(int miningInterval, Timestamp currentBlockTime, long currentRoundNumber = 0, long currentTermNumber = 0) { var sortedMiners = @@ -41,4 +41,39 @@ orderby obj.Value descending return round; } + + public NextTermInput GenerateFirstRoundOfNewTerm(int miningInterval, byte[] randomNumber, + Timestamp currentBlockTime, long currentRoundNumber = 0, long currentTermNumber = 0) + { + var sortedMiners = + (from obj in Pubkeys + .ToDictionary(miner => miner.ToHex(), miner => miner[0]) + orderby obj.Value descending + select obj.Key).ToList(); + + var input = new NextTermInput(); + + for (var i = 0; i < sortedMiners.Count; i++) + { + var minerInRound = new MinerInRound(); + + // The first miner will be the extra block producer of first round of each term. + if (i == 0) minerInRound.IsExtraBlockProducer = true; + + minerInRound.Pubkey = sortedMiners[i]; + minerInRound.Order = i + 1; + minerInRound.ExpectedMiningTime = currentBlockTime.AddMilliseconds(i * miningInterval + miningInterval); + // Should be careful during validation. + minerInRound.PreviousInValue = Hash.Empty; + + input.RealTimeMinersInformation.Add(sortedMiners[i], minerInRound); + } + + input.RoundNumber = currentRoundNumber + 1; + input.TermNumber = currentTermNumber + 1; + input.IsMinerListJustChanged = true; + input.RandomNumber = ByteString.CopyFrom(randomNumber); + + return input; + } } \ No newline at end of file diff --git a/test/AElf.Contracts.Economic.TestBase/Types/Round_Generation.cs b/test/AElf.Contracts.Economic.TestBase/Types/Round_Generation.cs index 567ca6cf6a..f195933fff 100644 --- a/test/AElf.Contracts.Economic.TestBase/Types/Round_Generation.cs +++ b/test/AElf.Contracts.Economic.TestBase/Types/Round_Generation.cs @@ -1,16 +1,18 @@ using System.Collections.Generic; using System.Linq; using AElf.CSharp.Core; +using AElf.Types; +using Google.Protobuf; using Google.Protobuf.WellKnownTypes; namespace AElf.Contracts.Consensus.AEDPoS; internal partial class Round { - public bool GenerateNextRoundInformation(Timestamp currentBlockTimestamp, Timestamp blockchainStartTimestamp, - out Round nextRound) + public bool GenerateNextRoundInformation(Timestamp currentBlockTimestamp, Timestamp blockchainStartTimestamp, ByteString randomNumber, + out NextRoundInput nextRound) { - nextRound = new Round(); + nextRound = new NextRoundInput(); var minersMinedCurrentRound = GetMinedMiners(); var minersNotMinedCurrentRound = GetNotMinedMiners(); @@ -62,7 +64,8 @@ public bool GenerateNextRoundInformation(Timestamp currentBlockTimestamp, Timest nextRound.RealTimeMinersInformation.Values.First().IsExtraBlockProducer = true; else expectedExtraBlockProducer.IsExtraBlockProducer = true; - + nextRound.RandomNumber = randomNumber; + return true; } diff --git a/test/AElf.Contracts.EconomicSystem.Tests/AElf.Contracts.EconomicSystem.Tests.csproj b/test/AElf.Contracts.EconomicSystem.Tests/AElf.Contracts.EconomicSystem.Tests.csproj index c5333b816b..dbad41ec40 100644 --- a/test/AElf.Contracts.EconomicSystem.Tests/AElf.Contracts.EconomicSystem.Tests.csproj +++ b/test/AElf.Contracts.EconomicSystem.Tests/AElf.Contracts.EconomicSystem.Tests.csproj @@ -13,7 +13,7 @@ runtime; build; native; contentfiles; analyzers - + all @@ -75,10 +75,10 @@ Contract PreserveNewest - - - - + + + + false Contract @@ -99,9 +99,14 @@ Contract PreserveNewest - - - + + false + Contract + PreserveNewest + + + + @@ -222,5 +227,5 @@ - + diff --git a/test/AElf.Contracts.EconomicSystem.Tests/BVT/DonateTests.cs b/test/AElf.Contracts.EconomicSystem.Tests/BVT/DonateTests.cs index db05af1544..4c3a80ae96 100644 --- a/test/AElf.Contracts.EconomicSystem.Tests/BVT/DonateTests.cs +++ b/test/AElf.Contracts.EconomicSystem.Tests/BVT/DonateTests.cs @@ -118,13 +118,14 @@ private async Task InitialBuildConnector(string symbol) TokenSymbol = symbol, AmountToTokenConvert = 0 }; - var issueRet = (await TransactionFeeChargingContractStub.IssueToTokenConvert.SendAsync( - new IssueAmount + await TokenContractStub.Issue.SendAsync( + new IssueInput { Symbol = symbol, - Amount = token.TotalSupply - token.Supply - })).TransactionResult; - issueRet.Status.ShouldBe(TransactionResultStatus.Mined); + Amount = token.TotalSupply - token.Supply, + To = TokenConverterContractAddress, + Memo = "test" + }); var buildConnector = (await TokenConverterContractStub.EnableConnector.SendAsync(tokenInfo)).TransactionResult; buildConnector.Status.ShouldBe(TransactionResultStatus.Mined); } diff --git a/test/AElf.Contracts.EconomicSystem.Tests/BVT/TreasuryBasicTests.cs b/test/AElf.Contracts.EconomicSystem.Tests/BVT/TreasuryBasicTests.cs index 44fa2b131a..24fc9e63d0 100644 --- a/test/AElf.Contracts.EconomicSystem.Tests/BVT/TreasuryBasicTests.cs +++ b/test/AElf.Contracts.EconomicSystem.Tests/BVT/TreasuryBasicTests.cs @@ -281,10 +281,11 @@ public async Task Treasury_SetSymbolList_With_Invalid_Token_Test() TokenName = "TEST name", TotalSupply = 1_0000_0000, Issuer = BootMinerAddress, - IsBurnable = true + IsBurnable = true, + Owner = BootMinerAddress }; - var createTokenRet = await TokenContractStub.Create.SendAsync(tokenCreateInput); - createTokenRet.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + await ExecuteProposalForParliamentTransactionWithException(Tester, TokenContractAddress, nameof(TokenContractStub.Create), + tokenCreateInput); // without native token { var newSymbolList = new SymbolList @@ -328,9 +329,11 @@ public async Task Treasury_SetSymbolList_Success_Test() TokenName = "CWJ name", TotalSupply = 1_0000_0000, Issuer = BootMinerAddress, - IsBurnable = true + IsBurnable = true, + Owner = BootMinerAddress }; - await TokenContractStub.Create.SendAsync(tokenCreateInput); + await ExecuteProposalForParliamentTransaction(TokenContractAddress, nameof(TokenContractStub.Create), + tokenCreateInput); var newSymbolList = new SymbolList { Value = diff --git a/test/AElf.Contracts.Election.Tests/AElf.Contracts.Election.Tests.csproj b/test/AElf.Contracts.Election.Tests/AElf.Contracts.Election.Tests.csproj index 4f04ca5c83..e472065cfe 100644 --- a/test/AElf.Contracts.Election.Tests/AElf.Contracts.Election.Tests.csproj +++ b/test/AElf.Contracts.Election.Tests/AElf.Contracts.Election.Tests.csproj @@ -40,6 +40,11 @@ Contract PreserveNewest + + false + Contract + PreserveNewest + @@ -52,6 +57,13 @@ Protobuf\Proto\transaction_fee.proto + + + + Protobuf\Proto\test_virtual_address_contract.proto + + + diff --git a/test/AElf.Contracts.Election.Tests/BVT/ElectionTests.cs b/test/AElf.Contracts.Election.Tests/BVT/ElectionTests.cs index ce2fac0ff9..2ba363f220 100644 --- a/test/AElf.Contracts.Election.Tests/BVT/ElectionTests.cs +++ b/test/AElf.Contracts.Election.Tests/BVT/ElectionTests.cs @@ -6,6 +6,7 @@ using AElf.Contracts.MultiToken; using AElf.Contracts.Parliament; using AElf.Contracts.Profit; +using AElf.Contracts.TestContract.VirtualAddress; using AElf.Contracts.Vote; using AElf.Cryptography.ECDSA; using AElf.CSharp.Core; @@ -299,7 +300,7 @@ await VoteToCandidates( { Value = voterKeyPair.PublicKey.ToHex() }); - voterVotes.Pubkey.ShouldBe(ByteString.CopyFrom(voterKeyPair.PublicKey)); + voterVotes.Address.ShouldBe(Address.FromPublicKey(voterKeyPair.PublicKey)); voterVotes.ActiveVotingRecordIds.Count.ShouldBe(19); voterVotes.AllVotedVotesAmount.ShouldBe(actualVotedAmount); voterVotes.ActiveVotedVotesAmount.ShouldBe(actualVotedAmount); @@ -1900,4 +1901,224 @@ private async Task ResetMinerCount(int count) } #endregion + + [Fact] + public async Task VirtualAddress_Vote_Test() + { + var amount = 100; + const int lockTime = 100 * 60 * 60 * 24; + var candidatesKeyPairs = await ElectionContract_AnnounceElection_Test(); + var candidateKeyPair = candidatesKeyPairs[0]; + + var address = await VirtualAddressContractStub.GetVirtualAddress.CallAsync(new Empty()); + var initBalance = 100000; + + await TokenContractStub.Transfer.SendAsync(new TransferInput + { + Amount = initBalance, + Symbol = "ELF", + To = address, + Memo = "test" + }); + + CheckBalance(address, "ELF", initBalance); + CheckBalance(address, "SHARE", 0); + CheckBalance(address, "VOTE", 0); + + await VirtualAddressContractStub.VirtualAddressVote.SendAsync(new VirtualAddressVoteInput + { + PubKey = candidateKeyPair.PublicKey.ToHex(), + Amount = amount, + EndTimestamp = TimestampHelper.GetUtcNow().AddSeconds(lockTime), + Token = HashHelper.ComputeFrom("token A") + }); + + var result = await ElectionContractStub.GetElectorVote.CallAsync(new StringValue + { + Value = address.ToBase58() + }); + result.ActiveVotedVotesAmount.ShouldBe(amount); + result = await ElectionContractStub.GetElectorVoteWithRecords.CallAsync(new StringValue + { + Value = address.ToBase58() + }); + result.ActiveVotedVotesAmount.ShouldBe(amount); + result = await ElectionContractStub.GetElectorVoteWithAllRecords.CallAsync(new StringValue + { + Value = address.ToBase58() + }); + result.AllVotedVotesAmount.ShouldBe(amount); + + CheckBalance(address, "ELF", initBalance - amount); + CheckBalance(address, "SHARE", amount); + CheckBalance(address, "VOTE", amount); + + await VirtualAddressContractStub.VirtualAddressVote.SendAsync(new VirtualAddressVoteInput + { + PubKey = candidateKeyPair.PublicKey.ToHex(), + Amount = amount, + EndTimestamp = TimestampHelper.GetUtcNow().AddSeconds(lockTime), + Token = HashHelper.ComputeFrom("token A") + }); + + result = await ElectionContractStub.GetElectorVote.CallAsync(new StringValue + { + Value = address.ToBase58() + }); + result.ActiveVotedVotesAmount.ShouldBe(amount + amount); + result = await ElectionContractStub.GetElectorVoteWithRecords.CallAsync(new StringValue + { + Value = address.ToBase58() + }); + result.ActiveVotedVotesAmount.ShouldBe(amount + amount); + result = await ElectionContractStub.GetElectorVoteWithAllRecords.CallAsync(new StringValue + { + Value = address.ToBase58() + }); + result.AllVotedVotesAmount.ShouldBe(amount + amount); + + CheckBalance(address, "ELF", initBalance - amount - amount); + CheckBalance(address, "SHARE", amount + amount); + CheckBalance(address, "VOTE", amount + amount); + + return result.ActiveVotingRecords.First().VoteId; + } + + [Fact] + public async Task VirtualAddress_Withdraw_Test() + { + var amount = 100; + const int lockTime = 100 * 60 * 60 * 24; + + var address = await VirtualAddressContractStub.GetVirtualAddress.CallAsync(new Empty()); + var initBalance = 100000; + + var voteId = await VirtualAddress_Vote_Test(); + BlockTimeProvider.SetBlockTime(TimestampHelper.GetUtcNow().AddDays(101)); + + await VirtualAddressContractStub.VirtualAddressWithdraw.SendAsync(new Hash + { + Value = voteId.Value + }); + + var result = await ElectionContractStub.GetElectorVote.CallAsync(new StringValue + { + Value = address.ToBase58() + }); + result.ActiveVotedVotesAmount.ShouldBe(amount); + result = await ElectionContractStub.GetElectorVoteWithRecords.CallAsync(new StringValue + { + Value = address.ToBase58() + }); + result.ActiveVotedVotesAmount.ShouldBe(amount); + result = await ElectionContractStub.GetElectorVoteWithAllRecords.CallAsync(new StringValue + { + Value = address.ToBase58() + }); + + result.ActiveVotedVotesAmount.ShouldBe(amount); + result.WithdrawnVotesRecords.Count().ShouldBe(1); + result.AllVotedVotesAmount.ShouldBe(amount + amount); + + CheckBalance(address, "ELF", initBalance - amount); + CheckBalance(address, "SHARE", amount); + CheckBalance(address, "VOTE", amount); + } + + [Fact] + public async Task Vote_Test() + { + var amount = 100; + const int lockTime = 100 * 60 * 60 * 24; + var candidatesKeyPairs = await ElectionContract_AnnounceElection_Test(); + var candidateKeyPair = candidatesKeyPairs[0]; + + var address = Address.FromPublicKey(BootMinerKeyPair.PublicKey); + var initBalance = 100000000000000; + + CheckBalance(address, "ELF", initBalance); + CheckBalance(address, "SHARE", 0); + CheckBalance(address, "VOTE", 0); + + var voteRet = await ElectionContractStub.Vote.SendAsync(new VoteMinerInput + { + CandidatePubkey = candidateKeyPair.PublicKey.ToHex(), + Amount = amount, + EndTimestamp = TimestampHelper.GetUtcNow().AddSeconds(lockTime), + Token = HashHelper.ComputeFrom("token A") + }); + voteRet.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + + var result = await ElectionContractStub.GetElectorVote.CallAsync(new StringValue + { + Value = BootMinerKeyPair.PublicKey.ToHex() + }); + result.ActiveVotedVotesAmount.ShouldBe(amount); + result = await ElectionContractStub.GetElectorVoteWithRecords.CallAsync(new StringValue + { + Value = BootMinerKeyPair.PublicKey.ToHex() + }); + result.ActiveVotedVotesAmount.ShouldBe(amount); + result = await ElectionContractStub.GetElectorVoteWithAllRecords.CallAsync(new StringValue + { + Value = BootMinerKeyPair.PublicKey.ToHex() + }); + result.AllVotedVotesAmount.ShouldBe(amount); + + CheckBalance(address, "ELF", initBalance - amount); + CheckBalance(address, "SHARE", amount); + CheckBalance(address, "VOTE", amount); + + return result.ActiveVotingRecords.First().VoteId; + } + + [Fact] + public async Task Withdraw_Test() + { + var voteId = await Vote_Test(); + + BlockTimeProvider.SetBlockTime(TimestampHelper.GetUtcNow().AddDays(101)); + + await ElectionContractStub.Withdraw.SendAsync(new Hash + { + Value = voteId.Value + }); + + var result = await ElectionContractStub.GetElectorVote.CallAsync(new StringValue + { + Value = BootMinerKeyPair.PublicKey.ToHex() + }); + result.ActiveVotedVotesAmount.ShouldBe(0); + result = await ElectionContractStub.GetElectorVoteWithRecords.CallAsync(new StringValue + { + Value = BootMinerKeyPair.PublicKey.ToHex() + }); + result.ActiveVotedVotesAmount.ShouldBe(0); + result = await ElectionContractStub.GetElectorVoteWithAllRecords.CallAsync(new StringValue + { + Value = BootMinerKeyPair.PublicKey.ToHex() + }); + + result.ActiveVotedVotesAmount.ShouldBe(0); + result.WithdrawnVotesRecords.Count().ShouldBe(1); + result.AllVotedVotesAmount.ShouldBe(100); + + var address = Address.FromPublicKey(BootMinerKeyPair.PublicKey); + var initBalance = 100000000000000; + + CheckBalance(address, "ELF", initBalance); + CheckBalance(address, "SHARE", 0); + CheckBalance(address, "VOTE", 0); + } + + private void CheckBalance(Address address, string symbol, long amount) + { + var balance = TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = address, + Symbol = symbol + }).Result; + + balance.Balance.ShouldBe(amount); + } } \ No newline at end of file diff --git a/test/AElf.Contracts.Election.Tests/BVT/ElectionViewTests.cs b/test/AElf.Contracts.Election.Tests/BVT/ElectionViewTests.cs index 61d8128ced..a8d4093af4 100644 --- a/test/AElf.Contracts.Election.Tests/BVT/ElectionViewTests.cs +++ b/test/AElf.Contracts.Election.Tests/BVT/ElectionViewTests.cs @@ -47,10 +47,7 @@ public async Task GetElectorVoteWithRecords_NotExist_Test() { Value = ValidationDataCenterKeyPairs.Last().PublicKey.ToHex() }); - voteRecords.ShouldBe(new ElectorVote - { - Pubkey = ByteString.CopyFrom(ValidationDataCenterKeyPairs.Last().PublicKey) - }); + voteRecords.ShouldBe(new ElectorVote()); } [Fact] @@ -305,7 +302,7 @@ public async Task Election_GetElectorVote_Test() { Value = key }); - ret.ShouldBe(new ElectorVote { Pubkey = ByteStringHelper.FromHexString(key) }); + ret.ShouldBe(new ElectorVote()); } [Fact] diff --git a/test/AElf.Contracts.Election.Tests/ElectionContractTestBase.cs b/test/AElf.Contracts.Election.Tests/ElectionContractTestBase.cs index 4375a2c352..6ba094c35d 100644 --- a/test/AElf.Contracts.Election.Tests/ElectionContractTestBase.cs +++ b/test/AElf.Contracts.Election.Tests/ElectionContractTestBase.cs @@ -5,9 +5,11 @@ using AElf.Contracts.MultiToken; using AElf.Contracts.Parliament; using AElf.Contracts.Profit; +using AElf.Contracts.TestContract.VirtualAddress; using AElf.Contracts.TokenConverter; using AElf.Contracts.Treasury; using AElf.Contracts.Vote; +using AElf.ContractTestKit; using AElf.Cryptography.ECDSA; using AElf.Types; using Google.Protobuf.WellKnownTypes; @@ -17,6 +19,13 @@ namespace AElf.Contracts.Election; public class ElectionContractTestBase : EconomicContractsTestBase { + protected readonly IBlockTimeProvider BlockTimeProvider; + + protected ElectionContractTestBase() + { + BlockTimeProvider = GetRequiredService(); + } + protected Hash MinerElectionVotingItemId; internal BasicContractZeroImplContainer.BasicContractZeroImplStub BasicContractZeroStub => @@ -48,6 +57,9 @@ public class ElectionContractTestBase : EconomicContractsTestBase internal EconomicContractImplContainer.EconomicContractImplStub EconomicContractStub => GetEconomicContractTester(BootMinerKeyPair); + internal VirtualAddressContractContainer.VirtualAddressContractStub VirtualAddressContractStub => + GetVirtualAddressContractTester(BootMinerKeyPair); + private new void DeployAllContracts() { _ = TokenContractAddress; @@ -135,4 +147,9 @@ internal EconomicContractImplContainer.EconomicContractImplStub GetEconomicContr { return GetTester(EconomicContractAddress, keyPair); } + + internal VirtualAddressContractContainer.VirtualAddressContractStub GetVirtualAddressContractTester(ECKeyPair keyPair) + { + return GetTester(VirtualAddressContractAddress, keyPair); + } } \ No newline at end of file diff --git a/test/AElf.Contracts.Election.Tests/Full/BackupSubsidyTests.cs b/test/AElf.Contracts.Election.Tests/Full/BackupSubsidyTests.cs index 48a1f5a9b2..c4d466b08f 100644 --- a/test/AElf.Contracts.Election.Tests/Full/BackupSubsidyTests.cs +++ b/test/AElf.Contracts.Election.Tests/Full/BackupSubsidyTests.cs @@ -618,6 +618,41 @@ await candidateAdminStub.SetProfitsReceiver.SendAsync( } } + [Fact] + public async Task SetProfitsReceiver_Wrong_Admin_Test() + { + var announceElectionKeyPair = ValidationDataCenterKeyPairs.First(); + var candidateAdmin = ValidationDataCenterKeyPairs.Last(); + var profitReceiver = candidateAdmin; + var candidateAdminAddress = Address.FromPublicKey(candidateAdmin.PublicKey); + await AnnounceElectionAsync(announceElectionKeyPair, candidateAdminAddress); + + var candidateAdminStub = + GetTester(TreasuryContractAddress, + candidateAdmin); + await candidateAdminStub.SetProfitsReceiver.SendAsync( + new Treasury.SetProfitsReceiverInput + { + Pubkey = announceElectionKeyPair.PublicKey.ToHex(), + ProfitsReceiverAddress = Address.FromPublicKey(profitReceiver.PublicKey) + }); + var getProfitReceiver = await GetProfitReceiver(announceElectionKeyPair.PublicKey.ToHex()); + getProfitReceiver.ShouldBe(Address.FromPublicKey(profitReceiver.PublicKey)); + + var wrongAdminStub = + GetTester(TreasuryContractAddress, + announceElectionKeyPair); + var setResult = await wrongAdminStub.SetProfitsReceiver.SendWithExceptionAsync( + new Treasury.SetProfitsReceiverInput(new Treasury.SetProfitsReceiverInput + { + Pubkey = announceElectionKeyPair.PublicKey.ToHex(), + ProfitsReceiverAddress = Address.FromPublicKey(profitReceiver.PublicKey) + })); + + setResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); + setResult.TransactionResult.Error.ShouldContain("No permission."); + } + private async Task GetBackupSubsidyProfitDetails(Address address) { return await ProfitContractStub.GetProfitDetails.CallAsync(new GetProfitDetailsInput diff --git a/test/AElf.Contracts.Genesis.Tests/AElf.Contracts.Genesis.Tests.csproj b/test/AElf.Contracts.Genesis.Tests/AElf.Contracts.Genesis.Tests.csproj index fc5f102700..c439d16de0 100644 --- a/test/AElf.Contracts.Genesis.Tests/AElf.Contracts.Genesis.Tests.csproj +++ b/test/AElf.Contracts.Genesis.Tests/AElf.Contracts.Genesis.Tests.csproj @@ -33,6 +33,11 @@ Contract PreserveNewest + + false + Contract + PreserveNewest + diff --git a/test/AElf.Contracts.MultiToken.Tests/AElf.Contracts.MultiToken.Tests.csproj b/test/AElf.Contracts.MultiToken.Tests/AElf.Contracts.MultiToken.Tests.csproj index 08ec6e2c33..a7de2a24a1 100644 --- a/test/AElf.Contracts.MultiToken.Tests/AElf.Contracts.MultiToken.Tests.csproj +++ b/test/AElf.Contracts.MultiToken.Tests/AElf.Contracts.MultiToken.Tests.csproj @@ -63,17 +63,18 @@ all runtime; build; native; contentfiles; analyzers - + - + + - all - runtime; build; native; contentfiles; analyzers - + all + runtime; build; native; contentfiles; analyzers + - all - runtime; build; native; contentfiles; analyzers - + all + runtime; build; native; contentfiles; analyzers + @@ -134,6 +135,6 @@ Protobuf\Proto\base\acs11.proto - - + + diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/ACS1_ImplementTest.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/ACS1_ImplementTest.cs index d50f4ad89d..ec111cbc73 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/ACS1_ImplementTest.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/ACS1_ImplementTest.cs @@ -129,7 +129,6 @@ public async Task SendInvalidTransactionsTest() [Fact] public async Task SetMethodFee_Success_Test() { - await CreateNativeTokenAsync(); var methodName = "Transfer"; var tokenSymbol = NativeTokenInfo.Symbol; var basicFee = 100; @@ -161,7 +160,6 @@ public async Task SetMethodFee_Success_Test() [Fact] public async Task SetMethodFee_Fail_Test() { - await CreateNativeTokenAsync(); var tokenSymbol = NativeTokenInfo.Symbol; var methodName = "Transfer"; // unauthorized @@ -212,11 +210,12 @@ public async Task SetMethodFee_Fail_Test() // token is not profitable { var tokenNotProfitable = "DLS"; - await TokenContractStub.Create.SendAsync(new CreateInput + await CreateMutiTokenAsync(TokenContractStub,new CreateInput { Symbol = tokenNotProfitable, TokenName = "name", Issuer = DefaultAddress, + Owner = DefaultAddress, TotalSupply = 1000_000 }); var methodFees = new MethodFees @@ -239,7 +238,6 @@ public async Task GetMethodFee_No_Fee_Test(params string[] defaultSetMethodNames { var methodFeeController = await TokenContractStub.GetMethodFeeController.CallAsync(new Empty()); var proposalMethodName = nameof(TokenContractStub.SetMethodFee); - await CreateNativeTokenAsync(); var tokenSymbol = NativeTokenInfo.Symbol; var basicFee = 100; foreach (var methodName in defaultSetMethodNames) @@ -271,7 +269,6 @@ public async Task GetMethodFee_Fix_Fee_Test(params string[] defaultSetMethodName { var methodFeeController = await TokenContractStub.GetMethodFeeController.CallAsync(new Empty()); var proposalMethodName = nameof(TokenContractStub.SetMethodFee); - await CreateNativeTokenAsync(); var tokenSymbol = NativeTokenInfo.Symbol; var basicFee = 100; foreach (var methodName in defaultSetMethodNames) diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/ACS2_TokenResourceTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/ACS2_TokenResourceTests.cs index 45da7dc586..35f84f7154 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/ACS2_TokenResourceTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/ACS2_TokenResourceTests.cs @@ -1,4 +1,11 @@ +using System.Collections.Generic; using System.Threading.Tasks; +using AElf.CSharp.Core.Extension; +using AElf.Kernel; +using AElf.Standards.ACS1; +using AElf.Standards.ACS3; +using AElf.Types; +using Google.Protobuf; using Google.Protobuf.WellKnownTypes; using Shouldly; using Xunit; @@ -42,6 +49,139 @@ public async Task ACS2_GetResourceInfo_TransferFrom_Test() result.WritePaths.Count.ShouldBeGreaterThan(0); } + private async Task
GetDefaultParliamentAddressAsync() + { + var defaultParliamentAddress = + await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); + return defaultParliamentAddress; + } + + [Fact] + public async Task ACS2_GetResourceInfo_Transfer_MultiToken_Test() + { + var theDefaultController = await GetDefaultParliamentAddressAsync(); + var newSymbolList = new SymbolListToPayTxSizeFee(); + await CreateTokenAsync(DefaultAddress, "CPU"); + await CreateTokenAsync(DefaultAddress, "NET"); + var methodName = "Transfer"; + var tokenSymbol = "NET"; + var basicFee = 100; + var methodFeeController = await TokenContractStub.GetMethodFeeController.CallAsync(new Empty()); + var proposalMethodName = nameof(TokenContractStub.SetMethodFee); + var methodFees = new MethodFees + { + MethodName = methodName, + Fees = + { + new MethodFee { Symbol = tokenSymbol, BasicFee = basicFee } + } + }; + var proposalId = await CreateProposalAsync(TokenContractAddress, + methodFeeController.OwnerAddress, proposalMethodName, methodFees); + await ApproveWithMinersAsync(proposalId); + var releaseResult = await ParliamentContractStub.Release.SendAsync(proposalId); + releaseResult.TransactionResult.Error.ShouldBeNullOrEmpty(); + releaseResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + newSymbolList.SymbolsToPayTxSizeFee.Add(new SymbolToPayTxSizeFee + { + TokenSymbol = "ELF", + AddedTokenWeight = 1, + BaseTokenWeight = 1 + }); + newSymbolList.SymbolsToPayTxSizeFee.Add(new SymbolToPayTxSizeFee + { + TokenSymbol = "CPU", + AddedTokenWeight = 2, + BaseTokenWeight = 1 + }); + var createProposalInput = new CreateProposalInput + { + ToAddress = TokenContractAddress, + Params = newSymbolList.ToByteString(), + OrganizationAddress = theDefaultController, + ContractMethodName = nameof(TokenContractImplContainer.TokenContractImplStub + .SetSymbolsToPayTxSizeFee), + ExpiredTime = TimestampHelper.GetUtcNow().AddHours(1) + }; + var parliamentCreateProposal = await ParliamentContractStub.CreateProposal.SendAsync(createProposalInput); + var parliamentProposalId = parliamentCreateProposal.Output; + foreach (var bp in InitialCoreDataCenterKeyPairs) + { + var tester = GetParliamentContractTester(bp); + var approveResult = await tester.Approve.SendAsync(parliamentProposalId); + approveResult.TransactionResult.Error.ShouldBeNullOrEmpty(); + } + var transactionRelease = ParliamentContractStub.Release.GetTransaction(parliamentProposalId); + await ExecuteTransactionWithMiningAsync(transactionRelease); + var symbolListToPayTxSizeFee = await TokenContractStub.GetSymbolsToPayTxSizeFee.CallAsync(new Empty()); + symbolListToPayTxSizeFee.SymbolsToPayTxSizeFee.Count.ShouldBeGreaterThan(0); + var transaction = GenerateTokenTransaction(Accounts[0].Address, nameof(TokenContractStub.Transfer), + new TransferFromInput + { + Amount = 100, + Symbol = "ELF", + From = Accounts[1].Address, + To = Accounts[2].Address, + Memo = "Test get resource" + }); + + var result1 = await Acs2BaseStub.GetResourceInfo.CallAsync(transaction); + result1.NonParallelizable.ShouldBeFalse(); + result1.WritePaths.Count.ShouldBeGreaterThan(0); + } + + [Fact] + public async Task ACS2_GetResourceInfo_TransferFrom_WithDelegate_Test() + { + var delegations1 = new Dictionary + { + [NativeToken] = 1000, + }; + var delegateInfo1 = new DelegateInfo + { + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractStub.TransferFrom), + Delegations = + { + delegations1 + }, + IsUnlimitedDelegate = false + }; + await TokenContractStubUser.SetTransactionFeeDelegateInfos.SendAsync(new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = Accounts[0].Address, + DelegateInfoList = { delegateInfo1 } + }); + await TokenContractStubDelegate.SetTransactionFeeDelegateInfos.SendAsync(new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = Accounts[0].Address, + DelegateInfoList = { delegateInfo1 } + }); + await TokenContractStubDelegate2.SetTransactionFeeDelegateInfos.SendAsync(new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = User1Address, + DelegateInfoList = { delegateInfo1 } + }); + await TokenContractStubDelegate3.SetTransactionFeeDelegateInfos.SendAsync(new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = User2Address, + DelegateInfoList = { delegateInfo1 } + }); + var transaction = GenerateTokenTransaction(Accounts[0].Address, nameof(TokenContractStub.TransferFrom), + new TransferFromInput + { + Amount = 100, + Symbol = NativeToken, + From = Accounts[1].Address, + To = Accounts[2].Address, + Memo = "Test get resource" + }); + + var result = await Acs2BaseStub.GetResourceInfo.CallAsync(transaction); + result.NonParallelizable.ShouldBeFalse(); + result.WritePaths.Count.ShouldBe(9); + + } [Fact] public async Task ACS2_GetResourceInfo_DonateResourceToken_Test() { diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs index ce5e3c2c8e..24055b432b 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs @@ -2,6 +2,8 @@ using System.Linq; using System.Threading.Tasks; using AElf.CSharp.Core; +using AElf.CSharp.Core.Extension; +using AElf.Kernel; using AElf.Types; using Google.Protobuf.WellKnownTypes; using Shouldly; @@ -44,7 +46,8 @@ public partial class MultiTokenContractTests "https://i.seadn.io/gcs/files/0f5cdfaaf687de2ebb5834b129a5bef3.png?auto=format&w=3840" } } - } + }, + Owner = Accounts[0].Address }; private TokenInfo NftCollection1155Info => new() @@ -75,6 +78,7 @@ public partial class MultiTokenContractTests TotalSupply = 1, Decimals = 0, Issuer = Accounts[0].Address, + Owner = Accounts[0].Address, IssueChainId = _chainId, IsBurnable = true, ExternalInfo = new ExternalInfo() @@ -113,14 +117,14 @@ public partial class MultiTokenContractTests private async Task> CreateNftCollectionAsync(TokenInfo collectionInfo) { - await CreateNativeTokenAsync(); - return await TokenContractStub.Create.SendAsync(new CreateInput + return await CreateMutiTokenAsync(TokenContractStub, new CreateInput { Symbol = $"{collectionInfo.Symbol}0", TokenName = collectionInfo.TokenName, TotalSupply = collectionInfo.TotalSupply, Decimals = collectionInfo.Decimals, Issuer = collectionInfo.Issuer, + Owner = collectionInfo.Issuer, IssueChainId = collectionInfo.IssueChainId, ExternalInfo = collectionInfo.ExternalInfo }); @@ -137,7 +141,8 @@ private async Task> CreateNftAsync(string colllectionSym Issuer = nftInfo.Issuer, IsBurnable = nftInfo.IsBurnable, IssueChainId = nftInfo.IssueChainId, - ExternalInfo = nftInfo.ExternalInfo + ExternalInfo = nftInfo.ExternalInfo, + Owner = nftInfo.Issuer }); } @@ -208,11 +213,10 @@ public async Task MultiTokenContract_Create_721Nft_Test() [Fact(DisplayName = "[MultiToken_Nft] Create nft collection input check")] public async Task MultiTokenContract_Create_NFTCollection_Input_Check_Test() { - await CreateNativeTokenAsync(); var input = NftCollection721Info; // Decimals check { - var result = await TokenContractStub.Create.SendWithExceptionAsync(new CreateInput + var result = await CreateMutiTokenWithExceptionAsync(TokenContractStub, new CreateInput { Symbol = $"{input.Symbol}0", TokenName = input.TokenName, @@ -220,14 +224,15 @@ public async Task MultiTokenContract_Create_NFTCollection_Input_Check_Test() Decimals = 8, Issuer = input.Issuer, IssueChainId = input.IssueChainId, - ExternalInfo = input.ExternalInfo + ExternalInfo = input.ExternalInfo, + Owner = input.Owner }); result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); result.TransactionResult.Error.ShouldContain("NFT's decimals must be 0"); } // Symbol check { - var result = await TokenContractStub.Create.SendWithExceptionAsync(new CreateInput + var seedInput = BuildSeedCreateInput( new CreateInput { Symbol = "ABC123", TokenName = input.TokenName, @@ -235,14 +240,17 @@ public async Task MultiTokenContract_Create_NFTCollection_Input_Check_Test() Decimals = input.Decimals, Issuer = input.Issuer, IssueChainId = input.IssueChainId, - ExternalInfo = input.ExternalInfo + ExternalInfo = input.ExternalInfo, + Owner = input.Owner }); + + var result = await TokenContractStub.Create.SendWithExceptionAsync(seedInput);; result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); result.TransactionResult.Error.ShouldContain("Invalid Symbol input"); } // Symbol length check { - var result = await TokenContractStub.Create.SendWithExceptionAsync(new CreateInput + var seedInput = BuildSeedCreateInput( new CreateInput { Symbol = "ABCDEFGHIJKLMNOPQRSTUVWXYZABC-0", TokenName = input.TokenName, @@ -250,23 +258,27 @@ public async Task MultiTokenContract_Create_NFTCollection_Input_Check_Test() Decimals = input.Decimals, Issuer = input.Issuer, IssueChainId = input.IssueChainId, - ExternalInfo = input.ExternalInfo + ExternalInfo = input.ExternalInfo, + Owner = input.Owner }); + var result = await TokenContractStub.Create.SendWithExceptionAsync(seedInput);; result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); result.TransactionResult.Error.ShouldContain("Invalid NFT symbol length"); } // Issue chain Id check { - var result = await TokenContractStubUser.Create.SendAsync(new CreateInput + var result = await CreateMutiTokenAsync(TokenContractStub, new CreateInput { - Symbol = $"{input.Symbol}0", + Symbol = AliceCoinTokenInfo.Symbol, TokenName = input.TokenName, TotalSupply = input.TotalSupply, Decimals = input.Decimals, Issuer = NftCollection721Info.Issuer, + Owner = NftCollection721Info.Issuer, IssueChainId = ChainHelper.ConvertBase58ToChainId("tDVV"), ExternalInfo = input.ExternalInfo }); + result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); } } @@ -279,9 +291,9 @@ public async Task MultiTokenContract_Create_NFT_Input_Check_Test() // Decimals check { - var result = await TokenContractStub.Create.SendWithExceptionAsync(new CreateInput + var result = await CreateMutiTokenWithExceptionAsync(TokenContractStub, new CreateInput { - Symbol = $"{NftCollection721Info.Symbol}{input.Symbol}", + Symbol = "GHJ-0", TokenName = input.TokenName, TotalSupply = input.TotalSupply, Decimals = 8, @@ -294,7 +306,7 @@ public async Task MultiTokenContract_Create_NFT_Input_Check_Test() } // Symbol check { - var result = await TokenContractStub.Create.SendWithExceptionAsync(new CreateInput + var result = await CreateSeedNftWithExceptionAsync(TokenContractStub, new CreateInput { Symbol = "ABC-ABC", TokenName = input.TokenName, @@ -309,7 +321,7 @@ public async Task MultiTokenContract_Create_NFT_Input_Check_Test() } // Symbol check { - var result = await TokenContractStub.Create.SendWithExceptionAsync(new CreateInput + var result = await CreateSeedNftWithExceptionAsync(TokenContractStub, new CreateInput { Symbol = "ABC-", TokenName = input.TokenName, @@ -324,7 +336,7 @@ public async Task MultiTokenContract_Create_NFT_Input_Check_Test() } // Symbol check { - var result = await TokenContractStub.Create.SendWithExceptionAsync(new CreateInput + var result = await CreateSeedNftWithExceptionAsync(TokenContractStub, new CreateInput { Symbol = "ABC-ABC-1", TokenName = input.TokenName, @@ -347,13 +359,14 @@ public async Task MultiTokenContract_Create_NFT_Input_Check_Test() Decimals = input.Decimals, Issuer = Accounts.Last().Address, IssueChainId = input.IssueChainId, - ExternalInfo = input.ExternalInfo + ExternalInfo = input.ExternalInfo, + Owner = Accounts.Last().Address }); result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); - result.TransactionResult.Error.ShouldContain("NFT issuer must be collection's issuer"); + result.TransactionResult.Error.ShouldContain("NFT owner must be collection's owner"); } { - var result = await TokenContractStubUser.Create.SendWithExceptionAsync(new CreateInput + var result = await TokenContractStub.Create.SendWithExceptionAsync( new CreateInput { Symbol = $"{NftCollection721Info.Symbol}{input.Symbol}", TokenName = input.TokenName, @@ -361,7 +374,8 @@ public async Task MultiTokenContract_Create_NFT_Input_Check_Test() Decimals = input.Decimals, Issuer = input.Issuer, IssueChainId = ChainHelper.ConvertBase58ToChainId("tDVV"), - ExternalInfo = input.ExternalInfo + ExternalInfo = input.ExternalInfo, + Owner = input.Owner }); result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); result.TransactionResult.Error.ShouldContain("NFT create ChainId must be collection's issue chainId"); @@ -371,7 +385,6 @@ public async Task MultiTokenContract_Create_NFT_Input_Check_Test() [Fact(DisplayName = "[MultiToken_Nft] Collection not exist")] public async Task MultiTokenContract_Create_NFT_Collection_NotExist() { - await CreateNativeTokenAsync(); var input = Nft721Info; var result = await TokenContractStub.Create.SendWithExceptionAsync(new CreateInput { @@ -381,7 +394,8 @@ public async Task MultiTokenContract_Create_NFT_Collection_NotExist() Decimals = input.Decimals, Issuer = input.Issuer, IssueChainId = input.IssueChainId, - ExternalInfo = input.ExternalInfo + ExternalInfo = input.ExternalInfo, + Owner = input.Owner }); result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); result.TransactionResult.Error.ShouldContain("NFT collection not exist"); @@ -400,7 +414,8 @@ public async Task MultiTokenContract_Create_NFT_Already_Exist() Decimals = input.Decimals, Issuer = input.Issuer, IssueChainId = input.IssueChainId, - ExternalInfo = input.ExternalInfo + ExternalInfo = input.ExternalInfo, + Owner = input.Owner }); result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); result.TransactionResult.Error.ShouldContain("Token already exists."); @@ -669,4 +684,212 @@ public async Task NftIssue_Approve_TransferFrom() }); ownerBalanceOutput.Balance.ShouldBe(90); } + + [Fact(DisplayName = "[token] create Test")] + public async Task CreateTokenTest() + { + var res = await CreateMutiTokenAsync(TokenContractStub, new CreateInput + { + Symbol = "XYZ", + TokenName = "Trump Digital Trading Cards #1155", + TotalSupply = TotalSupply, + Decimals = 0, + Issuer = DefaultAddress, + Owner = DefaultAddress, + IssueChainId = _chainId, + }); + res.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + // check symbol repeat + var exceptionRes = await CreateSeedNftWithExceptionAsync(TokenContractStub, new CreateInput + { + Symbol = "XYZ", + TokenName = "Trump Digital Trading Cards #1155", + TotalSupply = TotalSupply, + Decimals = 0, + Issuer = DefaultAddress, + IssueChainId = _chainId, + }); + exceptionRes.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); + exceptionRes.TransactionResult.Error.ShouldContain("Token already exists"); + // check collection symbol prefix duplicated + var failCollection = await CreateSeedNftWithExceptionAsync(TokenContractStub, new CreateInput + { + TokenName = "Trump Digital Trading Cards #1155", + TotalSupply = TotalSupply, + Decimals = 0, + Issuer = DefaultAddress, + IssueChainId = _chainId, + Symbol = "XYZ-0" + }); + failCollection.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); + failCollection.TransactionResult.Error.ShouldContain("Token already exists."); + + var successCollection = await CreateMutiTokenAsync(TokenContractStub, new CreateInput + { + TokenName = "Trump Digital Trading Cards #1155", + TotalSupply = TotalSupply, + Decimals = 0, + Issuer = DefaultAddress, + Owner = DefaultAddress, + IssueChainId = _chainId, + Symbol = "GHJ-0" + }); + successCollection.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + // check ft symbol prefix duplicated + var fTokenAsync = await CreateSeedNftWithExceptionAsync(TokenContractStub, new CreateInput + { + TokenName = "Trump Digital Trading Cards #1155", + TotalSupply = TotalSupply, + Decimals = 0, + Issuer = DefaultAddress, + IssueChainId = _chainId, + Symbol = "GHJ" + }); + fTokenAsync.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); + fTokenAsync.TransactionResult.Error.ShouldContain("Token already exists."); + + + var createInput = BuildSeedCreateInput(new CreateInput() + { + Symbol = "GH" + }); + createInput.ExternalInfo.Value["__seed_owned_symbol"] = ""; + var ownError = await TokenContractStub.Create.SendWithExceptionAsync(createInput); + ownError.TransactionResult.Error.ShouldContain("Invalid Symbol input"); + var createInputExpire = BuildSeedCreateInput(new CreateInput() + { + Symbol = "GHT" + }); + createInputExpire.ExternalInfo.Value["__seed_exp_time"] = "1234"; + var expireError = await TokenContractStub.Create.SendWithExceptionAsync(createInputExpire); + expireError.TransactionResult.Error.ShouldContain("Invalid ownedSymbol."); + // create nft + var nftSuccessAsync = await TokenContractStub.Create.SendAsync(new CreateInput + { + TokenName = "Trump Digital Trading Cards #1155", + TotalSupply = TotalSupply, + Decimals = 0, + Issuer = DefaultAddress, + Owner = DefaultAddress, + IssueChainId = _chainId, + Symbol = "GHJ-1" + }); + nftSuccessAsync.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + } + + // [Fact(DisplayName = "[feed nft] create Test")] + // public async Task FeedNftCreateTest() + // { + // // symbol expire reCreate success + // var createInput = await CreateSeedNftAsync(TokenContractStub, new CreateInput + // { + // Issuer = DefaultAddress, + // Symbol = "XYZ-0" + // }); + // + // createInput.ExternalInfo.Value["__seed_exp_time"] = "1234"; + // await TokenContractStub.ResetExternalInfo.SendAsync(new ResetExternalInfoInput() + // { + // Symbol = createInput.Symbol, + // ExternalInfo = createInput.ExternalInfo + // }); + // + // var input = BuildSeedCreateInput(new CreateInput + // { + // Issuer = DefaultAddress, + // Symbol = "XYZ-0" + // }); + // var seedRes = await TokenContractStub.Create.SendAsync(input); + // seedRes.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + // + // // owner doesn't own enough balance + // var nftAsync = await TokenContractStub.Create.SendWithExceptionAsync(GetCreateInput()); + // nftAsync.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); + // nftAsync.TransactionResult.Error.ShouldContain("Seed NFT balance is not enough"); + // // ExternalInfo check + // await TokenContractStub.Issue.SendAsync(new IssueInput + // { + // Symbol = input.Symbol, + // Amount = 1, + // Memo = "ddd", + // To = DefaultAddress + // }); + // input.ExternalInfo.Value["__seed_owned_symbol"] = "XY-0"; + // + // await TokenContractStub.ResetExternalInfo.SendAsync(new ResetExternalInfoInput() + // { + // Symbol = input.Symbol, + // ExternalInfo = input.ExternalInfo + // }); + // var inconsistentExceptionAsync = await TokenContractStub.Create.SendWithExceptionAsync(GetCreateInput()); + // inconsistentExceptionAsync.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); + // inconsistentExceptionAsync.TransactionResult.Error.ShouldContain( + // "Invalid OwnedSymbol"); + // + // input.ExternalInfo.Value["__seed_owned_symbol"] = "XYZ-0"; + // input.ExternalInfo.Value["__seed_exp_time"] = ""; + // await TokenContractStub.ResetExternalInfo.SendAsync(new ResetExternalInfoInput() + // { + // Symbol = input.Symbol, + // ExternalInfo = input.ExternalInfo + // }); + // var expireExceptionAsync = await TokenContractStub.Create.SendWithExceptionAsync( + // GetCreateInput()); + // expireExceptionAsync.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); + // expireExceptionAsync.TransactionResult.Error.ShouldContain("OwnedSymbol is expired"); + // + // input.ExternalInfo.Value["__seed_owned_symbol"] = "XYZ-0"; + // input.ExternalInfo.Value["__seed_exp_time"] = "1234"; + // await TokenContractStub.ResetExternalInfo.SendAsync(new ResetExternalInfoInput() + // { + // Symbol = input.Symbol, + // ExternalInfo = input.ExternalInfo + // }); + // var expireExceptionAsync1 = await TokenContractStub.Create.SendWithExceptionAsync(GetCreateInput()); + // expireExceptionAsync1.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); + // expireExceptionAsync1.TransactionResult.Error.ShouldContain("OwnedSymbol is expired"); + // await TokenContractStub.ResetExternalInfo.SendAsync(new ResetExternalInfoInput + // { + // Symbol = input.Symbol, + // ExternalInfo = new ExternalInfo() + // }); + // var emptyExceptionAsync = await TokenContractStub.Create.SendWithExceptionAsync(GetCreateInput()); + // emptyExceptionAsync.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); + // emptyExceptionAsync.TransactionResult.Error.ShouldContain("Invalid OwnedSymbol"); + // input.ExternalInfo.Value["__seed_owned_symbol"] = "XYZ-0"; + // input.ExternalInfo.Value["__seed_exp_time"] = TimestampHelper.GetUtcNow().AddDays(1).Seconds.ToString(); + // await TokenContractStub.ResetExternalInfo.SendAsync(new ResetExternalInfoInput + // { + // Symbol = input.Symbol, + // ExternalInfo = input.ExternalInfo + // }); + // var re = await SubmitAndApproveProposalOfDefaultParliamentWithException(TokenContractAddress, + // nameof(TokenContractStub.Create), new CreateInput + // { + // Symbol = "XYZ-0", + // Decimals = 0, + // IsBurnable = true, + // TokenName = "ELF2", + // TotalSupply = 100_000_000_000_000_000L, + // Issuer = DefaultAddress, + // ExternalInfo = new ExternalInfo() + // }); + // re.TransactionResult.Error.ShouldContain("OwnedSymbol has been created"); + // re.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); + // + // + // } + + private CreateInput GetCreateInput() + { + return new CreateInput + { + TokenName = "Trump Digital Trading Cards #1155", + TotalSupply = TotalSupply, + Decimals = 0, + Issuer = DefaultAddress, + IssueChainId = _chainId, + Symbol = "XYZ-0" + }; + } } \ No newline at end of file diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs index 137c034a2d..b4aa6e54d7 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs @@ -5,6 +5,7 @@ using AElf.Contracts.TestContract.BasicFunction; using AElf.CSharp.Core; using AElf.CSharp.Core.Extension; +using AElf.Kernel; using AElf.Standards.ACS10; using AElf.Types; using Google.Protobuf.WellKnownTypes; @@ -15,6 +16,7 @@ namespace AElf.Contracts.MultiToken; public partial class MultiTokenContractTests { + protected const string SymbolForTest = "GHJ"; [Fact(DisplayName = "[MultiToken] Transfer token test")] public async Task MultiTokenContract_Transfer_Test() { @@ -372,12 +374,13 @@ private async Task CreateTokenAndIssue(List
whitelist = null, Address i OtherBasicFunctionContractAddress, TreasuryContractAddress }; - await TokenContractStub.Create.SendAsync(new CreateInput + await CreateMutiTokenAsync(TokenContractStub,new CreateInput { Symbol = SymbolForTest, Decimals = 2, IsBurnable = true, Issuer = DefaultAddress, + Owner = DefaultAddress, TokenName = "elf test token", TotalSupply = DPoSContractConsts.LockTokenForElection * 1000000, LockWhiteList = @@ -811,14 +814,15 @@ public async Task MultiTokenContract_Burn_Invalid_Token_Test() { await CreateAndIssueMultiTokensAsync(); var unburnedTokenSymbol = "UNBURNED"; - await TokenContractStub.Create.SendAsync(new CreateInput + await CreateMutiTokenAsync(TokenContractStub, new CreateInput { Symbol = unburnedTokenSymbol, TokenName = "Name", TotalSupply = 100_000_000_000L, Decimals = 10, IsBurnable = false, - Issuer = DefaultAddress + Issuer = DefaultAddress, + Owner = DefaultAddress, }); var burnRet = await TokenContractStub.Burn.SendWithExceptionAsync(new BurnInput { @@ -940,69 +944,6 @@ await BasicFunctionContractStub.TransferTokenToContract.SendAsync( afterBalance.Balance.ShouldBe(beforeBalance.Balance.Add(transferAmount)); } - [Fact(DisplayName = "[MultiToken] ChangeTokenIssuer test")] - public async Task ChangeTokenIssuer_Test() - { - const string tokenSymbol = "PO"; - await CreateAndIssueMultiTokensAsync(); - await TokenContractStub.Create.SendAsync(new CreateInput - { - Symbol = tokenSymbol, - TokenName = "Name", - TotalSupply = 100_000_000_000L, - Decimals = 10, - IsBurnable = true, - Issuer = Accounts[1].Address - }); - var tokenIssuerStub = - GetTester(TokenContractAddress, Accounts[1].KeyPair); - - { - var issueNotExistTokenRet = await tokenIssuerStub.ChangeTokenIssuer.SendWithExceptionAsync( - new ChangeTokenIssuerInput - { - Symbol = "NOTEXIST", - NewTokenIssuer = Accounts[2].Address - }); - issueNotExistTokenRet.TransactionResult.Error.ShouldContain("invalid token symbol"); - } - { - await tokenIssuerStub.ChangeTokenIssuer.SendAsync(new ChangeTokenIssuerInput - { - Symbol = tokenSymbol, - NewTokenIssuer = Accounts[2].Address - }); - var tokenInfo = await tokenIssuerStub.GetTokenInfo.CallAsync(new GetTokenInfoInput - { - Symbol = tokenSymbol - }); - tokenInfo.Issuer.ShouldBe(Accounts[2].Address); - } - } - - [Fact(DisplayName = "[MultiToken] sender is not the token issuer")] - public async Task ChangeTokenIssuer_Without_Authorization_Test() - { - const string tokenSymbol = "PO"; - await CreateAndIssueMultiTokensAsync(); - await TokenContractStub.Create.SendAsync(new CreateInput - { - Symbol = tokenSymbol, - TokenName = "Name", - TotalSupply = 100_000_000_000L, - Decimals = 10, - IsBurnable = true, - Issuer = Accounts[1].Address - }); - var changeIssuerRet = await TokenContractStub.ChangeTokenIssuer.SendWithExceptionAsync( - new ChangeTokenIssuerInput - { - Symbol = tokenSymbol, - NewTokenIssuer = Accounts[2].Address - }); - changeIssuerRet.TransactionResult.Error.ShouldContain("permission denied"); - } - [Fact(DisplayName = "[MultiToken] Token initialize from parent chain test")] public async Task InitializeFromParent_Test() { @@ -1064,7 +1005,8 @@ public async Task Side_Chain_Creat_Token_Test() TokenName = "Ali", Decimals = 4, TotalSupply = 100_000, - Issuer = DefaultAddress + Issuer = DefaultAddress, + Owner = DefaultAddress }); createTokenRet.TransactionResult.Error.ShouldContain( "Failed to create token if side chain creator already set."); @@ -1078,7 +1020,6 @@ public async Task Side_Chain_Creat_Token_Test() public async Task CheckThreshold_With_One_Token_Test(long totalSupply, long issueAmount, long ApproveAmount, long checkAmount, bool isCheckAllowance, bool isThrowException) { - await CreateNativeTokenAsync(); var tokenA = "AITA"; await CreateAndIssueCustomizeTokenAsync(DefaultAddress, tokenA, totalSupply, issueAmount); if (ApproveAmount > 0) @@ -1122,7 +1063,6 @@ await TokenContractStub.Approve.SendAsync(new ApproveInput public async Task CheckThreshold_With_Multiple_Token_Test(long tokenACheckAmount, long tokenAApporveAmount, long tokenBCheckAmount, long tokenBApporveAmount, bool isCheckAllowance, bool isThrowException) { - await CreateNativeTokenAsync(); var tokenA = "AITA"; await CreateAndIssueCustomizeTokenAsync(DefaultAddress, tokenA, 10000, 1000); var tokenB = "AITB"; @@ -1170,10 +1110,11 @@ private async Task CreateAndIssueCustomizeTokenAsync(Address creator, string sym long issueAmount, Address to = null, params string[] otherParameters) { - await TokenContractStub.Create.SendAsync(new CreateInput + await CreateMutiTokenAsync(TokenContractStub,new CreateInput { Symbol = symbol, Issuer = creator, + Owner = creator, TokenName = symbol + "name", TotalSupply = totalSupply, Decimals = 4 @@ -1189,14 +1130,14 @@ await TokenContractStub.Issue.SendAsync(new IssueInput [Fact] public async Task ValidateTokenInfoExists_ExternalInfo_Test() { - await CreateNativeTokenAsync(); - await TokenContractStub.Create.SendAsync(new CreateInput + await CreateMutiTokenAsync(TokenContractStub, new CreateInput { Symbol = AliceCoinTokenInfo.Symbol, TokenName = AliceCoinTokenInfo.TokenName, TotalSupply = AliceCoinTokenInfo.TotalSupply, Decimals = AliceCoinTokenInfo.Decimals, Issuer = AliceCoinTokenInfo.Issuer, + Owner = AliceCoinTokenInfo.Issuer, IsBurnable = AliceCoinTokenInfo.IsBurnable, LockWhiteList = { @@ -1215,6 +1156,7 @@ await TokenContractStub.Create.SendAsync(new CreateInput TotalSupply = AliceCoinTokenInfo.TotalSupply, Decimals = AliceCoinTokenInfo.Decimals, Issuer = AliceCoinTokenInfo.Issuer, + Owner = AliceCoinTokenInfo.Issuer, IsBurnable = AliceCoinTokenInfo.IsBurnable, IssueChainId = _chainId }); @@ -1229,6 +1171,7 @@ await TokenContractStub.Create.SendAsync(new CreateInput TotalSupply = AliceCoinTokenInfo.TotalSupply, Decimals = AliceCoinTokenInfo.Decimals, Issuer = AliceCoinTokenInfo.Issuer, + Owner = AliceCoinTokenInfo.Issuer, IsBurnable = AliceCoinTokenInfo.IsBurnable, ExternalInfo = { { "key", "value" } } }); @@ -1239,14 +1182,6 @@ await TokenContractStub.Create.SendAsync(new CreateInput [Fact] public async Task CrossContractCreateToken_Test() { - await CreateNativeTokenAsync(); - await TokenContractStub.Issue.SendAsync(new IssueInput - { - Symbol = NativeToken, - To = BasicFunctionContractAddress, - Amount = 10000_00000000 - }); - var fee = await TokenContractStub.GetMethodFee.CallAsync(new StringValue { Value = "Create" }); var createTokenInput = new CreateTokenThroughMultiTokenInput { @@ -1258,22 +1193,31 @@ await TokenContractStub.Issue.SendAsync(new IssueInput TotalSupply = TotalSupply, ExternalInfo = new TestContract.BasicFunction.ExternalInfo() }; + var input = new CreateInput + { + Symbol = SeedNFTSymbolPre + 100, + Decimals = 0, + IsBurnable = true, + TokenName = "seed token" + 100, + TotalSupply = 1, + Issuer = DefaultAddress, + ExternalInfo = new ExternalInfo(), + LockWhiteList = { TokenContractAddress }, + Owner = DefaultAddress + }; + input.ExternalInfo.Value["__seed_owned_symbol"] = createTokenInput.Symbol; + input.ExternalInfo.Value["__seed_exp_time"] = TimestampHelper.GetUtcNow().AddDays(1).Seconds.ToString(); + await TokenContractStub.Create.SendAsync(input); + await TokenContractStub.Issue.SendAsync(new IssueInput + { + Symbol = input.Symbol, + Amount = 1, + Memo = "ddd", + To = BasicFunctionContractAddress + }); + var result = await BasicFunctionContractStub.CreateTokenThroughMultiToken.SendAsync(createTokenInput); result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); - - var logs = result.TransactionResult.Logs.First(l => l.Name.Equals("TransactionFeeCharged")); - var transactionFeeCharged = TransactionFeeCharged.Parser.ParseFrom(logs.NonIndexed); - transactionFeeCharged.Amount.ShouldBe(fee.Fees.First().BasicFee); - transactionFeeCharged.Symbol.ShouldBe(fee.Fees.First().Symbol); - - var tokenBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput - { Owner = TokenContractAddress, Symbol = NativeToken }); - tokenBalance.Balance.ShouldBe(0); - - var functionBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput - { Owner = BasicFunctionContractAddress, Symbol = NativeToken }); - functionBalance.Balance.ShouldBe(0); - var checkTokenInfo = await TokenContractStub.GetTokenInfo.CallAsync(new GetTokenInfoInput { Symbol = "TEST" }); checkTokenInfo.Decimals.ShouldBe(createTokenInput.Decimals); checkTokenInfo.Issuer.ShouldBe(createTokenInput.Issuer); diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenDelegationTest.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenDelegationTest.cs index ad8baa382d..47acb0a625 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenDelegationTest.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenDelegationTest.cs @@ -2,7 +2,9 @@ using System.Linq; using System.Threading.Tasks; using AElf.Types; +using Google.Protobuf.Collections; using Shouldly; +using Volo.Abp; using Xunit; namespace AElf.Contracts.MultiToken; @@ -156,20 +158,20 @@ public async Task SetTokenDelegation_addNotExistToken_Test() private async Task Initialize() { - await CreateBaseNativeTokenAsync(); await CreateTokenAsync(DefaultAddress, BasicFeeSymbol); await CreateTokenAsync(DefaultAddress, SizeFeeSymbol); } private async Task CreateTokenAsync(Address creator, string tokenSymbol, bool isBurned = true) { - await TokenContractStub.Create.SendAsync(new CreateInput + await CreateMutiTokenAsync(TokenContractStub,new CreateInput { Symbol = tokenSymbol, TokenName = tokenSymbol + " name", TotalSupply = 1000_00000000, IsBurnable = isBurned, Issuer = creator, + Owner = creator, }); } @@ -274,6 +276,7 @@ await TokenContractStub.Create.SendAsync(new CreateInput Decimals = NativeTokenInfo.Decimals, Issuer = NativeTokenInfo.Issuer, IsBurnable = NativeTokenInfo.IsBurnable, + Owner = NativeTokenInfo.Owner }); } @@ -307,4 +310,1504 @@ await TokenContractStubUser.SetTransactionFeeDelegations.SendAsync(new SetTransa output.DelegateeAddresses[0].ShouldBe(DefaultAddress); output.DelegateeAddresses[1].ShouldBe(User1Address); } + + [Fact] + public async Task SetDelegateInfos_NewDelegate_Success_Test() + { + await Initialize(); + var delegations1 = new Dictionary + { + [NativeToken] = 1000, + [BasicFeeSymbol] = 500, + [SizeFeeSymbol] = 100 + }; + var delegations2 = new Dictionary + { + [NativeToken] = 100 + }; + var delegateInfo1 = new DelegateInfo + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + Delegations = + { + delegations1 + }, + IsUnlimitedDelegate = false + }; + var delegateInfo2 = new DelegateInfo + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test2", + Delegations = + { + delegations2 + }, + IsUnlimitedDelegate = false + }; + var delegateInfo3 = new DelegateInfo + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test3", + Delegations = + { + delegations2 + }, + IsUnlimitedDelegate = true + }; + var executionResult = await TokenContractStub.SetTransactionFeeDelegateInfos.SendAsync(new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = User1Address, + DelegateInfoList = { delegateInfo1,delegateInfo2,delegateInfo3 } + }); + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test1" + }); + delegateInfoOfADelegatee.Delegations[NativeToken].ShouldBe(1000); + delegateInfoOfADelegatee.Delegations[BasicFeeSymbol].ShouldBe(500); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeFalse(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test2" + }); + delegateInfoOfADelegatee.Delegations[NativeToken].ShouldBe(100); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeFalse(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test3" + }); + delegateInfoOfADelegatee.Delegations.ShouldBeEmpty(); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeTrue(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + { + var log = TransactionFeeDelegateInfoAdded.Parser.ParseFrom(executionResult.TransactionResult.Logs + .FirstOrDefault(i => i.Name == nameof(TransactionFeeDelegateInfoAdded))?.NonIndexed); + log.Delegator.ShouldBe(User1Address); + log.Delegatee.ShouldBe(DefaultAddress); + log.DelegateTransactionList.Value.Count.ShouldBe(3); + log.DelegateTransactionList.Value[0].ContractAddress.ShouldBe(BasicFunctionContractAddress); + log.DelegateTransactionList.Value[0].MethodName.ShouldBe("test1"); + log.DelegateTransactionList.Value[1].ContractAddress.ShouldBe(BasicFunctionContractAddress); + log.DelegateTransactionList.Value[1].MethodName.ShouldBe("test2"); + log.DelegateTransactionList.Value[2].ContractAddress.ShouldBe(BasicFunctionContractAddress); + log.DelegateTransactionList.Value[2].MethodName.ShouldBe("test3"); + } + + } + [Fact] + public async Task SetDelegateInfos_NewOrUpdateDelegate_Success_Test() + { + await SetDelegateInfos_NewDelegate_Success_Test(); + var newDelegations = new Dictionary + { + [NativeToken] = 10, + [BasicFeeSymbol] = 20 + }; + var delegations2 = new Dictionary + { + [NativeToken] = 100, + [SizeFeeSymbol] = 30 + }; + var delegateInfo1 = new DelegateInfo + { + ContractAddress = BasicContractZeroAddress, + MethodName = "test1", + Delegations = + { + newDelegations + }, + IsUnlimitedDelegate = false + }; + var delegateInfo2 = new DelegateInfo + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test2", + Delegations = + { + delegations2 + }, + IsUnlimitedDelegate = false + }; + var executionResult = await TokenContractStub.SetTransactionFeeDelegateInfos.SendAsync(new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = User1Address, + DelegateInfoList = { delegateInfo1,delegateInfo2 } + }); + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicContractZeroAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test1" + }); + delegateInfoOfADelegatee.Delegations[NativeToken].ShouldBe(10); + delegateInfoOfADelegatee.Delegations[BasicFeeSymbol].ShouldBe(20); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeFalse(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test2" + }); + delegateInfoOfADelegatee.Delegations[NativeToken].ShouldBe(100); + delegateInfoOfADelegatee.Delegations[SizeFeeSymbol].ShouldBe(30); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeFalse(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + { + var log = TransactionFeeDelegateInfoAdded.Parser.ParseFrom(executionResult.TransactionResult.Logs + .FirstOrDefault(i => i.Name == nameof(TransactionFeeDelegateInfoAdded))?.NonIndexed); + log.Delegator.ShouldBe(User1Address); + log.Delegatee.ShouldBe(DefaultAddress); + log.DelegateTransactionList.Value.Count.ShouldBe(1); + log.DelegateTransactionList.Value[0].ContractAddress.ShouldBe(BasicContractZeroAddress); + log.DelegateTransactionList.Value[0].MethodName.ShouldBe("test1"); + } + { + var log = TransactionFeeDelegateInfoUpdated.Parser.ParseFrom(executionResult.TransactionResult.Logs + .FirstOrDefault(i => i.Name == nameof(TransactionFeeDelegateInfoUpdated))?.NonIndexed); + log.Delegator.ShouldBe(User1Address); + log.Delegatee.ShouldBe(DefaultAddress); + log.DelegateTransactionList.Value.Count.ShouldBe(1); + log.DelegateTransactionList.Value[0].ContractAddress.ShouldBe(BasicFunctionContractAddress); + log.DelegateTransactionList.Value[0].MethodName.ShouldBe("test2"); + } + + } + [Fact] + public async Task SetDelegateInfos_UpdateDelegate_Success_Test() + { + await SetDelegateInfos_NewOrUpdateDelegate_Success_Test(); + var delegations1 = new Dictionary + { + [NativeToken] = 300, + [BasicFeeSymbol] = 200, + [SizeFeeSymbol] = 100 + }; + var delegations3 = new Dictionary + { + [NativeToken] = 100, + [BasicFeeSymbol] = 200, + }; + var delegateInfo1 = new DelegateInfo + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + Delegations = + { + delegations1 + }, + IsUnlimitedDelegate = false + }; + var delegateInfo2 = new DelegateInfo + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test2", + IsUnlimitedDelegate = true + }; + var delegateInfo3 = new DelegateInfo + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test3", + Delegations = + { + delegations3 + }, + IsUnlimitedDelegate = false + }; + var delegateInfo4 = new DelegateInfo + { + ContractAddress = BasicContractZeroAddress, + MethodName = "test1", + IsUnlimitedDelegate = true + }; + await TokenContractStub.SetTransactionFeeDelegateInfos.SendAsync(new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = User1Address, + DelegateInfoList = { delegateInfo1,delegateInfo2,delegateInfo3,delegateInfo4 } + }); + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test1" + }); + delegateInfoOfADelegatee.Delegations[NativeToken].ShouldBe(300); + delegateInfoOfADelegatee.Delegations[BasicFeeSymbol].ShouldBe(200); + delegateInfoOfADelegatee.Delegations[SizeFeeSymbol].ShouldBe(100); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeFalse(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test2" + }); + delegateInfoOfADelegatee.Delegations.ShouldBeEmpty(); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeTrue(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test3" + }); + delegateInfoOfADelegatee.Delegations[NativeToken].ShouldBe(100); + delegateInfoOfADelegatee.Delegations[BasicFeeSymbol].ShouldBe(200); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeFalse(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicContractZeroAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test1" + }); + delegateInfoOfADelegatee.Delegations.ShouldBeEmpty(); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeTrue(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + } + [Fact] + public async Task SetDelegateInfos_UpdateDelegate_RemoveDelegation_Success_Test() + { + await SetDelegateInfos_NewOrUpdateDelegate_Success_Test(); + var delegations1 = new Dictionary + { + [NativeToken] = 300, + [BasicFeeSymbol] = 200, + [SizeFeeSymbol] = -1 + }; + var delegations2 = new Dictionary + { + [NativeToken] = 0, + [SizeFeeSymbol] = 200, + }; + var delegations3 = new Dictionary + { + [NativeToken] = 5, + [BasicFeeSymbol] = 0 + }; + var delegateInfo1 = new DelegateInfo + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + Delegations = + { + delegations1 + }, + IsUnlimitedDelegate = false + }; + var delegateInfo2 = new DelegateInfo + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test2", + Delegations = + { + delegations2 + }, + IsUnlimitedDelegate = false + }; + var delegateInfo3 = new DelegateInfo + { + ContractAddress = BasicContractZeroAddress, + MethodName = "test1", + Delegations = + { + delegations3 + }, + IsUnlimitedDelegate = false + }; + var executionResult = await TokenContractStub.SetTransactionFeeDelegateInfos.SendAsync(new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = User1Address, + DelegateInfoList = { delegateInfo1,delegateInfo2,delegateInfo3 } + }); + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test1" + }); + delegateInfoOfADelegatee.Delegations.Count.ShouldBe(2); + delegateInfoOfADelegatee.Delegations[NativeToken].ShouldBe(300); + delegateInfoOfADelegatee.Delegations[BasicFeeSymbol].ShouldBe(200); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeFalse(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test2" + }); + delegateInfoOfADelegatee.Delegations.Count.ShouldBe(1); + delegateInfoOfADelegatee.Delegations[SizeFeeSymbol].ShouldBe(200); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeFalse(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicContractZeroAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test1" + }); + delegateInfoOfADelegatee.Delegations.Count.ShouldBe(1); + delegateInfoOfADelegatee.Delegations[NativeToken].ShouldBe(5); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeFalse(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + { + var log = TransactionFeeDelegateInfoUpdated.Parser.ParseFrom(executionResult.TransactionResult.Logs + .FirstOrDefault(i => i.Name == nameof(TransactionFeeDelegateInfoUpdated))?.NonIndexed); + log.Delegator.ShouldBe(User1Address); + log.Delegatee.ShouldBe(DefaultAddress); + log.DelegateTransactionList.Value.Count.ShouldBe(3); + log.DelegateTransactionList.Value[0].ContractAddress.ShouldBe(BasicFunctionContractAddress); + log.DelegateTransactionList.Value[0].MethodName.ShouldBe("test1"); + log.DelegateTransactionList.Value[1].ContractAddress.ShouldBe(BasicFunctionContractAddress); + log.DelegateTransactionList.Value[1].MethodName.ShouldBe("test2"); + log.DelegateTransactionList.Value[2].ContractAddress.ShouldBe(BasicContractZeroAddress); + log.DelegateTransactionList.Value[2].MethodName.ShouldBe("test1"); + } + } + [Fact] + public async Task SetDelegateInfos_UpdateDelegate_RemoveDelegateInfo_Success_Test() + { + await SetDelegateInfos_NewDelegate_Success_Test(); + var delegations1 = new Dictionary + { + [NativeToken] = -1, + [BasicFeeSymbol] = -1, + [SizeFeeSymbol] = -1 + }; + var delegations2 = new Dictionary + { + [NativeToken] = -1, + [BasicFeeSymbol] = 200, + }; + var delegateInfo1 = new DelegateInfo + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + Delegations = + { + delegations1 + }, + IsUnlimitedDelegate = false + }; + var delegateInfo2 = new DelegateInfo + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test2", + Delegations = + { + delegations2 + }, + IsUnlimitedDelegate = false + }; + var executionResult = await TokenContractStub.SetTransactionFeeDelegateInfos.SendAsync(new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = User1Address, + DelegateInfoList = { delegateInfo1,delegateInfo2 } + }); + { + var delegateeAddress = await TokenContractStub.GetTransactionFeeDelegateeList.CallAsync( + new GetTransactionFeeDelegateeListInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + MethodName = "test1" + }); + delegateeAddress.DelegateeAddresses.Count.ShouldBe(0); + } + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test1" + }); + delegateInfoOfADelegatee.ShouldBe(new TransactionFeeDelegations()); + } + { + var delegateeAddress = await TokenContractStub.GetTransactionFeeDelegateeList.CallAsync( + new GetTransactionFeeDelegateeListInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + MethodName = "test2" + }); + delegateeAddress.DelegateeAddresses.Count.ShouldBe(1); + } + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test2" + }); + delegateInfoOfADelegatee.Delegations.Count.ShouldBe(1); + delegateInfoOfADelegatee.Delegations[BasicFeeSymbol].ShouldBe(200); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeFalse(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + { + var log = TransactionFeeDelegateInfoCancelled.Parser.ParseFrom(executionResult.TransactionResult.Logs + .FirstOrDefault(i => i.Name == nameof(TransactionFeeDelegateInfoCancelled))?.NonIndexed); + log.Delegator.ShouldBe(User1Address); + log.Delegatee.ShouldBe(DefaultAddress); + log.DelegateTransactionList.Value.Count.ShouldBe(1); + log.DelegateTransactionList.Value[0].ContractAddress.ShouldBe(BasicFunctionContractAddress); + log.DelegateTransactionList.Value[0].MethodName.ShouldBe("test1"); + } + } + + [Fact] + public async Task SetDelegateInfos_AddOrUpdateOrRemoveDelegate_Success_Test() + { + await SetDelegateInfos_NewOrUpdateDelegate_Success_Test(); + var delegations1 = new Dictionary + { + [NativeToken] = 300, + [BasicFeeSymbol] = 200, + [SizeFeeSymbol] = 0 + }; + var delegations4 = new Dictionary + { + [NativeToken] = 60, + }; + var delegations5 = new Dictionary + { + [NativeToken] = 0, + }; + var delegateInfo1 = new DelegateInfo + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + Delegations = + { + delegations1 + }, + IsUnlimitedDelegate = false + }; + var delegateInfo2 = new DelegateInfo + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test2", + IsUnlimitedDelegate = true + }; + var delegateInfo4 = new DelegateInfo + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test4", + Delegations = + { + delegations4 + }, + IsUnlimitedDelegate = false + }; + var delegateInfo5 = new DelegateInfo + { + ContractAddress = BasicContractZeroAddress, + MethodName = "test1", + Delegations = + { + delegations5 + }, + IsUnlimitedDelegate = false + }; + var executionResult = await TokenContractStub.SetTransactionFeeDelegateInfos.SendAsync(new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = User1Address, + DelegateInfoList = { delegateInfo1,delegateInfo2,delegateInfo4,delegateInfo5 } + }); + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test1" + }); + delegateInfoOfADelegatee.Delegations.Count.ShouldBe(2); + delegateInfoOfADelegatee.Delegations[NativeToken].ShouldBe(300); + delegateInfoOfADelegatee.Delegations[BasicFeeSymbol].ShouldBe(200); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeFalse(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test2" + }); + delegateInfoOfADelegatee.Delegations.ShouldBeEmpty(); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeTrue(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test4" + }); + delegateInfoOfADelegatee.Delegations[NativeToken].ShouldBe(60); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeFalse(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicContractZeroAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test1" + }); + delegateInfoOfADelegatee.Delegations.Count.ShouldBe(1); + delegateInfoOfADelegatee.Delegations[BasicFeeSymbol].ShouldBe(20); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeFalse(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + { + { + var log = TransactionFeeDelegateInfoAdded.Parser.ParseFrom(executionResult.TransactionResult.Logs + .FirstOrDefault(i => i.Name == nameof(TransactionFeeDelegateInfoAdded))?.NonIndexed); + log.Delegator.ShouldBe(User1Address); + log.Delegatee.ShouldBe(DefaultAddress); + log.DelegateTransactionList.Value.Count.ShouldBe(1); + log.DelegateTransactionList.Value[0].ContractAddress.ShouldBe(BasicFunctionContractAddress); + log.DelegateTransactionList.Value[0].MethodName.ShouldBe("test4"); + } + { + var log = TransactionFeeDelegateInfoUpdated.Parser.ParseFrom(executionResult.TransactionResult.Logs + .FirstOrDefault(i => i.Name == nameof(TransactionFeeDelegateInfoUpdated))?.NonIndexed); + log.Delegator.ShouldBe(User1Address); + log.Delegatee.ShouldBe(DefaultAddress); + log.DelegateTransactionList.Value.Count.ShouldBe(3); + log.DelegateTransactionList.Value[0].ContractAddress.ShouldBe(BasicFunctionContractAddress); + log.DelegateTransactionList.Value[0].MethodName.ShouldBe("test1"); + log.DelegateTransactionList.Value[1].ContractAddress.ShouldBe(BasicFunctionContractAddress); + log.DelegateTransactionList.Value[1].MethodName.ShouldBe("test2"); + log.DelegateTransactionList.Value[2].ContractAddress.ShouldBe(BasicContractZeroAddress); + log.DelegateTransactionList.Value[2].MethodName.ShouldBe("test1"); + } + } + } + + [Fact] + public async Task SetDelegateInfos_MultiDelegateeAndSameTransaction_Success_Test() + { + await Initialize(); + var delegations1 = new Dictionary + { + [NativeToken] = 1000, + [BasicFeeSymbol] = 500, + [SizeFeeSymbol] = 100 + }; + var delegateInfo1 = new DelegateInfo + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + Delegations = + { + delegations1 + }, + IsUnlimitedDelegate = false + }; + //Default -> User1 + await TokenContractStub.SetTransactionFeeDelegateInfos.SendAsync(new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = User1Address, + DelegateInfoList = { delegateInfo1 } + }); + //User2 -> User1 + await TokenContractStubDelegate.SetTransactionFeeDelegateInfos.SendAsync(new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = User1Address, + DelegateInfoList = { delegateInfo1 } + }); + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test1" + }); + delegateInfoOfADelegatee.Delegations.Count.ShouldBe(3); + delegateInfoOfADelegatee.Delegations[NativeToken].ShouldBe(1000); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeFalse(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = User2Address, + MethodName = "test1" + }); + delegateInfoOfADelegatee.Delegations.Count.ShouldBe(3); + delegateInfoOfADelegatee.Delegations[NativeToken].ShouldBe(1000); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeFalse(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + { + var delegateeList = await TokenContractStub.GetTransactionFeeDelegateeList.CallAsync( + new GetTransactionFeeDelegateeListInput + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + DelegatorAddress = User1Address + }); + delegateeList.DelegateeAddresses.Count.ShouldBe(2); + delegateeList.DelegateeAddresses[0].ShouldBe(DefaultAddress); + delegateeList.DelegateeAddresses[1].ShouldBe(User2Address); + } + } + + [Fact] + public async Task SetDelegateInfos_MultiDelegatorAndSameTransaction_Success_Test() + { + await Initialize(); + var delegations1 = new Dictionary + { + [NativeToken] = 1000, + [BasicFeeSymbol] = 500, + [SizeFeeSymbol] = 100 + }; + var delegateInfo1 = new DelegateInfo + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + Delegations = + { + delegations1 + }, + IsUnlimitedDelegate = false + }; + //Default -> User1 + await TokenContractStub.SetTransactionFeeDelegateInfos.SendAsync(new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = User1Address, + DelegateInfoList = { delegateInfo1 } + }); + //Default -> User2 + await TokenContractStub.SetTransactionFeeDelegateInfos.SendAsync(new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = User2Address, + DelegateInfoList = { delegateInfo1 } + }); + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test1" + }); + delegateInfoOfADelegatee.Delegations.Count.ShouldBe(3); + delegateInfoOfADelegatee.Delegations[NativeToken].ShouldBe(1000); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeFalse(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User2Address, + DelegateeAddress = DefaultAddress, + MethodName = "test1" + }); + delegateInfoOfADelegatee.Delegations.Count.ShouldBe(3); + delegateInfoOfADelegatee.Delegations[NativeToken].ShouldBe(1000); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeFalse(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + { + var delegateeList = await TokenContractStub.GetTransactionFeeDelegateeList.CallAsync( + new GetTransactionFeeDelegateeListInput + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + DelegatorAddress = User1Address + }); + delegateeList.DelegateeAddresses.Count.ShouldBe(1); + delegateeList.DelegateeAddresses[0].ShouldBe(DefaultAddress); + } + { + var delegateeList = await TokenContractStub.GetTransactionFeeDelegateeList.CallAsync( + new GetTransactionFeeDelegateeListInput + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + DelegatorAddress = User2Address + }); + delegateeList.DelegateeAddresses.Count.ShouldBe(1); + delegateeList.DelegateeAddresses[0].ShouldBe(DefaultAddress); + } + } + [Fact] + public async Task SetDelegateInfos_InvalidInput_Failed_Test() + { + await Initialize(); + var delegations1 = new Dictionary + { + [NativeToken] = 1000, + [BasicFeeSymbol] = 500, + [SizeFeeSymbol] = 100 + }; + var delegateInfo = new DelegateInfo + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + Delegations = { delegations1 } + }; + var delegateInfo1 = new DelegateInfo + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + IsUnlimitedDelegate = false + }; + var delegateInfo2 = new DelegateInfo + { + MethodName = "test1", + Delegations = { delegations1 }, + IsUnlimitedDelegate = false + }; + var delegateInfo3 = new DelegateInfo + { + ContractAddress = BasicFunctionContractAddress, + Delegations = { delegations1 }, + IsUnlimitedDelegate = false + }; + { + await TokenContractStub.SetTransactionFeeDelegateInfos.SendAsync(new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = User1Address, + DelegateInfoList = { delegateInfo } + }); + } + { + var transactionFeeDelegation = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress + }); + transactionFeeDelegation.IsUnlimitedDelegate.ShouldBeFalse(); + transactionFeeDelegation.Delegations.Count.ShouldBe(3); + transactionFeeDelegation.Delegations[NativeToken].ShouldBe(1000); + } + { + var executionResult = await TokenContractStub.SetTransactionFeeDelegateInfos.SendWithExceptionAsync(new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = User1Address, + DelegateInfoList = { delegateInfo1 } + }); + executionResult.TransactionResult.Error.ShouldContain("Delegation cannot be null."); + } + { + var executionResult = await TokenContractStub.SetTransactionFeeDelegateInfos.SendWithExceptionAsync(new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = User1Address, + DelegateInfoList = { delegateInfo2 } + }); + executionResult.TransactionResult.Error.ShouldContain("Invalid contract address and method name."); + } + { + var executionResult = await TokenContractStub.SetTransactionFeeDelegateInfos.SendWithExceptionAsync(new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = User1Address, + DelegateInfoList = { delegateInfo3 } + }); + executionResult.TransactionResult.Error.ShouldContain("Invalid contract address and method name."); + } + { + var executionResult = await TokenContractStub.SetTransactionFeeDelegateInfos.SendWithExceptionAsync(new SetTransactionFeeDelegateInfosInput + { + DelegateInfoList = { delegateInfo } + }); + executionResult.TransactionResult.Error.ShouldContain("Delegator address and delegate info cannot be null."); + } + { + var executionResult = await TokenContractStub.SetTransactionFeeDelegateInfos.SendWithExceptionAsync(new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = User1Address, + }); + executionResult.TransactionResult.Error.ShouldContain("Delegator address and delegate info cannot be null."); + } + } + + [Fact] + public async Task RemoveTransactionFeeDelegateeInfos_Success_Test() + { + await SetDelegateInfos_NewDelegate_Success_Test(); + var delegations1 = new Dictionary + { + [NativeToken] = 300, + [BasicFeeSymbol] = 200 + }; + var delegateInfo1 = new DelegateInfo + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + Delegations = + { + delegations1 + }, + IsUnlimitedDelegate = false + }; + await TokenContractStubDelegate.SetTransactionFeeDelegateInfos.SendAsync(new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = User1Address, + DelegateInfoList = { delegateInfo1 } + }); + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test1" + }); + delegateInfoOfADelegatee.Delegations[NativeToken].ShouldBe(1000); + delegateInfoOfADelegatee.Delegations[BasicFeeSymbol].ShouldBe(500); + delegateInfoOfADelegatee.IsUnlimitedDelegate.ShouldBeFalse(); + delegateInfoOfADelegatee.BlockHeight.ShouldBeGreaterThan(0); + } + var delegationTransactionList = new RepeatedField + { + new DelegateTransaction + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1" + }, + new DelegateTransaction + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test3" + } + }; + await TokenContractStubUser.RemoveTransactionFeeDelegateeInfos.SendAsync(new RemoveTransactionFeeDelegateeInfosInput + { + DelegateeAddress = DefaultAddress, + DelegateTransactionList = { delegationTransactionList } + }); + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test1" + }); + delegateInfoOfADelegatee.ShouldBe(new TransactionFeeDelegations()); + } + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test2" + }); + delegateInfoOfADelegatee.Delegations[NativeToken] = 100; + delegateInfoOfADelegatee.IsUnlimitedDelegate = false; + } + { + var delegateeAddress = await TokenContractStub.GetTransactionFeeDelegateeList.CallAsync( + new GetTransactionFeeDelegateeListInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + MethodName = "test1" + }); + delegateeAddress.DelegateeAddresses.Count.ShouldBe(1); + delegateeAddress.DelegateeAddresses[0].ShouldBe(User2Address); + } + } + + [Fact] + public async Task RemoveTransactionFeeDelegateeInfos_SingleTransaction_Success_Test() + { + await SetDelegateInfos_NewDelegate_Success_Test(); + var delegationTransactionList = new RepeatedField + { + new DelegateTransaction + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1" + } + }; + var executionResult = await TokenContractStubUser.RemoveTransactionFeeDelegateeInfos.SendAsync(new RemoveTransactionFeeDelegateeInfosInput + { + DelegateeAddress = DefaultAddress, + DelegateTransactionList = { delegationTransactionList } + }); + { + var transactionDelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress + }); + transactionDelegatee.ShouldBe(new TransactionFeeDelegations()); + } + { + var delegateeList = await TokenContractStub.GetTransactionFeeDelegateeList.CallAsync( + new GetTransactionFeeDelegateeListInput + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + DelegatorAddress = User1Address, + }); + delegateeList.DelegateeAddresses.Count.ShouldBe(0); + } + { + var log = TransactionFeeDelegateInfoCancelled.Parser.ParseFrom(executionResult.TransactionResult.Logs + .FirstOrDefault(i => i.Name == nameof(TransactionFeeDelegateInfoCancelled))?.NonIndexed); + log.Delegatee.ShouldBe(DefaultAddress); + log.Delegator.ShouldBe(User1Address); + delegationTransactionList.Count.ShouldBe(1); + delegationTransactionList[0].ContractAddress.ShouldBe(BasicFunctionContractAddress); + delegationTransactionList[0].MethodName.ShouldBe("test1"); + } + } + [Fact] + public async Task RemoveTransactionFeeDelegateeInfos_MultiTransaction_Success_Test() + { + await SetDelegateInfos_NewOrUpdateDelegate_Success_Test(); + var delegationTransactionList = new RepeatedField + { + new DelegateTransaction + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1" + }, + new DelegateTransaction + { + ContractAddress = BasicContractZeroAddress, + MethodName = "test1" + } + }; + var executionResult = await TokenContractStubUser.RemoveTransactionFeeDelegateeInfos.SendAsync(new RemoveTransactionFeeDelegateeInfosInput + { + DelegateeAddress = DefaultAddress, + DelegateTransactionList = { delegationTransactionList } + }); + { + var transactionDelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress + }); + transactionDelegatee.ShouldBe(new TransactionFeeDelegations()); + } + { + var transactionDelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicContractZeroAddress, + MethodName = "test1", + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress + }); + transactionDelegatee.ShouldBe(new TransactionFeeDelegations()); + } + { + var delegateeList = await TokenContractStub.GetTransactionFeeDelegateeList.CallAsync( + new GetTransactionFeeDelegateeListInput + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + DelegatorAddress = User1Address, + }); + delegateeList.DelegateeAddresses.Count.ShouldBe(0); + } + { + var delegateeList = await TokenContractStub.GetTransactionFeeDelegateeList.CallAsync( + new GetTransactionFeeDelegateeListInput + { + ContractAddress = BasicContractZeroAddress, + MethodName = "test1", + DelegatorAddress = User1Address, + }); + delegateeList.DelegateeAddresses.Count.ShouldBe(0); + } + { + var log = TransactionFeeDelegateInfoCancelled.Parser.ParseFrom(executionResult.TransactionResult.Logs + .FirstOrDefault(i => i.Name == nameof(TransactionFeeDelegateInfoCancelled))?.NonIndexed); + log.Delegatee.ShouldBe(DefaultAddress); + log.Delegator.ShouldBe(User1Address); + delegationTransactionList.Count.ShouldBe(2); + delegationTransactionList[0].ContractAddress.ShouldBe(BasicFunctionContractAddress); + delegationTransactionList[0].MethodName.ShouldBe("test1"); + delegationTransactionList[1].ContractAddress.ShouldBe(BasicContractZeroAddress); + delegationTransactionList[1].MethodName.ShouldBe("test1"); + } + } + [Fact] + public async Task RemoveTransactionFeeDelegateeInfos_NotExistTransaction_Success_Test() + { + await SetDelegateInfos_NewOrUpdateDelegate_Success_Test(); + var delegationTransactionList = new RepeatedField + { + new DelegateTransaction + { + ContractAddress = TokenContractAddress, + MethodName = "transfer" + } + }; + var executionResult = await TokenContractStubUser.RemoveTransactionFeeDelegateeInfos.SendAsync(new RemoveTransactionFeeDelegateeInfosInput + { + DelegateeAddress = DefaultAddress, + DelegateTransactionList = { delegationTransactionList } + }); + var executionResult1 = await TokenContractStubUser.RemoveTransactionFeeDelegateeInfos.SendAsync(new RemoveTransactionFeeDelegateeInfosInput + { + DelegateeAddress = User2Address, + DelegateTransactionList = { delegationTransactionList } + }); + { + var transactionDelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = TokenContractAddress, + MethodName = "transfer", + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress + }); + transactionDelegatee.ShouldBe(new TransactionFeeDelegations()); + } + { + var delegateeList = await TokenContractStub.GetTransactionFeeDelegateeList.CallAsync( + new GetTransactionFeeDelegateeListInput + { + ContractAddress = TokenContractAddress, + MethodName = "transfer", + DelegatorAddress = User1Address, + }); + delegateeList.DelegateeAddresses.Count.ShouldBe(0); + } + { + var transactionDelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = TokenContractAddress, + MethodName = "transfer", + DelegatorAddress = User1Address, + DelegateeAddress = User2Address + }); + transactionDelegatee.ShouldBe(new TransactionFeeDelegations()); + } + { + var delegateeList = await TokenContractStub.GetTransactionFeeDelegateeList.CallAsync( + new GetTransactionFeeDelegateeListInput + { + ContractAddress = TokenContractAddress, + MethodName = "transfer", + DelegatorAddress = User1Address, + }); + delegateeList.DelegateeAddresses.Count.ShouldBe(0); + } + } + + [Fact] + public async Task RemoveTransactionFeeDelegateeInfos_InvalidInput_Failed_Test() + { + await SetDelegateInfos_NewOrUpdateDelegate_Success_Test(); + var delegationTransactionList = new RepeatedField + { + new DelegateTransaction + { + ContractAddress = BasicContractZeroAddress + }, + new DelegateTransaction + { + MethodName = "jsh&&&" + }, + new DelegateTransaction + { + + } + }; + var executionResult = await TokenContractStubUser.RemoveTransactionFeeDelegateeInfos.SendWithExceptionAsync(new RemoveTransactionFeeDelegateeInfosInput + { + DelegateeAddress = DefaultAddress, + DelegateTransactionList = { delegationTransactionList } + }); + executionResult.TransactionResult.Error.ShouldContain("Invalid contract address and method name."); + } + [Fact] + public async Task RemoveTransactionFeeDelegatorInfos_Success_Test() + { + await SetDelegateInfos_NewDelegate_Success_Test(); + var delegations1 = new Dictionary + { + [NativeToken] = 300, + [BasicFeeSymbol] = 200 + }; + var delegateInfo1 = new DelegateInfo + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + Delegations = + { + delegations1 + }, + IsUnlimitedDelegate = false + }; + await TokenContractStub.SetTransactionFeeDelegateInfos.SendAsync(new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = User2Address, + DelegateInfoList = { delegateInfo1 } + }); + var delegationTransactionList = new RepeatedField + { + new DelegateTransaction + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1" + }, + new DelegateTransaction + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test3" + } + }; + await TokenContractStub.RemoveTransactionFeeDelegatorInfos.SendAsync(new RemoveTransactionFeeDelegatorInfosInput + { + DelegatorAddress = User1Address, + DelegateTransactionList = { delegationTransactionList } + }); + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test1" + }); + delegateInfoOfADelegatee.ShouldBe(new TransactionFeeDelegations()); + } + { + var delegateInfoOfADelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress, + MethodName = "test2" + }); + delegateInfoOfADelegatee.Delegations[NativeToken] = 100; + delegateInfoOfADelegatee.IsUnlimitedDelegate = false; + } + { + var delegateeAddress = await TokenContractStub.GetTransactionFeeDelegateeList.CallAsync( + new GetTransactionFeeDelegateeListInput + { + ContractAddress = BasicFunctionContractAddress, + DelegatorAddress = User2Address, + MethodName = "test1" + }); + delegateeAddress.DelegateeAddresses.Count.ShouldBe(1); + delegateeAddress.DelegateeAddresses[0].ShouldBe(DefaultAddress); + } + } + [Fact] + public async Task RemoveTransactionFeeDelegatorInfos_SingleTransaction_Success_Test() + { + await SetDelegateInfos_NewDelegate_Success_Test(); + var delegationTransactionList = new RepeatedField + { + new DelegateTransaction + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1" + } + }; + var executionResult = await TokenContractStub.RemoveTransactionFeeDelegatorInfos.SendAsync(new RemoveTransactionFeeDelegatorInfosInput + { + DelegatorAddress = User1Address, + DelegateTransactionList = { delegationTransactionList } + }); + { + var transactionDelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress + }); + transactionDelegatee.ShouldBe(new TransactionFeeDelegations()); + } + { + var delegateeList = await TokenContractStub.GetTransactionFeeDelegateeList.CallAsync( + new GetTransactionFeeDelegateeListInput + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + DelegatorAddress = User1Address, + }); + delegateeList.DelegateeAddresses.Count.ShouldBe(0); + } + { + var log = TransactionFeeDelegateInfoCancelled.Parser.ParseFrom(executionResult.TransactionResult.Logs + .FirstOrDefault(i => i.Name == nameof(TransactionFeeDelegateInfoCancelled))?.NonIndexed); + log.Delegatee.ShouldBe(DefaultAddress); + log.Delegator.ShouldBe(User1Address); + delegationTransactionList.Count.ShouldBe(1); + delegationTransactionList[0].ContractAddress.ShouldBe(BasicFunctionContractAddress); + delegationTransactionList[0].MethodName.ShouldBe("test1"); + } + } + [Fact] + public async Task RemoveTransactionFeeDelegatorInfos_MultiTransaction_Success_Test() + { + await SetDelegateInfos_NewOrUpdateDelegate_Success_Test(); + var delegationTransactionList = new RepeatedField + { + new DelegateTransaction + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1" + }, + new DelegateTransaction + { + ContractAddress = BasicContractZeroAddress, + MethodName = "test1" + } + }; + var executionResult = await TokenContractStub.RemoveTransactionFeeDelegatorInfos.SendAsync(new RemoveTransactionFeeDelegatorInfosInput + { + DelegatorAddress = User1Address, + DelegateTransactionList = { delegationTransactionList } + }); + { + var transactionDelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress + }); + transactionDelegatee.ShouldBe(new TransactionFeeDelegations()); + } + { + var transactionDelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = BasicContractZeroAddress, + MethodName = "test1", + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress + }); + transactionDelegatee.ShouldBe(new TransactionFeeDelegations()); + } + { + var delegateeList = await TokenContractStub.GetTransactionFeeDelegateeList.CallAsync( + new GetTransactionFeeDelegateeListInput + { + ContractAddress = BasicFunctionContractAddress, + MethodName = "test1", + DelegatorAddress = User1Address, + }); + delegateeList.DelegateeAddresses.Count.ShouldBe(0); + } + { + var delegateeList = await TokenContractStub.GetTransactionFeeDelegateeList.CallAsync( + new GetTransactionFeeDelegateeListInput + { + ContractAddress = BasicContractZeroAddress, + MethodName = "test1", + DelegatorAddress = User1Address, + }); + delegateeList.DelegateeAddresses.Count.ShouldBe(0); + } + { + var log = TransactionFeeDelegateInfoCancelled.Parser.ParseFrom(executionResult.TransactionResult.Logs + .FirstOrDefault(i => i.Name == nameof(TransactionFeeDelegateInfoCancelled))?.NonIndexed); + log.Delegatee.ShouldBe(DefaultAddress); + log.Delegator.ShouldBe(User1Address); + delegationTransactionList.Count.ShouldBe(2); + delegationTransactionList[0].ContractAddress.ShouldBe(BasicFunctionContractAddress); + delegationTransactionList[0].MethodName.ShouldBe("test1"); + delegationTransactionList[1].ContractAddress.ShouldBe(BasicContractZeroAddress); + delegationTransactionList[1].MethodName.ShouldBe("test1"); + } + } + [Fact] + public async Task RemoveTransactionFeeDelegatorInfos_NotExistTransaction_Success_Test() + { + await SetDelegateInfos_NewOrUpdateDelegate_Success_Test(); + var delegationTransactionList = new RepeatedField + { + new DelegateTransaction + { + ContractAddress = TokenContractAddress, + MethodName = "transfer" + } + }; + var executionResult = await TokenContractStub.RemoveTransactionFeeDelegatorInfos.SendAsync(new RemoveTransactionFeeDelegatorInfosInput + { + DelegatorAddress = User1Address, + DelegateTransactionList = { delegationTransactionList } + }); + var executionResult1 = await TokenContractStub.RemoveTransactionFeeDelegatorInfos.SendAsync(new RemoveTransactionFeeDelegatorInfosInput + { + DelegatorAddress = User2Address, + DelegateTransactionList = { delegationTransactionList } + }); + { + var transactionDelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = TokenContractAddress, + MethodName = "transfer", + DelegatorAddress = User1Address, + DelegateeAddress = DefaultAddress + }); + transactionDelegatee.ShouldBe(new TransactionFeeDelegations()); + } + { + var delegateeList = await TokenContractStub.GetTransactionFeeDelegateeList.CallAsync( + new GetTransactionFeeDelegateeListInput + { + ContractAddress = TokenContractAddress, + MethodName = "transfer", + DelegatorAddress = User1Address, + }); + delegateeList.DelegateeAddresses.Count.ShouldBe(0); + } + { + var transactionDelegatee = await TokenContractStub.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + ContractAddress = TokenContractAddress, + MethodName = "transfer", + DelegatorAddress = User2Address, + DelegateeAddress = DefaultAddress + }); + transactionDelegatee.ShouldBe(new TransactionFeeDelegations()); + } + { + var delegateeList = await TokenContractStub.GetTransactionFeeDelegateeList.CallAsync( + new GetTransactionFeeDelegateeListInput + { + ContractAddress = TokenContractAddress, + MethodName = "transfer", + DelegatorAddress = User2Address, + }); + delegateeList.DelegateeAddresses.Count.ShouldBe(0); + } + } + + [Fact] + public async Task RemoveTransactionFeeDelegatorInfos_InvalidInput_Failed_Test() + { + await SetDelegateInfos_NewOrUpdateDelegate_Success_Test(); + var delegationTransactionList = new RepeatedField + { + new DelegateTransaction + { + ContractAddress = BasicContractZeroAddress + }, + new DelegateTransaction + { + MethodName = "jsh&&&" + }, + new DelegateTransaction + { + + } + }; + var executionResult = await TokenContractStub.RemoveTransactionFeeDelegatorInfos.SendWithExceptionAsync(new RemoveTransactionFeeDelegatorInfosInput + { + DelegatorAddress = User1Address, + DelegateTransactionList = { delegationTransactionList } + }); + executionResult.TransactionResult.Error.ShouldContain("Invalid contract address and method name."); + } } \ No newline at end of file diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenEconomicTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenEconomicTests.cs index d35af8163a..89ab1cce32 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenEconomicTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenEconomicTests.cs @@ -18,7 +18,8 @@ await TokenContractStub.Create.SendAsync(new CreateInput TokenName = "elf token", TotalSupply = totalSupply, Issuer = DefaultAddress, - LockWhiteList = { TreasuryContractAddress } + LockWhiteList = { TreasuryContractAddress }, + Owner = DefaultAddress }); await TokenContractStub.Issue.SendAsync(new IssueInput { @@ -51,7 +52,8 @@ await TreasuryContractStub.InitialMiningRewardProfitItem.SendAsync( { ParliamentContractAddress, TreasuryContractAddress - } + }, + Owner = DefaultAddress })).TransactionResult; } diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenFeeTest.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenFeeTest.cs index 05cdb0efab..c4ae34ae8f 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenFeeTest.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenFeeTest.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Threading.Tasks; +using AElf.CSharp.Core; using AElf.Types; using Google.Protobuf; using Google.Protobuf.WellKnownTypes; @@ -13,7 +14,7 @@ public partial class MultiTokenContractTests [Fact(DisplayName = "[MultiToken] advance token not exist in resource token")] public async Task AdvancedResourceToken_Test() { - await CreateNativeTokenAsync(); + // await CreateNativeTokenAsync(); long advanceAmount = 1000; { var tokenNotResrouce = "NORESOURCE"; @@ -51,7 +52,6 @@ public async Task AdvancedResourceToken_Test() [Fact(DisplayName = "[MultiToken] take more token than that of the contract address's balance")] public async Task TakeResourceTokenBack_Test() { - await CreateNativeTokenAsync(); var trafficToken = "TRAFFIC"; var advanceAmount = 1000; await CreateAndIssueCustomizeTokenAsync(DefaultAddress, trafficToken, 10000, 10000); @@ -134,7 +134,13 @@ public async Task UpdateCoefficientForSender_Without_Authorization_Test() initializeControllerRet.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); var updateRet = await TokenContractStub.UpdateCoefficientsForSender.SendWithExceptionAsync( - new UpdateCoefficientsInput()); + new UpdateCoefficientsInput + { + Coefficients = new CalculateFeeCoefficients + { + FeeTokenType = 0 + } + }); updateRet.TransactionResult.Error.ShouldContain("no permission"); } @@ -166,11 +172,11 @@ public async Task InitializeAuthorizedController_Test() } [Fact(DisplayName = "[MultiToken] illegal controller try to set free allowances")] - public async Task ConfigMethodFeeFreeAllowances_Without_Authorization_Test() + public async Task ConfigTransactionFeeFreeAllowances_Without_Authorization_Test() { - var configMethodFeeFreeAllowancesRet = - await TokenContractStub.ConfigMethodFeeFreeAllowances.SendWithExceptionAsync(new MethodFeeFreeAllowancesConfig()); - configMethodFeeFreeAllowancesRet.TransactionResult.Error.ShouldContain("Unauthorized behavior."); + var configTransactionFeeFreeAllowancesRet = + await TokenContractStub.ConfigTransactionFeeFreeAllowances.SendWithExceptionAsync(new ConfigTransactionFeeFreeAllowancesInput()); + configTransactionFeeFreeAllowancesRet.TransactionResult.Error.ShouldContain("Unauthorized behavior."); } [Fact] @@ -222,7 +228,7 @@ private async Task SubmitAndApproveProposalOfDefaultParliament(Address contractA { var defaultParliamentAddress = await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); - var proposalId = await CreateProposalAsync(TokenContractAddress, + var proposalId = await CreateProposalAsync(contractAddress, defaultParliamentAddress, methodName, message); await ApproveWithMinersAsync(proposalId); var releaseResult = await ParliamentContractStub.Release.SendAsync(proposalId); @@ -230,6 +236,19 @@ private async Task SubmitAndApproveProposalOfDefaultParliament(Address contractA releaseResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); } + private async Task> SubmitAndApproveProposalOfDefaultParliamentWithException( + Address contractAddress, + string methodName, + IMessage message) + { + var defaultParliamentAddress = + await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); + var proposalId = await CreateProposalAsync(contractAddress, + defaultParliamentAddress, methodName, message); + await ApproveWithMinersAsync(proposalId); + return await ParliamentContractStub.Release.SendWithExceptionAsync(proposalId); + } + private Transaction GenerateTokenTransaction(Address from, string method, IMessage input) { return new Transaction diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenManagementTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenManagementTests.cs index b132b14184..ae7e336a98 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenManagementTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenManagementTests.cs @@ -50,7 +50,8 @@ public MultiTokenContractTests() IsBurnable = true, Issuer = Accounts[0].Address, Supply = 0, - IssueChainId = _chainId + IssueChainId = _chainId, + Owner = Accounts[0].Address }; private TokenInfo PrimaryTokenInfo => new() @@ -78,7 +79,8 @@ public MultiTokenContractTests() Issuer = Accounts[0].Address, Supply = 0, IssueChainId = _chainId, - ExternalInfo = new ExternalInfo() + ExternalInfo = new ExternalInfo(), + Owner = Accounts[0].Address }; /// @@ -111,7 +113,7 @@ public MultiTokenContractTests() private async Task CreateNativeTokenAsync() { - await TokenContractStub.Create.SendAsync(new CreateInput + await CreateMutiTokenAsync(TokenContractStub, new CreateInput { Symbol = NativeTokenInfo.Symbol, TokenName = NativeTokenInfo.TokenName, @@ -130,21 +132,12 @@ await TokenContractStub.Create.SendAsync(new CreateInput private async Task CreatePrimaryTokenAsync() { - await TokenContractStub.Create.SendAsync(new CreateInput - { - Symbol = NativeTokenInfo.Symbol, - TokenName = NativeTokenInfo.TokenName, - TotalSupply = NativeTokenInfo.TotalSupply, - Decimals = NativeTokenInfo.Decimals, - Issuer = NativeTokenInfo.Issuer, - IsBurnable = NativeTokenInfo.IsBurnable - }); - - await TokenContractStub.Create.SendAsync(new CreateInput + await CreateMutiTokenAsync(TokenContractStub, new CreateInput { Decimals = PrimaryTokenInfo.Decimals, IsBurnable = PrimaryTokenInfo.IsBurnable, Issuer = PrimaryTokenInfo.Issuer, + Owner = PrimaryTokenInfo.Issuer, TotalSupply = PrimaryTokenInfo.TotalSupply, Symbol = PrimaryTokenInfo.Symbol, TokenName = PrimaryTokenInfo.TokenName, @@ -169,13 +162,14 @@ private async Task CreateNormalTokenAsync() tokenInfo.ShouldBe(new TokenInfo()); } - await TokenContractStub.Create.SendAsync(new CreateInput + await CreateMutiTokenAsync(TokenContractStub, new CreateInput { Symbol = AliceCoinTokenInfo.Symbol, TokenName = AliceCoinTokenInfo.TokenName, TotalSupply = AliceCoinTokenInfo.TotalSupply, Decimals = AliceCoinTokenInfo.Decimals, Issuer = AliceCoinTokenInfo.Issuer, + Owner = AliceCoinTokenInfo.Issuer, IsBurnable = AliceCoinTokenInfo.IsBurnable, LockWhiteList = { @@ -236,13 +230,14 @@ public async Task MultiTokenContract_Create_NotSame_Test() { await CreateAndIssueMultiTokensAsync(); - await TokenContractStub.Create.SendAsync(new CreateInput + await CreateMutiTokenAsync(TokenContractStub, new CreateInput { Symbol = BobCoinTokenInfo.Symbol, TokenName = BobCoinTokenInfo.TokenName, TotalSupply = BobCoinTokenInfo.TotalSupply, Decimals = BobCoinTokenInfo.Decimals, Issuer = BobCoinTokenInfo.Issuer, + Owner = BobCoinTokenInfo.Issuer, IsBurnable = BobCoinTokenInfo.IsBurnable }); @@ -259,13 +254,14 @@ await TokenContractStub.Create.SendAsync(new CreateInput [Fact(DisplayName = "[MultiToken] Create Token use custom address")] public async Task MultiTokenContract_Create_UseCustomAddress_Test() { - var transactionResult = (await TokenContractStub.Create.SendWithExceptionAsync(new CreateInput + var transactionResult = (await CreateMutiTokenWithExceptionAsync(TokenContractStub, new CreateInput { - Symbol = NativeTokenInfo.Symbol, + Symbol = BobCoinTokenInfo.Symbol, Decimals = 2, IsBurnable = true, Issuer = DefaultAddress, - TokenName = NativeTokenInfo.TokenName, + Owner = DefaultAddress, + TokenName = BobCoinTokenInfo.TokenName, TotalSupply = AliceCoinTotalAmount, LockWhiteList = { @@ -278,7 +274,6 @@ public async Task MultiTokenContract_Create_UseCustomAddress_Test() private async Task CreateAndIssueMultiTokensAsync() { - await CreateNativeTokenAsync(); await CreateNormalTokenAsync(); //issue AliceToken amount of 1000_00L to DefaultAddress { @@ -299,25 +294,6 @@ private async Task CreateAndIssueMultiTokensAsync() balance.ShouldBe(AliceCoinTotalAmount); } - //issue ELF amount of 1000_00L to DefaultAddress - { - var result = await TokenContractStub.Issue.SendAsync(new IssueInput - { - Symbol = "ELF", - Amount = AliceCoinTotalAmount, - To = DefaultAddress, - Memo = "first issue token." - }); - result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); - - var balance = (await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput - { - Owner = DefaultAddress, - Symbol = "ELF" - })).Balance; - balance.ShouldBe(AliceCoinTotalAmount); - } - //issue AliceToken amount of 1000L to User1Address { var result = await TokenContractStub.Issue.SendAsync(new IssueInput @@ -375,7 +351,6 @@ public async Task MultiTokenContract_Issue_OutOfAmount_Test() [Fact] public async Task IssueToken_With_Invalid_Input() { - await CreateNativeTokenAsync(); await CreateNormalTokenAsync(); // to is null { @@ -420,11 +395,12 @@ public async Task IssueToken_With_Invalid_Input() //invalid chain id { var chainTokenSymbol = "CHAIN"; - await TokenContractStub.Create.SendAsync(new CreateInput + await CreateMutiTokenAsync(TokenContractStub, new CreateInput { Symbol = chainTokenSymbol, TokenName = "chain token", Issuer = DefaultAddress, + Owner = DefaultAddress, IssueChainId = 10, TotalSupply = 1000_000, Decimals = 8 @@ -443,13 +419,13 @@ await TokenContractStub.Create.SendAsync(new CreateInput [Fact] public async Task IssueToken_Test() { - await CreateNativeTokenAsync(); var newTokenSymbol = "AIN"; - await TokenContractStub.Create.SendAsync(new CreateInput + await CreateMutiTokenAsync(TokenContractStub, new CreateInput { Symbol = newTokenSymbol, TokenName = "ain token", Issuer = DefaultAddress, + Owner = DefaultAddress, TotalSupply = 1000_000, Decimals = 8 }); @@ -512,21 +488,20 @@ public async Task TokenCreate_Test() OtherBasicFunctionContractAddress, TokenConverterContractAddress, TreasuryContractAddress - } + }, + Owner = AliceCoinTokenInfo.Owner }; - var createTokenRet = await TokenContractStub.Create.SendWithExceptionAsync(createTokenInfo); - createTokenRet.TransactionResult.Error.ShouldContain("Invalid native token input"); var createTokenInfoWithInvalidTokenName = new CreateInput(); createTokenInfoWithInvalidTokenName.MergeFrom(createTokenInfo); createTokenInfoWithInvalidTokenName.Symbol = "ITISAVERYLONGSYMBOLNAME"; - createTokenRet = await TokenContractStub.Create.SendWithExceptionAsync(createTokenInfoWithInvalidTokenName); + var createTokenRet = + await CreateSeedNftWithExceptionAsync(TokenContractStub, createTokenInfoWithInvalidTokenName); createTokenRet.TransactionResult.Error.ShouldContain("Invalid token symbol length"); var createTokenInfoWithInvalidDecimal = new CreateInput(); createTokenInfoWithInvalidDecimal.MergeFrom(createTokenInfo); createTokenInfoWithInvalidDecimal.Decimals = 100; - createTokenRet = await TokenContractStub.Create.SendWithExceptionAsync(createTokenInfoWithInvalidDecimal); + createTokenRet = await CreateMutiTokenWithExceptionAsync(TokenContractStub, createTokenInfoWithInvalidDecimal); createTokenRet.TransactionResult.Error.ShouldContain("Invalid input"); - await CreateNativeTokenAsync(); await TokenContractStub.Create.SendAsync(createTokenInfo); var tokenInfo = await TokenContractStub.GetTokenInfo.CallAsync(new GetTokenInfoInput { @@ -544,7 +519,6 @@ public async Task SetPrimaryToken_Test() Symbol = "NOTEXISTED" }); setPrimaryTokenRet.TransactionResult.Error.ShouldContain("Invalid input"); - await CreateNativeTokenAsync(); await TokenContractStub.SetPrimaryTokenSymbol.SendAsync(new SetPrimaryTokenSymbolInput { Symbol = NativeTokenInfo.Symbol @@ -562,7 +536,6 @@ await TokenContractStub.SetPrimaryTokenSymbol.SendAsync(new SetPrimaryTokenSymbo [Fact] public async Task GetNativeToken_Test() { - await CreateNativeTokenAsync(); var tokenInfo = await TokenContractStub.GetNativeTokenInfo.CallAsync(new Empty()); tokenInfo.Symbol.ShouldBe(NativeTokenInfo.Symbol); } diff --git a/test/AElf.Contracts.MultiToken.Tests/MultiTokenContractTestBase.cs b/test/AElf.Contracts.MultiToken.Tests/MultiTokenContractTestBase.cs index 819e5f825d..232a5f5b8b 100644 --- a/test/AElf.Contracts.MultiToken.Tests/MultiTokenContractTestBase.cs +++ b/test/AElf.Contracts.MultiToken.Tests/MultiTokenContractTestBase.cs @@ -1,13 +1,22 @@ using System.Collections.Generic; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using AElf.Contracts.Parliament; using AElf.Contracts.TestContract.BasicFunction; using AElf.Contracts.TokenConverter; using AElf.Contracts.Treasury; using AElf.ContractTestBase.ContractTestKit; using AElf.Cryptography.ECDSA; +using AElf.CSharp.Core; +using AElf.CSharp.Core.Extension; +using AElf.Kernel; using AElf.Standards.ACS2; +using AElf.Standards.ACS3; using AElf.Types; +using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; +using Volo.Abp.Threading; namespace AElf.Contracts.MultiToken; @@ -15,8 +24,6 @@ public class MultiTokenContractTestBase : ContractTestBase(TokenContractAddress, DefaultKeyPair); - TokenContractStubUser = + + TokenContractStubUser = GetTester(TokenContractAddress, User1KeyPair); + TokenContractStubDelegate = + GetTester(TokenContractAddress, User2KeyPair); + TokenContractStubDelegate2 = + GetTester(TokenContractAddress, User3KeyPair); + TokenContractStubDelegate3 = + GetTester(TokenContractAddress, User4KeyPair); Acs2BaseStub = GetTester(TokenContractAddress, DefaultKeyPair); TreasuryContractStub = GetTester( @@ -54,6 +73,20 @@ public MultiTokenContractTestBase() ParliamentContractStub = GetTester( ParliamentContractAddress, DefaultKeyPair); + AsyncHelper.RunSync(() => SubmitAndApproveProposalOfDefaultParliament(TokenContractAddress, + nameof(TokenContractStub.Create), new CreateInput() + { + Symbol = "ELF", + Decimals = 8, + IsBurnable = true, + TokenName = "ELF2", + TotalSupply = 100_000_000_000_000_000L, + Issuer = DefaultAddress, + ExternalInfo = new ExternalInfo(), + Owner = DefaultAddress + })); + + AsyncHelper.RunSync(() => CreateSeedNftCollection(TokenContractStub)); } protected long AliceCoinTotalAmount => 1_000_000_000_0000000L; @@ -62,7 +95,15 @@ public MultiTokenContractTestBase() protected Address DefaultAddress => Accounts[0].Address; protected ECKeyPair User1KeyPair => Accounts[10].KeyPair; protected Address User1Address => Accounts[10].Address; + protected ECKeyPair User2KeyPair => Accounts[11].KeyPair; protected Address User2Address => Accounts[11].Address; + protected ECKeyPair User3KeyPair => Accounts[12].KeyPair; + protected Address User3Address => Accounts[12].Address; + protected ECKeyPair User4KeyPair => Accounts[13].KeyPair; + protected Address User4Address => Accounts[13].Address; + + protected int SeedNum = 0; + protected string SeedNFTSymbolPre = "SEED-"; protected List InitialCoreDataCenterKeyPairs => Accounts.Take(InitialCoreDataCenterCount).Select(a => a.KeyPair).ToList(); @@ -74,6 +115,7 @@ public MultiTokenContractTestBase() protected Hash OtherBasicFunctionContractName => HashHelper.ComputeFrom("AElf.TestContractNames.OtherBasicFunction"); + protected Address OtherBasicFunctionContractAddress { get; set; } internal BasicFunctionContractContainer.BasicFunctionContractStub OtherBasicFunctionContractStub { get; set; } @@ -83,4 +125,119 @@ internal ParliamentContractImplContainer.ParliamentContractImplStub GetParliamen return GetTester(ParliamentContractAddress, keyPair); } + + private async Task SubmitAndApproveProposalOfDefaultParliament(Address contractAddress, string methodName, + IMessage message) + { + var defaultParliamentAddress = + await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); + var proposalId = await CreateProposalAsync(TokenContractAddress, + defaultParliamentAddress, methodName, message); + await ApproveWithMinersAsync(proposalId); + var releaseResult = await ParliamentContractStub.Release.SendAsync(proposalId); + } + + private async Task CreateProposalAsync(Address contractAddress, Address organizationAddress, + string methodName, IMessage input) + { + var proposal = new CreateProposalInput + { + OrganizationAddress = organizationAddress, + ContractMethodName = methodName, + ExpiredTime = TimestampHelper.GetUtcNow().AddHours(1), + Params = input.ToByteString(), + ToAddress = contractAddress + }; + + var createResult = await ParliamentContractStub.CreateProposal.SendAsync(proposal); + var proposalId = createResult.Output; + + return proposalId; + } + + private async Task ApproveWithMinersAsync(Hash proposalId) + { + foreach (var bp in InitialCoreDataCenterKeyPairs) + { + var tester = GetParliamentContractTester(bp); + var approveResult = await tester.Approve.SendAsync(proposalId); + } + } + + internal async Task CreateSeedNftCollection(TokenContractImplContainer.TokenContractImplStub stub) + { + var input = new CreateInput + { + Symbol = SeedNFTSymbolPre + SeedNum, + Decimals = 0, + IsBurnable = true, + TokenName = "seed Collection", + TotalSupply = 1, + Issuer = DefaultAddress, + Owner = DefaultAddress, + ExternalInfo = new ExternalInfo() + }; + await stub.Create.SendAsync(input); + } + + + internal async Task CreateSeedNftAsync(TokenContractImplContainer.TokenContractImplStub stub, + CreateInput createInput) + { + var input = BuildSeedCreateInput(createInput); + await stub.Create.SendAsync(input); + await stub.Issue.SendAsync(new IssueInput + { + Symbol = input.Symbol, + Amount = 1, + Memo = "ddd", + To = DefaultAddress + }); + return input; + } + + + + internal async Task> CreateSeedNftWithExceptionAsync( + TokenContractImplContainer.TokenContractImplStub stub, + CreateInput createInput) + { + var input = BuildSeedCreateInput(createInput); + return await stub.Create.SendWithExceptionAsync(input); + } + + internal CreateInput BuildSeedCreateInput(CreateInput createInput) + { + Interlocked.Increment(ref SeedNum); + var input = new CreateInput + { + Symbol = SeedNFTSymbolPre + SeedNum, + Decimals = 0, + IsBurnable = true, + TokenName = "seed token" + SeedNum, + TotalSupply = 1, + Issuer = DefaultAddress, + Owner = DefaultAddress, + ExternalInfo = new ExternalInfo(), + LockWhiteList = { TokenContractAddress } + }; + input.ExternalInfo.Value["__seed_owned_symbol"] = createInput.Symbol; + input.ExternalInfo.Value["__seed_exp_time"] = TimestampHelper.GetUtcNow().AddDays(1).Seconds.ToString(); + return input; + } + + internal async Task> CreateMutiTokenAsync( + TokenContractImplContainer.TokenContractImplStub stub, + CreateInput createInput) + { + await CreateSeedNftAsync(stub, createInput); + return await stub.Create.SendAsync(createInput); + } + + internal async Task> CreateMutiTokenWithExceptionAsync( + TokenContractImplContainer.TokenContractImplStub stub, CreateInput createInput) + { + await CreateSeedNftAsync(stub, createInput); + return await stub.Create.SendWithExceptionAsync(createInput); + } } \ No newline at end of file diff --git a/test/AElf.Contracts.MultiToken.Tests/MultiTokenContractWithCustomSystemTransactionTest.cs b/test/AElf.Contracts.MultiToken.Tests/MultiTokenContractWithCustomSystemTransactionTest.cs index 12a2888fba..f9c91f87b1 100644 --- a/test/AElf.Contracts.MultiToken.Tests/MultiTokenContractWithCustomSystemTransactionTest.cs +++ b/test/AElf.Contracts.MultiToken.Tests/MultiTokenContractWithCustomSystemTransactionTest.cs @@ -20,15 +20,6 @@ public MultiTokenContractWithCustomSystemTransactionTest() private async Task InitializeAsync() { - await TokenContractStub.Create.SendAsync(new CreateInput - { - Symbol = DefaultSymbol, - Decimals = 2, - IsBurnable = true, - TokenName = "elf token", - TotalSupply = _totalSupply, - Issuer = DefaultAddress - }); await TokenContractStub.SetPrimaryTokenSymbol.SendAsync(new SetPrimaryTokenSymbolInput { Symbol = DefaultSymbol diff --git a/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/MultiTokenContractCrossChainTest.cs b/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/MultiTokenContractCrossChainTest.cs index c40cc6ed98..4c76a04b09 100644 --- a/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/MultiTokenContractCrossChainTest.cs +++ b/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/MultiTokenContractCrossChainTest.cs @@ -195,8 +195,8 @@ public async Task MainChain_CrossChainCreateToken_Test() // Side chain create token var createTransaction = - CreateTransactionForTokenCreation(SideChainTokenContractStub, SideChainTestKit.DefaultAccount.Address, - SymbolForTesting); + await CreateTransactionForTokenCreation(SideChainTokenContractStub, SideChainTestKit.DefaultAccount.Address, + SymbolForTesting, SideTokenContractAddress); var executedSet = await SideChainTestKit.MineAsync(new List { createTransaction }); var createResult = executedSet.TransactionResultMap[createTransaction.GetHash()]; Assert.True(createResult.Status == TransactionResultStatus.Mined, createResult.Error); @@ -236,8 +236,8 @@ public async Task SideChain_CrossChainCreateToken_Test() // Main chain create token await BootMinerChangeRoundAsync(AEDPoSContractStub, true); - var createTransaction = CreateTransactionForTokenCreation(TokenContractStub, - DefaultAccount.Address, SymbolForTesting); + var createTransaction = await CreateTransactionForTokenCreation(TokenContractStub, + DefaultAccount.Address, SymbolForTesting, TokenContractAddress); var blockExecutedSet = await MineAsync(new List { createTransaction }); var createResult = blockExecutedSet.TransactionResultMap[createTransaction.GetHash()]; Assert.True(createResult.Status == TransactionResultStatus.Mined, createResult.Error); @@ -328,14 +328,13 @@ public async Task SideChain_CrossChainCreateToken_WithAlreadyCreated_Test() await RegisterSideChainContractAddressOnMainChainAsync(); await BootMinerChangeRoundAsync(AEDPoSContractStub, true); - var createTransaction = CreateTransactionForTokenCreation(TokenContractStub, - DefaultAccount.Address, SymbolForTesting); + var createTransaction = await CreateTransactionForTokenCreation(TokenContractStub, + DefaultAccount.Address, SymbolForTesting, TokenContractAddress); var blockExecutedSet = await MineAsync(new List { createTransaction }); var createResult = blockExecutedSet.TransactionResultMap[createTransaction.GetHash()]; Assert.True(createResult.Status == TransactionResultStatus.Mined, createResult.Error); - - var sideCreateTransaction = CreateTransactionForTokenCreation(SideChainTokenContractStub, - SideChainTestKit.DefaultAccount.Address, SymbolForTesting); + var sideCreateTransaction = await CreateTransactionForTokenCreation(SideChainTokenContractStub, + SideChainTestKit.DefaultAccount.Address, SymbolForTesting, SideTokenContractAddress); blockExecutedSet = await SideChainTestKit.MineAsync(new List { sideCreateTransaction }); var sideCreateResult = blockExecutedSet.TransactionResultMap[sideCreateTransaction.GetHash()]; Assert.True(sideCreateResult.Status == TransactionResultStatus.Mined, sideCreateResult.Error); @@ -437,8 +436,8 @@ public async Task MainChain_CrossChainTransfer_NativeToken_Test() [Fact] public async Task MainChain_CrossChainTransfer_Without_Burnable_Token_Test() { - var createTransaction = CreateTransactionForTokenCreation(TokenContractStub, - DefaultAccount.Address, SymbolForTesting, false); + var createTransaction = await CreateTransactionForTokenCreation(TokenContractStub, + DefaultAccount.Address, SymbolForTesting, TokenContractAddress, false); var blockExecutedSet = await MineAsync(new List { createTransaction }); var createResult = blockExecutedSet.TransactionResultMap[createTransaction.GetHash()]; Assert.True(createResult.Status == TransactionResultStatus.Mined, createResult.Error); @@ -471,8 +470,8 @@ public async Task MainChain_CrossChainTransfer_Test() // Main chain create token await BootMinerChangeRoundAsync(AEDPoSContractStub, true); - var createTransaction = CreateTransactionForTokenCreation(TokenContractStub, - DefaultAccount.Address, SymbolForTesting); + var createTransaction = await CreateTransactionForTokenCreation(TokenContractStub, + DefaultAccount.Address, SymbolForTesting, TokenContractAddress); var executedSet = await MineAsync(new List { createTransaction }); var createResult = executedSet.TransactionResultMap[createTransaction.GetHash()]; Assert.True(createResult.Status == TransactionResultStatus.Mined, createResult.Error); @@ -488,6 +487,7 @@ public async Task MainChain_CrossChainTransfer_Test() Symbol = createdTokenInfo.Symbol, Decimals = createdTokenInfo.Decimals, Issuer = createdTokenInfo.Issuer, + Owner = createdTokenInfo.Issuer, IsBurnable = createdTokenInfo.IsBurnable, TotalSupply = createdTokenInfo.TotalSupply, IssueChainId = createdTokenInfo.IssueChainId @@ -648,10 +648,11 @@ public async Task SideChain_CrossChainReceived_NativeToken_Test() TransferTransactionBytes = crossChainTransferTransaction.ToByteString(), MerklePath = transferMerKlePath }; - var receiveResult = await SideChainTokenContractStub.CrossChainReceiveToken.SendAsync(crossChainReceiveTokenInput); - + var receiveResult = + await SideChainTokenContractStub.CrossChainReceiveToken.SendAsync(crossChainReceiveTokenInput); + var logEvent = receiveResult.TransactionResult.Logs.First(l => l.Name == nameof(CrossChainReceived)); - var receivedEvent =new CrossChainReceived(); + var receivedEvent = new CrossChainReceived(); receivedEvent.MergeFrom(logEvent.NonIndexed); receivedEvent.From.ShouldBe(SideChainTestKit.DefaultAccount.Address); receivedEvent.To.ShouldBe(SideChainTestKit.DefaultAccount.Address); @@ -723,8 +724,8 @@ public async Task SideChain_CrossChainReceived_InvalidToken_Test() await RegisterMainChainTokenContractAddressOnSideChainAsync(sideChainId); var transferAmount = 1000; - var createTransaction = CreateTransactionForTokenCreation(TokenContractStub, - DefaultAccount.Address, SymbolForTesting); + var createTransaction = await CreateTransactionForTokenCreation(TokenContractStub, + DefaultAccount.Address, SymbolForTesting, TokenContractAddress); var executedSet = await MineAsync(new List { createTransaction }); var createResult = executedSet.TransactionResultMap[createTransaction.GetHash()]; createResult.Status.ShouldBe(TransactionResultStatus.Mined, createResult.Error); @@ -809,8 +810,8 @@ public async Task SideChain_CrossChainReceived_DifferentReceiver_Test() public async Task CrossChainCreateToken_WithoutRegister_Test() { await GenerateSideChainAsync(false); - var createTransaction = CreateTransactionForTokenCreation(TokenContractStub, - DefaultAccount.Address, SymbolForTesting); + var createTransaction = await CreateTransactionForTokenCreation(TokenContractStub, + DefaultAccount.Address, SymbolForTesting, TokenContractAddress); var blockExecutedSet = await MineAsync(new List { createTransaction }); var createResult = blockExecutedSet.TransactionResultMap[createTransaction.GetHash()]; Assert.True(createResult.Status == TransactionResultStatus.Mined, createResult.Error); @@ -871,22 +872,27 @@ private Transaction ValidateTransaction(Address tokenContractAddress, Hash name, } - private Transaction CreateTransactionForTokenCreation( + private async Task CreateTransactionForTokenCreation( TokenContractImplContainer.TokenContractImplStub tokenContractImplStub, - Address issuer, string symbol, bool isBurnable = true) + Address issuer, string symbol, Address lockWhiteAddress, bool isBurnable = true) { + await CreateSeedNftCollection(tokenContractImplStub, issuer); var tokenInfo = GetTokenInfo(symbol, issuer, isBurnable); - return tokenContractImplStub.Create.GetTransaction(new CreateInput + var input = new CreateInput { Symbol = tokenInfo.Symbol, Decimals = tokenInfo.Decimals, Issuer = tokenInfo.Issuer, + Owner = tokenInfo.Issuer, IsBurnable = tokenInfo.IsBurnable, TokenName = tokenInfo.TokenName, TotalSupply = tokenInfo.TotalSupply - }); + }; + await CreateSeedNftAsync(tokenContractImplStub, input, lockWhiteAddress); + return tokenContractImplStub.Create.GetTransaction(input); } + private Transaction CreateTokenInfoValidationTransaction(TokenInfo createdTokenInfo, TokenContractImplContainer.TokenContractImplStub tokenContractImplStub) { @@ -896,6 +902,7 @@ private Transaction CreateTokenInfoValidationTransaction(TokenInfo createdTokenI Symbol = createdTokenInfo.Symbol, Decimals = createdTokenInfo.Decimals, Issuer = createdTokenInfo.Issuer, + Owner = createdTokenInfo.Issuer, IsBurnable = createdTokenInfo.IsBurnable, TotalSupply = createdTokenInfo.TotalSupply, IssueChainId = createdTokenInfo.IssueChainId diff --git a/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/MultiTokenContractCrossChainTestBase.cs b/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/MultiTokenContractCrossChainTestBase.cs index ca4e882201..a84a9d9d98 100644 --- a/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/MultiTokenContractCrossChainTestBase.cs +++ b/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/MultiTokenContractCrossChainTestBase.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using AElf.Contracts.Consensus.AEDPoS; using AElf.Contracts.CrossChain; @@ -8,9 +9,12 @@ using AElf.Contracts.Referendum; using AElf.ContractTestBase.ContractTestKit; using AElf.CrossChain; +using AElf.Cryptography; +using AElf.Cryptography.ECDSA; using AElf.CSharp.Core; using AElf.CSharp.Core.Extension; using AElf.Kernel; +using AElf.Kernel.Blockchain.Application; using AElf.Kernel.Consensus; using AElf.Kernel.Proposal; using AElf.Kernel.SmartContract; @@ -61,6 +65,8 @@ public class MultiTokenContractCrossChainTestBase : ContractTestBase SideChainTestKit; internal TokenContractImplContainer.TokenContractImplStub SideChainTokenContractStub; + protected readonly IBlockchainService BlockchainService; + protected Address SideConsensusAddress; @@ -73,6 +79,9 @@ public class MultiTokenContractCrossChainTestBase : ContractTestBase>() .Value.ContextVariables["SymbolListToPayRental"].Split(",").ToList(); + + BlockchainService = Application.ServiceProvider.GetRequiredService(); } protected Timestamp BlockchainStartTimestamp => TimestampHelper.GetUtcNow(); @@ -318,10 +329,11 @@ internal async Task BootMinerChangeRoundAsync(AEDPoSContractContainer.AEDPoSCont { if (isMainChain) { + var randomNumber = await GenerateRandomProofAsync(aedPoSContractStub, DefaultAccount.KeyPair); var currentRound = await aedPoSContractStub.GetCurrentRoundInformation.CallAsync(new Empty()); var expectedStartTime = TimestampHelper.GetUtcNow(); currentRound.GenerateNextRoundInformation(expectedStartTime, BlockchainStartTimestamp, - out var nextRound); + ByteString.CopyFrom(randomNumber), out var nextRound); nextRound.RealTimeMinersInformation[DefaultAccount.KeyPair.PublicKey.ToHex()] .ExpectedMiningTime = expectedStartTime; await aedPoSContractStub.NextRound.SendAsync(nextRound); @@ -329,13 +341,14 @@ internal async Task BootMinerChangeRoundAsync(AEDPoSContractContainer.AEDPoSCont if (!isMainChain) { + var randomNumber = CryptoHelper.ECVrfProve(DefaultAccount.KeyPair, Hash.Empty.ToByteArray()); var currentRound = await aedPoSContractStub.GetCurrentRoundInformation.CallAsync(new Empty()); var expectedStartTime = BlockchainStartTimestamp.ToDateTime() .AddMilliseconds( ((long)currentRound.TotalMilliseconds(4000)).Mul( nextRoundNumber.Sub(1))); currentRound.GenerateNextRoundInformation(expectedStartTime.ToTimestamp(), BlockchainStartTimestamp, - out var nextRound); + ByteString.CopyFrom(randomNumber), out var nextRound); if (currentRound.RoundNumber >= 3) { @@ -353,6 +366,18 @@ internal async Task BootMinerChangeRoundAsync(AEDPoSContractContainer.AEDPoSCont } } + private async Task GenerateRandomProofAsync(AEDPoSContractContainer.AEDPoSContractStub aedPoSContractStub, + ECKeyPair keyPair) + { + var blockHeight = (await BlockchainService.GetChainAsync()).BestChainHeight; + var previousRandomHash = + blockHeight <= 1 + ? Hash.Empty + : await aedPoSContractStub.GetRandomHash.CallAsync(new Int64Value + { Value = blockHeight }); + return CryptoHelper.ECVrfProve(keyPair, previousRandomHash.ToByteArray()); + } + private async Task ApproveBalanceAsync(long amount) { await TokenContractStub.Approve.SendAsync(new ApproveInput @@ -372,4 +397,57 @@ await CrossChainContractStub.Initialize.SendAsync(new InitializeInput CreationHeightOnParentChain = parentChainHeightOfCreation }); } + + internal async Task CreateSeedNftCollection(TokenContractImplContainer.TokenContractImplStub stub, Address address) + { + var input = new CreateInput + { + Symbol = SeedNFTSymbolPre + 0, + Decimals = 0, + IsBurnable = true, + TokenName = "seed Collection", + TotalSupply = 1, + Issuer = address, + Owner = address, + ExternalInfo = new ExternalInfo() + }; + var re= await stub.Create.SendAsync(input); + } + + + internal async Task CreateSeedNftAsync(TokenContractImplContainer.TokenContractImplStub stub, + CreateInput createInput,Address lockWhiteAddress) + { + var input = BuildSeedCreateInput(createInput,lockWhiteAddress); + await stub.Create.SendAsync(input); + await stub.Issue.SendAsync(new IssueInput + { + Symbol = input.Symbol, + Amount = 1, + Memo = "ddd", + To = input.Issuer + }); + return input; + } + + internal CreateInput BuildSeedCreateInput(CreateInput createInput,Address lockWhiteAddress) + { + Interlocked.Increment(ref SeedNum); + var input = new CreateInput + { + Symbol = SeedNFTSymbolPre + SeedNum, + Decimals = 0, + IsBurnable = true, + TokenName = "seed token" + SeedNum, + TotalSupply = 1, + Issuer = createInput.Issuer, + Owner = createInput.Issuer, + ExternalInfo = new ExternalInfo(), + LockWhiteList = { lockWhiteAddress } + }; + input.ExternalInfo.Value["__seed_owned_symbol"] = createInput.Symbol; + input.ExternalInfo.Value["__seed_exp_time"] = TimestampHelper.GetUtcNow().AddDays(1).Seconds.ToString(); + return input; + } + } \ No newline at end of file diff --git a/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/MultiTokenContractReferenceFeeTest.cs b/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/MultiTokenContractReferenceFeeTest.cs index 4db7135097..f32c400c35 100644 --- a/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/MultiTokenContractReferenceFeeTest.cs +++ b/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/MultiTokenContractReferenceFeeTest.cs @@ -496,14 +496,18 @@ public async Task SetSymbolsToPayTxSizeFee_With_Invalid_Weight_Test() var theDefaultController = await GetDefaultParliamentAddressAsync(); var primaryTokenSymbol = await GetThePrimaryTokenAsync(); var feeToken = "FEETOKEN"; - await TokenContractStub.Create.SendAsync(new CreateInput + await CreateSeedNftCollection(TokenContractStub, DefaultAccount.Address); + var input = new CreateInput { Symbol = feeToken, TokenName = "name", - Issuer = TokenContractAddress, + Issuer = DefaultAccount.Address, TotalSupply = 100_000, - IsBurnable = true - }); + IsBurnable = true, + Owner = DefaultAccount.Address + }; + await CreateSeedNftAsync(TokenContractStub, input, TokenContractAddress); + await TokenContractStub.Create.SendAsync(input); var newSymbolList = new SymbolListToPayTxSizeFee(); newSymbolList.SymbolsToPayTxSizeFee.Add(new SymbolToPayTxSizeFee { @@ -531,13 +535,17 @@ public async Task SetSymbolsToPayTxSizeFee_With_Repeat_Token_Test() var theDefaultController = await GetDefaultParliamentAddressAsync(); var primaryTokenSymbol = await GetThePrimaryTokenAsync(); var feeToken = "FEETOKEN"; - await TokenContractStub.Create.SendAsync(new CreateInput + await CreateSeedNftCollection(TokenContractStub, DefaultAccount.Address); + var input = new CreateInput { Symbol = feeToken, TokenName = "name", - Issuer = TokenContractAddress, - TotalSupply = 100_000 - }); + Issuer = DefaultAccount.Address, + TotalSupply = 100_000, + Owner = DefaultAccount.Address + }; + await CreateSeedNftAsync(TokenContractStub, input, TokenContractAddress); + await TokenContractStub.Create.SendAsync(input); var newSymbolList = new SymbolListToPayTxSizeFee { SymbolsToPayTxSizeFee = @@ -572,13 +580,17 @@ public async Task SetSymbolsToPayTxSizeFee_Without_PrimaryToken_Test() { var theDefaultController = await GetDefaultParliamentAddressAsync(); var feeToken = "FEETOKEN"; - await TokenContractStub.Create.SendAsync(new CreateInput + await CreateSeedNftCollection(TokenContractStub, DefaultAccount.Address); + var input = new CreateInput { Symbol = feeToken, TokenName = "name", - Issuer = TokenContractAddress, - TotalSupply = 100_000 - }); + Issuer = DefaultAccount.Address, + TotalSupply = 100_000, + Owner = DefaultAccount.Address + }; + await CreateSeedNftAsync(TokenContractStub, input, TokenContractAddress); + await TokenContractStub.Create.SendAsync(input); var newSymbolList = new SymbolListToPayTxSizeFee(); newSymbolList.SymbolsToPayTxSizeFee.Add(new SymbolToPayTxSizeFee { @@ -596,13 +608,17 @@ public async Task SetSymbolsToPayTxSizeFee_Without_Profitable_Token_Test() var theDefaultController = await GetDefaultParliamentAddressAsync(); var primaryTokenSymbol = await GetThePrimaryTokenAsync(); const string feeToken = "FEETOKEN"; - await TokenContractStub.Create.SendAsync(new CreateInput + await CreateSeedNftCollection(TokenContractStub, DefaultAccount.Address); + var input = new CreateInput { Symbol = feeToken, TokenName = "name", - Issuer = TokenContractAddress, - TotalSupply = 100_000 - }); + Issuer = DefaultAccount.Address, + TotalSupply = 100_000, + Owner = DefaultAccount.Address + }; + await CreateSeedNftAsync(TokenContractStub, input, TokenContractAddress); + await TokenContractStub.Create.SendAsync(input); var newSymbolList = new SymbolListToPayTxSizeFee { SymbolsToPayTxSizeFee = @@ -648,7 +664,8 @@ await TokenContractStub.Create.SendAsync(new CreateInput TokenName = "name", Issuer = TokenContractAddress, TotalSupply = newTokenTotalSupply, - IsBurnable = true + IsBurnable = true, + Owner = TokenContractAddress }); var invalidBaseTokenWeight = (int)long.MaxValue.Div(newTokenTotalSupply).Add(1); newSymbolList.SymbolsToPayTxSizeFee.Add(new SymbolToPayTxSizeFee @@ -695,14 +712,18 @@ public async Task SetSymbolsToPayTxSizeFee_Success_Test() var theDefaultController = await GetDefaultParliamentAddressAsync(); var primaryTokenSymbol = await GetThePrimaryTokenAsync(); const string feeToken = "FEETOKEN"; - await TokenContractStub.Create.SendAsync(new CreateInput + await CreateSeedNftCollection(TokenContractStub, DefaultAccount.Address); + var input = new CreateInput { Symbol = feeToken, TokenName = "name", - Issuer = TokenContractAddress, + Issuer = DefaultAccount.Address, TotalSupply = 100_000, - IsBurnable = true - }); + IsBurnable = true, + Owner = DefaultAccount.Address + }; + await CreateSeedNftAsync(TokenContractStub, input, TokenContractAddress); + await TokenContractStub.Create.SendAsync(input); var newSymbolList = new SymbolListToPayTxSizeFee { SymbolsToPayTxSizeFee = diff --git a/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/SideChainUnitTestTokenContractInitializationProvider.cs b/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/SideChainUnitTestTokenContractInitializationProvider.cs index e6f9791681..aeb8d3148a 100644 --- a/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/SideChainUnitTestTokenContractInitializationProvider.cs +++ b/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/SideChainUnitTestTokenContractInitializationProvider.cs @@ -127,6 +127,7 @@ private CreateInput GenerateTokenCreateInput(TokenInfo tokenInfo) Decimals = tokenInfo.Decimals, IssueChainId = tokenInfo.IssueChainId, Issuer = tokenInfo.Issuer, + Owner = tokenInfo.Issuer, IsBurnable = tokenInfo.IsBurnable, Symbol = tokenInfo.Symbol, TokenName = tokenInfo.TokenName, diff --git a/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/Types/Round_Generation.cs b/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/Types/Round_Generation.cs index 34830558d0..63ca824cd6 100644 --- a/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/Types/Round_Generation.cs +++ b/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/Types/Round_Generation.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using System.Linq; using AElf.CSharp.Core; +using AElf.Types; +using Google.Protobuf; using Google.Protobuf.WellKnownTypes; namespace AElf.Contracts.Consensus.AEDPoS; @@ -8,9 +10,9 @@ namespace AElf.Contracts.Consensus.AEDPoS; internal partial class Round { public bool GenerateNextRoundInformation(Timestamp currentBlockTimestamp, Timestamp blockchainStartTimestamp, - out Round nextRound) + ByteString randomNumber, out NextRoundInput nextRound) { - nextRound = new Round(); + nextRound = new NextRoundInput(); var minersMinedCurrentRound = GetMinedMiners(); var minersNotMinedCurrentRound = GetNotMinedMiners(); @@ -65,7 +67,8 @@ public bool GenerateNextRoundInformation(Timestamp currentBlockTimestamp, Timest nextRound.RealTimeMinersInformation.Values.First().IsExtraBlockProducer = true; else expectedExtraBlockProducer.IsExtraBlockProducer = true; - + nextRound.RandomNumber = randomNumber; + return true; } diff --git a/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/UnitTestTokenContractInitializationProvider.cs b/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/UnitTestTokenContractInitializationProvider.cs index b9df291357..cc84673ccf 100644 --- a/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/UnitTestTokenContractInitializationProvider.cs +++ b/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/UnitTestTokenContractInitializationProvider.cs @@ -38,6 +38,7 @@ public override List GetInitializeMethodList(b { Decimals = _economicOptions.Decimals, Issuer = address, + Owner = address, IsBurnable = _economicOptions.IsBurnable, Symbol = _economicOptions.Symbol, TokenName = _economicOptions.TokenName, diff --git a/test/AElf.Contracts.Parliament.Tests/ParliamentContractTest.cs b/test/AElf.Contracts.Parliament.Tests/ParliamentContractTest.cs index 143c27a168..5a82089232 100644 --- a/test/AElf.Contracts.Parliament.Tests/ParliamentContractTest.cs +++ b/test/AElf.Contracts.Parliament.Tests/ParliamentContractTest.cs @@ -32,32 +32,9 @@ public ParliamentContractTest() InitializeContracts(); } - [Fact] - public async Task Get_DefaultOrganizationAddress_Test() - { - var transactionResult = - await ParliamentContractStub.GetDefaultOrganizationAddress.SendWithExceptionAsync(new Empty()); - transactionResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); - transactionResult.TransactionResult.Error.Contains("Not initialized.").ShouldBeTrue(); - - await InitializeParliamentContracts(); - var defaultParliamentAddress = - await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); - defaultParliamentAddress.ShouldNotBeNull(); - } - - [Fact] - public async Task ParliamentContract_Initialize_Test() - { - var result = await ParliamentContractStub.Initialize.SendAsync(new InitializeInput()); - result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); - } - [Fact] public async Task ParliamentContract_InitializeTwice_Test() { - await ParliamentContract_Initialize_Test(); - var result = await ParliamentContractStub.Initialize.SendWithExceptionAsync(new InitializeInput()); result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); result.TransactionResult.Error.Contains("Already initialized.").ShouldBeTrue(); @@ -66,7 +43,7 @@ public async Task ParliamentContract_InitializeTwice_Test() [Fact] public async Task Get_Organization_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 10000 / MinersCount; var maximalAbstentionThreshold = 2000 / MinersCount; var maximalRejectionThreshold = 3000 / MinersCount; @@ -110,7 +87,7 @@ public async Task Get_OrganizationFailed_Test() [Fact] public async Task Get_Proposal_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -152,7 +129,7 @@ public async Task ApproveMultiProposals_Without_Authority_Test() [Fact] public async Task ApproveMultiProposals_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -185,7 +162,7 @@ public async Task Get_ProposalFailed_Test() [Fact] public async Task Create_OrganizationFailed_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -204,13 +181,7 @@ public async Task Create_OrganizationFailed_Test() }; var minerParliamentContractStub = GetParliamentContractTester(InitialMinersKeyPairs[0]); - - { - var transactionResult = - await ParliamentContractStub.CreateOrganization.SendWithExceptionAsync(createOrganizationInput); - transactionResult.TransactionResult.Error.ShouldContain("Unauthorized to create organization."); - } - + { createOrganizationInput.ProposalReleaseThreshold = proposalReleaseThreshold; createOrganizationInput.ProposalReleaseThreshold.MinimalApprovalThreshold = 10000; @@ -280,7 +251,7 @@ public async Task Create_OrganizationFailed_Test() [Fact] public async Task Create_ProposalFailed_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -325,7 +296,7 @@ public async Task Create_ProposalFailed_Test() } //"Expired proposal." { - createProposalInput.ExpiredTime = TimestampHelper.GetUtcNow().AddSeconds(-1); + createProposalInput.ExpiredTime = TimestampHelper.GetUtcNow().AddMinutes(-10); var transactionResult = await ParliamentContractStub.CreateProposal.SendWithExceptionAsync(createProposalInput); transactionResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); @@ -374,7 +345,7 @@ public async Task Approve_Proposal_NotFoundProposal_Test() [Fact] public async Task Approve_Proposal_NotAuthorizedApproval_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -392,7 +363,7 @@ public async Task Approve_Proposal_NotAuthorizedApproval_Test() [Fact] public async Task Approve_Proposal_ExpiredTime_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -410,7 +381,7 @@ public async Task Approve_Proposal_ExpiredTime_Test() [Fact] public async Task Approve_Proposal_ApprovalAlreadyExists_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -436,7 +407,7 @@ public async Task Approve_Proposal_ApprovalAlreadyExists_Test() [Fact] public async Task Reject_Without_Authority_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -453,7 +424,7 @@ public async Task Reject_Without_Authority_Test() [Fact] public async Task Reject_With_Invalid_Proposal_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -482,7 +453,7 @@ public async Task Reject_With_Invalid_Proposal_Test() [Fact] public async Task Reject_Approved_Proposal_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -501,7 +472,7 @@ public async Task Reject_Approved_Proposal_Test() [Fact] public async Task Reject_Success_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -519,7 +490,7 @@ public async Task Reject_Success_Test() [Fact] public async Task Abstain_Without_Authority_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -536,7 +507,7 @@ public async Task Abstain_Without_Authority_Test() [Fact] public async Task Abstain_With_Invalid_Proposal_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -565,7 +536,7 @@ public async Task Abstain_With_Invalid_Proposal_Test() [Fact] public async Task Abstain_Approved_Proposal_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -584,7 +555,7 @@ public async Task Abstain_Approved_Proposal_Test() [Fact] public async Task Abstain_Success_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -602,7 +573,7 @@ public async Task Abstain_Success_Test() [Fact] public async Task Check_Proposal_ToBeReleased() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); { var minimalApprovalThreshold = 3000; @@ -676,7 +647,7 @@ public async Task Check_Proposal_ToBeReleased() [Fact] public async Task Release_NotEnoughApprove_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -706,7 +677,7 @@ public async Task Release_NotFound_Test() [Fact] public async Task Release_WrongSender_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -727,7 +698,7 @@ public async Task Release_WrongSender_Test() [Fact] public async Task Release_Expired_Proposal_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -744,7 +715,7 @@ public async Task Release_Expired_Proposal_Test() [Fact] public async Task Release_Proposal_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -776,7 +747,7 @@ public async Task Release_Proposal_Test() [Fact] public async Task Release_Proposal_AlreadyReleased_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -818,7 +789,7 @@ await ParliamentContractStub.ChangeOrganizationThreshold.SendWithExceptionAsync( [Fact] public async Task Change_OrganizationThreshold_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 3000; var maximalAbstentionThreshold = 3000; var maximalRejectionThreshold = 3000; @@ -881,7 +852,7 @@ public async Task Change_OrganizationThreshold_Test() [Fact] public async Task Check_ValidProposal_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6000; var maximalAbstentionThreshold = 2000; @@ -929,7 +900,7 @@ public async Task Check_ValidProposal_Test() [Fact] public async Task Clear_NotExpiredProposal_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var defaultParliamentAddress = await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); var miner = InitialMinersKeyPairs[1]; @@ -950,7 +921,7 @@ public async Task Clear_NotExpiredProposal_Test() [Fact] public async Task Clear_ExpiredProposal_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; @@ -974,7 +945,7 @@ public async Task Clear_ExpiredProposal_Test() [Fact] public async Task ChangeMethodFeeController_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var parliamentContractStub = GetParliamentContractTester(InitialMinersKeyPairs[0]); var createOrganizationResult = await parliamentContractStub.CreateOrganization.SendAsync( @@ -1014,7 +985,7 @@ await parliamentContractStub.CreateOrganization.SendAsync( [Fact] public async Task ChangeMethodFeeController_WithoutAuth_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -1037,7 +1008,7 @@ public async Task ChangeMethodFeeController_WithoutAuth_Test() [Fact] public async Task ChangeMethodFeeController_With_Invalid_Authority_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var parliamentContractStub = GetParliamentContractTester(InitialMinersKeyPairs[0]); @@ -1103,7 +1074,7 @@ public async Task SetMethodFee_With_Invalid_Input_Test() [Fact] public async Task SetMethodFee_Without_Authority_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var setMethodFeeRet = await ParliamentContractStub.SetMethodFee.SendWithExceptionAsync(new MethodFees { MethodName = nameof(ParliamentContractStub.Abstain), @@ -1122,7 +1093,7 @@ public async Task SetMethodFee_Without_Authority_Test() [Fact] public async Task SetMethodFee_Success_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var parliamentContractStub = GetParliamentContractTester(InitialMinersKeyPairs[0]); var methodFeeController = await parliamentContractStub.GetMethodFeeController.CallAsync(new Empty()); var methodFeeName = nameof(parliamentContractStub.Abstain); @@ -1213,7 +1184,7 @@ await _smartContractAddressService.SetSmartContractAddressAsync(blockIndex, [Fact] public async Task ValidateOrganizationExist_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -1233,10 +1204,10 @@ public async Task ValidateOrganizationExist_Test() public async Task ValidateProposerInWhiteList_Test() { var proposer = DefaultSender; - await ParliamentContractStub.Initialize.SendAsync(new InitializeInput - { - PrivilegedProposer = proposer - }); + // await ParliamentContractStub.Initialize.SendAsync(new InitializeInput + // { + // PrivilegedProposer = proposer + // }); var isProposerInWhitelist = await ParliamentContractStub.ValidateProposerInWhiteList.CallAsync(new ValidateProposerInWhiteListInput { @@ -1287,10 +1258,10 @@ await _smartContractAddressService.SetSmartContractAddressAsync(blockIndex, [Fact] public async Task CreateProposalBySystemContract_Success_Test() { - await ParliamentContractStub.Initialize.SendAsync(new InitializeInput - { - PrivilegedProposer = DefaultSender - }); + // await ParliamentContractStub.Initialize.SendAsync(new InitializeInput + // { + // PrivilegedProposer = DefaultSender + // }); var defaultParliamentAddress = await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); var chain = _blockchainService.GetChainAsync(); @@ -1338,7 +1309,7 @@ public async Task GetNotVotedProposals_With_Invalid_Proposal_Test() //fail to validate proposal, proposal expires { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -1372,7 +1343,7 @@ public async Task GetNotVotedPendingProposals_With_Invalid_Proposal_Test() } //fail to validate proposal, proposal expires { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -1395,7 +1366,7 @@ public async Task GetNotVotedPendingProposals_With_Invalid_Proposal_Test() [Fact] public async Task ApproveMultiProposals_With_Invalid_Proposal_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6667; var maximalAbstentionThreshold = 2000; var maximalRejectionThreshold = 3000; @@ -1417,7 +1388,7 @@ await ParliamentContractStub.ApproveMultiProposals.SendAsync(new ProposalIdList [Fact] public async Task Check_ValidProposal_With_Rejected_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 6000; var maximalAbstentionThreshold = 1; @@ -1461,7 +1432,7 @@ public async Task Check_ValidProposal_With_Rejected_Test() [Fact] public async Task GetReleaseThresholdReachedProposals_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 3000; var maximalAbstentionThreshold = 3000; @@ -1504,7 +1475,7 @@ public async Task GetReleaseThresholdReachedProposals_Test() [Fact] public async Task GetAvailableProposals_Test() { - await InitializeParliamentContracts(); + // await InitializeParliamentContracts(); var minimalApprovalThreshold = 3000; var maximalAbstentionThreshold = 3000; diff --git a/test/AElf.Contracts.Parliament.Tests/ParliamentContractTestBase.cs b/test/AElf.Contracts.Parliament.Tests/ParliamentContractTestBase.cs index 4cc77daaa3..d70eac58c3 100644 --- a/test/AElf.Contracts.Parliament.Tests/ParliamentContractTestBase.cs +++ b/test/AElf.Contracts.Parliament.Tests/ParliamentContractTestBase.cs @@ -8,6 +8,7 @@ using AElf.Contracts.TestBase; using AElf.ContractTestKit; using AElf.Cryptography.ECDSA; +using AElf.CSharp.Core.Extension; using AElf.Kernel; using AElf.Kernel.Consensus; using AElf.Kernel.Proposal; @@ -37,6 +38,9 @@ public class ParliamentContractTestBase : ContractTestKit.ContractTestBase Accounts[10].Address; protected Address Tester => Accounts[MinersCount + 1].Address; + protected List InitialCoreDataCenterKeyPairs => + Accounts.Take(3).Select(a => a.KeyPair).ToList(); + protected Address TokenContractAddress { get; set; } protected Address ConsensusContractAddress { get; set; } protected Address ParliamentContractAddress { get; set; } @@ -66,6 +70,20 @@ protected void InitializeContracts() DefaultSenderKeyPair )); ParliamentContractStub = GetParliamentContractTester(DefaultSenderKeyPair); + AsyncHelper.RunSync(() => ParliamentContractStub.Initialize.SendAsync(new InitializeInput + { + ProposerAuthorityRequired = false, + PrivilegedProposer = DefaultSender + })); + + ConsensusContractAddress = AsyncHelper.RunSync(() => DeploySystemSmartContract( + KernelConstants.CodeCoverageRunnerCategory, + DPoSConsensusCode, + ConsensusSmartContractAddressNameProvider.Name, + DefaultSenderKeyPair)); + ConsensusContractStub = GetConsensusContractTester(DefaultSenderKeyPair); + AsyncHelper.RunSync(async () => await InitializeConsensusAsync()); + //deploy token contract TokenContractAddress = AsyncHelper.RunSync(() => DeploySystemSmartContract( @@ -75,14 +93,6 @@ protected void InitializeContracts() DefaultSenderKeyPair)); TokenContractStub = GetTokenContractTester(DefaultSenderKeyPair); AsyncHelper.RunSync(async () => await InitializeTokenAsync()); - - ConsensusContractAddress = AsyncHelper.RunSync(() => DeploySystemSmartContract( - KernelConstants.CodeCoverageRunnerCategory, - DPoSConsensusCode, - ConsensusSmartContractAddressNameProvider.Name, - DefaultSenderKeyPair)); - ConsensusContractStub = GetConsensusContractTester(DefaultSenderKeyPair); - AsyncHelper.RunSync(async () => await InitializeConsensusAsync()); } @@ -112,15 +122,23 @@ private async Task InitializeTokenAsync() { const string symbol = "ELF"; const long totalSupply = 100_000_000; - await TokenContractStub.Create.SendAsync(new CreateInput + + var organizationAddress = + (await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty())); + var approveProposalId = await CreateParliamentProposalAsync(nameof(TokenContractStub.Create), organizationAddress, new CreateInput { Symbol = symbol, Decimals = 2, IsBurnable = true, TokenName = "elf token", TotalSupply = totalSupply, - Issuer = DefaultSender - }); + Issuer = DefaultSender, + Owner = DefaultSender + }, TokenContractAddress); + await ApproveWithMinersAsync(approveProposalId); + await ParliamentContractStub.Release.SendAsync(approveProposalId); + + await TokenContractStub.Issue.SendAsync(new IssueInput { Symbol = symbol, @@ -144,7 +162,34 @@ await ConsensusContractStub.FirstRound.SendAsync( internal async Task InitializeParliamentContracts() { - await ParliamentContractStub.Initialize.SendAsync(new InitializeInput()); + await ParliamentContractStub.Initialize.SendAsync(new InitializeInput + { + ProposerAuthorityRequired = false, + PrivilegedProposer = DefaultSender + }); + } + + internal async Task CreateParliamentProposalAsync(string method, Address organizationAddress, + IMessage input, Address toAddress = null) + { + var proposal = (await ParliamentContractStub.CreateProposal.SendAsync(new CreateProposalInput + { + ToAddress = toAddress, + ContractMethodName = method, + ExpiredTime = TimestampHelper.GetUtcNow().AddDays(1), + OrganizationAddress = organizationAddress, + Params = input.ToByteString() + })).Output; + return proposal; + } + + internal async Task ApproveWithMinersAsync(Hash proposalId) + { + foreach (var bp in InitialCoreDataCenterKeyPairs) + { + var tester = GetParliamentContractTester(bp); + await tester.Approve.SendAsync(proposalId); + } } } diff --git a/test/AElf.Contracts.Profit.Tests/ProfitContractTestBase.cs b/test/AElf.Contracts.Profit.Tests/ProfitContractTestBase.cs index 0b857f0e49..82dbf5f5ed 100644 --- a/test/AElf.Contracts.Profit.Tests/ProfitContractTestBase.cs +++ b/test/AElf.Contracts.Profit.Tests/ProfitContractTestBase.cs @@ -158,6 +158,7 @@ private SystemContractDeploymentInput.Types.SystemTransactionMethodCallList TokenName = "elf token", TotalSupply = ProfitContractTestConstants.NativeTokenTotalSupply, Issuer = Starter, + Owner = Starter, LockWhiteList = { ProfitContractAddress diff --git a/test/AElf.Contracts.Profit.Tests/ProfitTests.cs b/test/AElf.Contracts.Profit.Tests/ProfitTests.cs index 71d52660e0..34979069da 100644 --- a/test/AElf.Contracts.Profit.Tests/ProfitTests.cs +++ b/test/AElf.Contracts.Profit.Tests/ProfitTests.cs @@ -1679,19 +1679,6 @@ await ProfitContractStub.ClaimProfits.SendAsync(new ClaimProfitsInput details.Details.First().LastProfitPeriod.ShouldBe(scheme.CurrentPeriod); } } - - [Fact] - public async Task IncreaseBackupSubsidyTotalShare_Test() - { - var schemeId = await CreateSchemeAsync(); - var scheme = await ProfitContractStub.GetScheme.CallAsync(schemeId); - scheme.TotalShares.ShouldBe(0); - await ProfitContractStub.IncreaseBackupSubsidyTotalShare.SendAsync(schemeId); - scheme = await ProfitContractStub.GetScheme.CallAsync(schemeId); - scheme.TotalShares.ShouldBe(1); - var result = await ProfitContractStub.IncreaseBackupSubsidyTotalShare.SendWithExceptionAsync(schemeId); - result.TransactionResult.Error.ShouldContain("Already increased"); - } private async Task ContributeProfits(Hash schemeId, long amount = 100) { diff --git a/test/AElf.Contracts.Referendum.Tests/ReferendumContractTest.cs b/test/AElf.Contracts.Referendum.Tests/ReferendumContractTest.cs index e4e87910a0..d22a250401 100644 --- a/test/AElf.Contracts.Referendum.Tests/ReferendumContractTest.cs +++ b/test/AElf.Contracts.Referendum.Tests/ReferendumContractTest.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Threading.Tasks; using AElf.Contracts.MultiToken; +using AElf.Contracts.Parliament; using AElf.Cryptography.ECDSA; using AElf.CSharp.Core.Extension; using AElf.Kernel; @@ -97,6 +98,7 @@ public async Task Get_Proposal_Test() TotalSupply = 10_0000, TokenName = "new token", Issuer = organizationAddress, + Owner = organizationAddress, IsBurnable = true }; var proposalId = await CreateProposalAsync(DefaultSenderKeyPair, organizationAddress); @@ -531,6 +533,17 @@ public async Task Check_Proposal_ToBeRelease() result = await ReferendumContractStub.GetProposal.CallAsync(proposalId); result.ToBeReleased.ShouldBeFalse(); } + } + + [Fact] + public async Task Check_Proposal_ToBeRelease_Rejection() + { + var minimalApproveThreshold = 1000; + var minimalVoteThreshold = 1000; + var maximalRejectionThreshold = 1000; + var maximalAbstentionThreshold = 1000; + var organizationAddress = await CreateOrganizationAsync(minimalApproveThreshold, minimalVoteThreshold, + maximalAbstentionThreshold, maximalRejectionThreshold, new[] { DefaultSender }); //Rejection probability > maximalRejectionThreshold { var proposalId = await CreateProposalAsync(DefaultSenderKeyPair, organizationAddress); @@ -609,7 +622,7 @@ public async Task Release_Proposal_Test() var maximalAbstentionThreshold = 10000; var organizationAddress = await CreateOrganizationAsync(minimalApproveThreshold, minimalVoteThreshold, maximalAbstentionThreshold, maximalRejectionThreshold, new[] { DefaultSender }); - + await ApproveAndTransferCreateTokenFee(DefaultSenderKeyPair, minimalApproveThreshold, organizationAddress); var proposalId = await CreateProposalAsync(DefaultSenderKeyPair, organizationAddress); await ApproveAllowanceAsync(Accounts[3].KeyPair, minimalApproveThreshold, proposalId); @@ -634,7 +647,7 @@ public async Task Release_Proposal_AlreadyReleased_Test() var maximalAbstentionThreshold = 10000; var organizationAddress = await CreateOrganizationAsync(minimalApproveThreshold, minimalVoteThreshold, maximalAbstentionThreshold, maximalRejectionThreshold, new[] { DefaultSender }); - + await ApproveAndTransferCreateTokenFee(DefaultSenderKeyPair, minimalApproveThreshold, organizationAddress); var proposalId = await CreateProposalAsync(DefaultSenderKeyPair, organizationAddress); @@ -1290,6 +1303,7 @@ private CreateProposalInput GetValidCreateProposalInput(Address organizationAddr private async Task CreateProposalAsync(ECKeyPair proposalKeyPair, Address organizationAddress, Timestamp timestamp = null) { + await PrepareCreateToken(organizationAddress); var createInput = new CreateInput { Symbol = "NEW", @@ -1297,6 +1311,7 @@ private async Task CreateProposalAsync(ECKeyPair proposalKeyPair, Address TotalSupply = 10_0000, TokenName = "new token", Issuer = organizationAddress, + Owner = organizationAddress, IsBurnable = true }; var createProposalInput = new CreateProposalInput @@ -1313,6 +1328,61 @@ private async Task CreateProposalAsync(ECKeyPair proposalKeyPair, Address return proposal.Output; } + private async Task PrepareCreateToken(Address organizationAddress) + { + var defaultParliamentAddress = + await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); + var seedCollectionInfo = await TokenContractStub.GetTokenInfo.CallAsync(new GetTokenInfoInput + { + Symbol = "SEED-0" + }); + await SubmitAndApproveProposalOfDefaultParliament(DefaultSenderKeyPair, ParliamentContractAddress, + TokenContractAddress, + nameof(TokenContractStub.Create), new CreateInput + { + Symbol = "SEED-0", + Decimals = 0, + IsBurnable = true, + TokenName = "seed Collection", + TotalSupply = 1, + Issuer = defaultParliamentAddress, + ExternalInfo = new ExternalInfo(), + Owner = defaultParliamentAddress + }); + + await SubmitAndApproveProposalOfDefaultParliament(DefaultSenderKeyPair, ParliamentContractAddress, + TokenContractAddress, + nameof(TokenContractStub.Create), new CreateInput + { + Symbol = "SEED-1", + Decimals = 0, + IsBurnable = true, + TokenName = "seed token" + 1, + TotalSupply = 1, + Issuer = defaultParliamentAddress, + ExternalInfo = new ExternalInfo + { + Value = + { + { "__seed_owned_symbol", "NEW" }, + { "__seed_exp_time", TimestampHelper.GetUtcNow().AddDays(1).Seconds.ToString() } + } + }, + LockWhiteList = { TokenContractAddress }, + Owner = defaultParliamentAddress + }); + + await SubmitAndApproveProposalOfDefaultParliament(DefaultSenderKeyPair, ParliamentContractAddress, + TokenContractAddress, + nameof(TokenContractStub.Issue), new IssueInput + { + Symbol = "SEED-1", + Amount = 1, + Memo = "ddd", + To = organizationAddress + }); + } + private async Task CreateReferendumProposalAsync(ECKeyPair proposalKeyPair, IMessage input, string method, Address organizationAddress, Address toAddress) { @@ -1454,6 +1524,7 @@ private async Task ApproveWithMinersAsync(Hash proposalId) approveResult.TransactionResult.Error.ShouldBeNullOrEmpty(); } } + private async Task ApproveAndTransferCreateTokenFee(ECKeyPair proposalKeyPair, long minimalApproveThreshold, Address organizationAddress) { @@ -1497,4 +1568,33 @@ private MethodFees GetValidMethodFees() } }; } + + private async Task SubmitAndApproveProposalOfDefaultParliament(ECKeyPair senderKeyPair, Address parliamentAddress, + Address contractAddress, string methodName, IMessage message) + { + var defaultParliamentAddress = + await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); + + var proposal = new CreateProposalInput + { + OrganizationAddress = defaultParliamentAddress, + ContractMethodName = methodName, + ExpiredTime = TimestampHelper.GetUtcNow().AddHours(1), + Params = message.ToByteString(), + ToAddress = contractAddress + }; + var createResult = await ParliamentContractStub.CreateProposal.SendAsync(proposal); + var proposalId = createResult.Output; + await ApproveWithMinersAsync(proposalId, parliamentAddress); + await ParliamentContractStub.Release.SendAsync(proposalId); + } + + private async Task ApproveWithMinersAsync(Hash proposalId, Address parliamentAddress) + { + foreach (var bp in InitialCoreDataCenterKeyPairs) + { + var tester = GetTester(parliamentAddress, bp); + await tester.Approve.SendAsync(proposalId); + } + } } \ No newline at end of file diff --git a/test/AElf.Contracts.Referendum.Tests/ReferendumContractTestBase.cs b/test/AElf.Contracts.Referendum.Tests/ReferendumContractTestBase.cs index add7e3d3fb..58d057d846 100644 --- a/test/AElf.Contracts.Referendum.Tests/ReferendumContractTestBase.cs +++ b/test/AElf.Contracts.Referendum.Tests/ReferendumContractTestBase.cs @@ -141,7 +141,8 @@ private SystemContractDeploymentInput.Types.SystemTransactionMethodCallList IsBurnable = true, TokenName = "elf token", TotalSupply = totalSupply, - Issuer = DefaultSender + Issuer = DefaultSender, + Owner = DefaultSender, }); //issue default user diff --git a/test/AElf.Contracts.TestBase/ContractTester.cs b/test/AElf.Contracts.TestBase/ContractTester.cs index 2ac2ea3092..06520a17d2 100644 --- a/test/AElf.Contracts.TestBase/ContractTester.cs +++ b/test/AElf.Contracts.TestBase/ContractTester.cs @@ -710,6 +710,7 @@ public Action> GetDefaultContractTypes(Address iss TotalSupply = totalSupply, Decimals = 8, Issuer = issuer, + Owner = issuer, IsBurnable = true }); tokenContractCallList.Add(nameof(TokenContract.SetPrimaryTokenSymbol), @@ -767,6 +768,7 @@ public Action> GetSideChainSystemContract(Address Symbol = "ELF", Decimals = 2, Issuer = issuer, + Owner = issuer, IsBurnable = true, TokenName = "elf token", TotalSupply = TokenTotalSupply, @@ -781,6 +783,7 @@ public Action> GetSideChainSystemContract(Address Decimals = nativeTokenInfo.Decimals, IssueChainId = nativeTokenInfo.IssueChainId, Issuer = nativeTokenInfo.Issuer, + Owner = nativeTokenInfo.Issuer, IsBurnable = nativeTokenInfo.IsBurnable, Symbol = nativeTokenInfo.Symbol, TokenName = nativeTokenInfo.TokenName, @@ -796,6 +799,7 @@ public Action> GetSideChainSystemContract(Address Decimals = 2, IsBurnable = true, Issuer = Address.FromPublicKey(KeyPair.PublicKey), + Owner = Address.FromPublicKey(KeyPair.PublicKey), TotalSupply = 1_000_000_000, Symbol = symbol, TokenName = "TEST", diff --git a/test/AElf.Contracts.TestContract.BasicFunction/BasicContract_Action.cs b/test/AElf.Contracts.TestContract.BasicFunction/BasicContract_Action.cs index b2b539bb91..b5596c26d7 100644 --- a/test/AElf.Contracts.TestContract.BasicFunction/BasicContract_Action.cs +++ b/test/AElf.Contracts.TestContract.BasicFunction/BasicContract_Action.cs @@ -185,6 +185,7 @@ public override Empty CreateTokenThroughMultiToken(CreateTokenThroughMultiTokenI Symbol = input.Symbol, Decimals = input.Decimals, Issuer = input.Issuer, + Owner = input.Issuer, IsBurnable = input.IsBurnable, IssueChainId = input.IssueChainId, TokenName = input.TokenName, diff --git a/test/AElf.Contracts.TestContract.DApp/DAppContract.cs b/test/AElf.Contracts.TestContract.DApp/DAppContract.cs index 9e5d90337f..ec205bd134 100644 --- a/test/AElf.Contracts.TestContract.DApp/DAppContract.cs +++ b/test/AElf.Contracts.TestContract.DApp/DAppContract.cs @@ -197,6 +197,7 @@ private void CreateToken(Address profitReceiver, bool isLockWhiteListIncludingSe TokenName = "DApp Token", Decimals = DAppConstants.Decimal, Issuer = Context.Self, + Owner = Context.Self, IsBurnable = true, TotalSupply = DAppConstants.TotalSupply, LockWhiteList = diff --git a/test/AElf.Contracts.TestContract.MockParliament/AElf.Contracts.TestContract.MockParliament.csproj b/test/AElf.Contracts.TestContract.MockParliament/AElf.Contracts.TestContract.MockParliament.csproj new file mode 100644 index 0000000000..b9228e40c9 --- /dev/null +++ b/test/AElf.Contracts.TestContract.MockParliament/AElf.Contracts.TestContract.MockParliament.csproj @@ -0,0 +1,31 @@ + + + + net6.0 + 1.2.0 + AElf.Contracts.TestContract.MockParliament + AElf.Contracts.TestContract.MockParliament + + + + + + + + + Protobuf\Proto\authority_info.proto + + + + + + Protobuf\Proto\test_mock_parliament_contract.proto + + + + + + + + + diff --git a/test/AElf.Contracts.TestContract.MockParliament/Contract.cs b/test/AElf.Contracts.TestContract.MockParliament/Contract.cs new file mode 100644 index 0000000000..046457ca88 --- /dev/null +++ b/test/AElf.Contracts.TestContract.MockParliament/Contract.cs @@ -0,0 +1,19 @@ +using AElf.Types; +using Google.Protobuf.WellKnownTypes; + +namespace AElf.Contracts.TestContract.MockParliament; + +public class Contract : MockParliamentContractContainer.MockParliamentContractBase +{ + public override Address GetDefaultOrganizationAddress(Empty input) + { + return State.DefaultOrganizationAddress.Value ?? new Address(); + } + + public override Empty Initialize(InitializeInput input) + { + State.DefaultOrganizationAddress.Value = input.PrivilegedProposer; + + return new Empty(); + } +} \ No newline at end of file diff --git a/test/AElf.Contracts.TestContract.MockParliament/State.cs b/test/AElf.Contracts.TestContract.MockParliament/State.cs new file mode 100644 index 0000000000..07fa208914 --- /dev/null +++ b/test/AElf.Contracts.TestContract.MockParliament/State.cs @@ -0,0 +1,9 @@ +using AElf.Sdk.CSharp.State; +using AElf.Types; + +namespace AElf.Contracts.TestContract.MockParliament; + +public partial class MockParliamentContractState : ContractState +{ + public SingletonState
DefaultOrganizationAddress { get; set; } +} \ No newline at end of file diff --git a/test/AElf.Contracts.TestContract.Tests/TestContractTestBase.cs b/test/AElf.Contracts.TestContract.Tests/TestContractTestBase.cs index 8ebd7a63e2..861a23a300 100644 --- a/test/AElf.Contracts.TestContract.Tests/TestContractTestBase.cs +++ b/test/AElf.Contracts.TestContract.Tests/TestContractTestBase.cs @@ -329,7 +329,8 @@ protected async Task InitializeTestContracts() { TokenConverterContractAddress, TreasuryContractAddress - } + }, + Owner = DefaultSender }); CheckResult(createResult.TransactionResult); @@ -356,7 +357,8 @@ protected async Task InitializeTestContracts() { TokenConverterContractAddress, TreasuryContractAddress - } + }, + Owner = DefaultSender }); CheckResult(createResult.TransactionResult); issueResult = await TokenContractStub.Issue.SendAsync(new IssueInput diff --git a/test/AElf.Contracts.TestContract.VirtualAddress/AElf.Contracts.TestContract.VirtualAddress.csproj b/test/AElf.Contracts.TestContract.VirtualAddress/AElf.Contracts.TestContract.VirtualAddress.csproj new file mode 100644 index 0000000000..79dacdf5f2 --- /dev/null +++ b/test/AElf.Contracts.TestContract.VirtualAddress/AElf.Contracts.TestContract.VirtualAddress.csproj @@ -0,0 +1,47 @@ + + + + net6.0 + AElf.Contracts.TestContract.VirtualAddress + AElf.Contracts.TestContract.VirtualAddress + false + + + + + + + + + Protobuf\Proto\authority_info.proto + + + + + + Protobuf\Proto\reference\election_contract.proto + + + Protobuf\Proto\reference\election_contract_impl.proto + + + Protobuf\Proto\profit_contract.proto + + + Protobuf\Proto\profit_contract_impl.proto + + + + + + Protobuf\Proto\acs1.proto + + + + + + Protobuf\Proto\test_virtual_address_contract.proto + + + + diff --git a/test/AElf.Contracts.TestContract.VirtualAddress/Action.cs b/test/AElf.Contracts.TestContract.VirtualAddress/Action.cs new file mode 100644 index 0000000000..295830ea1c --- /dev/null +++ b/test/AElf.Contracts.TestContract.VirtualAddress/Action.cs @@ -0,0 +1,88 @@ +using AElf.Contracts.Election; +using AElf.Contracts.Profit; +using AElf.Sdk.CSharp; +using AElf.Types; +using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; + +namespace AElf.Contracts.TestContract.VirtualAddress; + +public partial class Action : VirtualAddressContractContainer.VirtualAddressContractBase +{ + public override Empty VirtualAddressVote(VirtualAddressVoteInput input) + { + Initialize(); + + Context.SendVirtualInline(HashHelper.ComputeFrom("test"), State.ElectionContract.Value, "Vote", new VoteMinerInput + { + CandidatePubkey = input.PubKey, + Amount = input.Amount, + EndTimestamp = input.EndTimestamp, + Token = input.Token + }.ToByteString()); + + return new Empty(); + } + + public override Empty VirtualAddressWithdraw(Hash input) + { + Initialize(); + + Context.SendVirtualInline(HashHelper.ComputeFrom("test"), State.ElectionContract.Value, "Withdraw", input.ToByteString()); + + return new Empty(); + } + + public override Empty VirtualAddressChangeVotingOption(VirtualAddressChangeVotingOptionInput input) + { + Initialize(); + + Context.SendVirtualInline(HashHelper.ComputeFrom("test"), State.ElectionContract.Value, "ChangeVotingOption", new ChangeVotingOptionInput + { + CandidatePubkey = input.PubKey, + VoteId = input.VoteId, + IsResetVotingTime = input.IsReset + }.ToByteString()); + + return new Empty(); + } + + public override Empty VirtualAddressClaimProfit(VirtualAddressClaimProfitInput input) + { + Initialize(); + + Context.SendVirtualInline(HashHelper.ComputeFrom("test"), State.ProfitContract.Value, "ClaimProfits", new ClaimProfitsInput + { + SchemeId = input.SchemeId, + Beneficiary = input.Beneficiary + }.ToByteString()); + + return new Empty(); + } + + public override Empty ForwardCall(ForwardCallInput input) + { + Context.SendVirtualInline(input.VirtualAddress, input.ContractAddress, input.MethodName, input.Args); + return new Empty(); + } + + public override Address GetVirtualAddress(Empty input) + { + return Context.ConvertVirtualAddressToContractAddress(HashHelper.ComputeFrom("test")); + } + + private void Initialize() + { + if (State.ElectionContract.Value == null) + { + State.ElectionContract.Value = + Context.GetContractAddressByName(SmartContractConstants.ElectionContractSystemName); + } + + if (State.ProfitContract.Value == null) + { + State.ProfitContract.Value = + Context.GetContractAddressByName(SmartContractConstants.ProfitContractSystemName); + } + } +} \ No newline at end of file diff --git a/test/AElf.Contracts.TestContract.VirtualAddress/Action_Fee.cs b/test/AElf.Contracts.TestContract.VirtualAddress/Action_Fee.cs new file mode 100644 index 0000000000..4d117b11f7 --- /dev/null +++ b/test/AElf.Contracts.TestContract.VirtualAddress/Action_Fee.cs @@ -0,0 +1,32 @@ +using AElf.Sdk.CSharp; +using AElf.Standards.ACS1; +using Google.Protobuf.WellKnownTypes; + +namespace AElf.Contracts.TestContract.VirtualAddress; + +public partial class Action +{ + [View] + public override MethodFees GetMethodFee(StringValue input) + { + var methodFees = State.TransactionFees[input.Value]; + if (methodFees != null) + return methodFees; + + //set default tx fee + return new MethodFees + { + MethodName = input.Value, + Fees = + { + new MethodFee { Symbol = "ELF", BasicFee = 1000_0000 } + } + }; + } + + public override Empty SetMethodFee(MethodFees input) + { + State.TransactionFees[input.MethodName] = input; + return new Empty(); + } +} \ No newline at end of file diff --git a/test/AElf.Contracts.TestContract.VirtualAddress/ReferenceState.cs b/test/AElf.Contracts.TestContract.VirtualAddress/ReferenceState.cs new file mode 100644 index 0000000000..e9d8ef4482 --- /dev/null +++ b/test/AElf.Contracts.TestContract.VirtualAddress/ReferenceState.cs @@ -0,0 +1,10 @@ +using AElf.Contracts.Election; +using AElf.Contracts.Profit; + +namespace AElf.Contracts.TestContract.VirtualAddress; + +public partial class State +{ + internal ElectionContractContainer.ElectionContractReferenceState ElectionContract { get; set; } + internal ProfitContractContainer.ProfitContractReferenceState ProfitContract { get; set; } +} \ No newline at end of file diff --git a/test/AElf.Contracts.TestContract.VirtualAddress/State.cs b/test/AElf.Contracts.TestContract.VirtualAddress/State.cs new file mode 100644 index 0000000000..0869e1a44e --- /dev/null +++ b/test/AElf.Contracts.TestContract.VirtualAddress/State.cs @@ -0,0 +1,9 @@ +using AElf.Sdk.CSharp.State; +using AElf.Standards.ACS1; + +namespace AElf.Contracts.TestContract.VirtualAddress; + +public partial class State : ContractState +{ + public MappedState TransactionFees { get; set; } +} \ No newline at end of file diff --git a/test/AElf.Contracts.TestContract.Vote/AElf.Contracts.TestContract.Vote.csproj b/test/AElf.Contracts.TestContract.Vote/AElf.Contracts.TestContract.Vote.csproj new file mode 100644 index 0000000000..2924bb78db --- /dev/null +++ b/test/AElf.Contracts.TestContract.Vote/AElf.Contracts.TestContract.Vote.csproj @@ -0,0 +1,29 @@ + + + + net6.0 + 1.1.0 + + + + true + + + + + + + + + Protobuf\Proto\authority_info.proto + + + + + Protobuf\Proto\acs1.proto + + + Protobuf\Proto\test_vote_contract.proto + + + diff --git a/test/AElf.Contracts.TestContract.Vote/VoteContract.cs b/test/AElf.Contracts.TestContract.Vote/VoteContract.cs new file mode 100644 index 0000000000..4e7ae7c700 --- /dev/null +++ b/test/AElf.Contracts.TestContract.Vote/VoteContract.cs @@ -0,0 +1,11 @@ +using Google.Protobuf.WellKnownTypes; + +namespace AElf.Contracts.TestContract.Vote; + +public class VoteContract : VoteContractContainer.VoteContractBase +{ + public override Empty AddOption(AddOptionInput input) + { + return new Empty(); + } +} \ No newline at end of file diff --git a/test/AElf.Contracts.TestContract.Vote/VoteContractState.cs b/test/AElf.Contracts.TestContract.Vote/VoteContractState.cs new file mode 100644 index 0000000000..b7dbffd482 --- /dev/null +++ b/test/AElf.Contracts.TestContract.Vote/VoteContractState.cs @@ -0,0 +1,10 @@ +using AElf.Sdk.CSharp.State; +using AElf.Types; +using Google.Protobuf.WellKnownTypes; + +namespace AElf.Contracts.TestContract.Vote; + +public class VoteContractState : ContractState +{ + public MappedState State { get; set; } +} \ No newline at end of file diff --git a/test/AElf.Contracts.TokenConverter.Tests/TokenConvertConnectorTest.cs b/test/AElf.Contracts.TokenConverter.Tests/TokenConvertConnectorTest.cs index fdefbd921a..6c462e1d96 100644 --- a/test/AElf.Contracts.TokenConverter.Tests/TokenConvertConnectorTest.cs +++ b/test/AElf.Contracts.TokenConverter.Tests/TokenConvertConnectorTest.cs @@ -15,11 +15,6 @@ namespace AElf.Contracts.TokenConverter; public partial class TokenConverterContractTests { - public TokenConverterContractTests() - { - AsyncHelper.RunSync(InitializeParliamentContractAsync); - } - [Fact] public async Task DefaultController_Test() { @@ -295,15 +290,7 @@ public async Task Set_Connector_Test() var tokenSymbol = "TRA"; //with authority user { - var createTokenRet = (await TokenContractStub.Create.SendAsync(new CreateInput - { - Symbol = tokenSymbol, - TokenName = "NET name", - TotalSupply = 100_0000_0000, - Issuer = DefaultSender, - IsBurnable = true - })).TransactionResult; - createTokenRet.Status.ShouldBe(TransactionResultStatus.Mined); + await CreateTokenAsync(tokenSymbol); var pairConnector = new PairConnectorParam { ResourceConnectorSymbol = tokenSymbol, @@ -328,16 +315,7 @@ await ExecuteProposalForParliamentTransaction(TokenConverterContractAddress, public async Task Update_Connector_Success_Test() { var token = "NETT"; - var createTokenRet = (await TokenContractStub.Create.SendAsync(new CreateInput - { - Symbol = token, - TokenName = "NETT name", - TotalSupply = 100_0000_0000, - Issuer = DefaultSender, - IsBurnable = true, - LockWhiteList = { TokenConverterContractAddress } - })).TransactionResult; - createTokenRet.Status.ShouldBe(TransactionResultStatus.Mined); + await CreateTokenAsync(token); var pairConnector = new PairConnectorParam { ResourceConnectorSymbol = token, @@ -526,15 +504,17 @@ private PairConnectorParam GetLegalPairConnectorParam(string tokenSymbol, long n private async Task CreateTokenAsync(string symbol, long totalSupply = 100_0000_0000) { - await TokenContractStub.Create.SendAsync(new CreateInput - { - Symbol = symbol, - TokenName = symbol + " name", - TotalSupply = totalSupply, - Issuer = DefaultSender, - IsBurnable = true, - LockWhiteList = { TokenConverterContractAddress } - }); + await ExecuteProposalForParliamentTransaction(TokenContractAddress, nameof(TokenContractStub.Create), + new CreateInput + { + Symbol = symbol, + TokenName = symbol + " name", + TotalSupply = totalSupply, + Issuer = DefaultSender, + Owner = DefaultSender, + IsBurnable = true, + LockWhiteList = { TokenContractAddress, TokenConverterContractAddress } + }); } private async Task AddPairConnectorAsync(string tokenSymbol) @@ -545,59 +525,4 @@ await ExecuteProposalForParliamentTransaction( nameof(TokenConverterContractImplContainer.TokenConverterContractImplStub.AddPairConnector), pairConnector); } - - private async Task ApproveByParliamentMembers(Hash proposalId) - { - foreach (var bp in InitialCoreDataCenterKeyPairs) - { - var tester = GetParliamentContractTester(bp); - await tester.Approve.SendAsync(proposalId); - } - } - - private async Task CreateAndApproveProposalForParliament(Address contract, - string method, IMessage input, Address parliamentOrganization = null) - { - if (parliamentOrganization == null) - parliamentOrganization = - await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); - var proposal = new CreateProposalInput - { - OrganizationAddress = parliamentOrganization, - ContractMethodName = method, - ExpiredTime = TimestampHelper.GetUtcNow().AddHours(1), - Params = input.ToByteString(), - ToAddress = contract - }; - var createResult = await ParliamentContractStub.CreateProposal.SendAsync(proposal); - var proposalHash = createResult.Output; - await ApproveByParliamentMembers(proposalHash); - return proposalHash; - } - - private async Task ExecuteProposalForParliamentTransactionWithException( - Address contract, - string method, IMessage input, Address parliamentOrganization = null) - { - if (parliamentOrganization == null) - parliamentOrganization = - await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); - var proposalHash = - await CreateAndApproveProposalForParliament(contract, method, input, - parliamentOrganization); - var releaseResult = await ParliamentContractStub.Release.SendWithExceptionAsync(proposalHash); - return releaseResult.TransactionResult; - } - - private async Task ExecuteProposalForParliamentTransaction(Address contract, - string method, IMessage input, Address parliamentOrganization = null) - { - if (parliamentOrganization == null) - parliamentOrganization = - await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); - var proposalHash = - await CreateAndApproveProposalForParliament(contract, method, input, - parliamentOrganization); - await ParliamentContractStub.Release.SendAsync(proposalHash); - } } \ No newline at end of file diff --git a/test/AElf.Contracts.TokenConverter.Tests/TokenConverterContractTests.cs b/test/AElf.Contracts.TokenConverter.Tests/TokenConverterContractTests.cs index 8d8bd44935..2f598d96e1 100644 --- a/test/AElf.Contracts.TokenConverter.Tests/TokenConverterContractTests.cs +++ b/test/AElf.Contracts.TokenConverter.Tests/TokenConverterContractTests.cs @@ -363,17 +363,18 @@ private InitializeInput GetLegalInitializeInput() private async Task CreateRamToken() { - var createResult = (await TokenContractStub.Create.SendAsync( + await ExecuteProposalForParliamentTransaction(TokenContractAddress, nameof(TokenContractStub.Create), new CreateInput { Symbol = WriteConnector.Symbol, Decimals = 2, IsBurnable = true, Issuer = DefaultSender, + Owner = DefaultSender, TokenName = "Write Resource", - TotalSupply = 100_0000L - })).TransactionResult; - createResult.Status.ShouldBe(TransactionResultStatus.Mined); + TotalSupply = 100_0000L, + LockWhiteList = { TokenContractAddress, TokenConverterContractAddress } + }); var issueResult = (await TokenContractStub.Issue.SendAsync( new IssueInput diff --git a/test/AElf.Contracts.TokenConverter.Tests/TokenConverterTestBase.cs b/test/AElf.Contracts.TokenConverter.Tests/TokenConverterTestBase.cs index e453e1b0fd..3167d6a9e5 100644 --- a/test/AElf.Contracts.TokenConverter.Tests/TokenConverterTestBase.cs +++ b/test/AElf.Contracts.TokenConverter.Tests/TokenConverterTestBase.cs @@ -7,11 +7,15 @@ using AElf.Contracts.Treasury; using AElf.ContractTestKit.AEDPoSExtension; using AElf.Cryptography.ECDSA; +using AElf.CSharp.Core.Extension; using AElf.EconomicSystem; +using AElf.Kernel; using AElf.Kernel.Consensus; using AElf.Kernel.Proposal; using AElf.Kernel.Token; +using AElf.Standards.ACS3; using AElf.Types; +using Google.Protobuf; using Google.Protobuf.WellKnownTypes; using Volo.Abp.Threading; @@ -30,7 +34,7 @@ public TokenConverterTestBase() ParliamentSmartContractAddressNameProvider.Name, ConsensusSmartContractAddressNameProvider.Name })); - + AsyncHelper.RunSync(InitializeParliamentContractAsync); AsyncHelper.RunSync(InitializeTokenAsync); } @@ -47,16 +51,18 @@ protected async Task GetBalanceAsync(string symbol, Address owner) private async Task InitializeTokenAsync() { - await TokenContractStub.Create.SendAsync(new CreateInput - { - Symbol = "ELF", - Decimals = 2, - IsBurnable = true, - TokenName = "elf token", - TotalSupply = 1000_0000_0000L, - Issuer = DefaultSender, - LockWhiteList = { TokenConverterContractAddress } - }); + await ExecuteProposalForParliamentTransaction(TokenContractAddress, nameof(TokenContractStub.Create), + new CreateInput + { + Symbol = "ELF", + Decimals = 2, + IsBurnable = true, + TokenName = "elf token", + TotalSupply = 1000_0000_0000L, + Issuer = DefaultSender, + Owner = DefaultSender, + LockWhiteList = { TokenContractAddress, TokenConverterContractAddress } + }); await TokenContractStub.Issue.SendAsync(new IssueInput { Symbol = "ELF", @@ -94,6 +100,61 @@ private void CheckResult(TransactionResult result) if (!string.IsNullOrEmpty(result.Error)) throw new Exception(result.Error); } + protected async Task ExecuteProposalForParliamentTransaction(Address contract, + string method, IMessage input, Address parliamentOrganization = null) + { + if (parliamentOrganization == null) + parliamentOrganization = + await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); + var proposalHash = + await CreateAndApproveProposalForParliament(contract, method, input, + parliamentOrganization); + await ParliamentContractStub.Release.SendAsync(proposalHash); + } + + private async Task CreateAndApproveProposalForParliament(Address contract, + string method, IMessage input, Address parliamentOrganization = null) + { + if (parliamentOrganization == null) + parliamentOrganization = + await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); + var proposal = new CreateProposalInput + { + OrganizationAddress = parliamentOrganization, + ContractMethodName = method, + ExpiredTime = TimestampHelper.GetUtcNow().AddHours(1), + Params = input.ToByteString(), + ToAddress = contract + }; + var createResult = await ParliamentContractStub.CreateProposal.SendAsync(proposal); + var proposalHash = createResult.Output; + await ApproveByParliamentMembers(proposalHash); + return proposalHash; + } + + protected async Task ExecuteProposalForParliamentTransactionWithException( + Address contract, + string method, IMessage input, Address parliamentOrganization = null) + { + if (parliamentOrganization == null) + parliamentOrganization = + await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); + var proposalHash = + await CreateAndApproveProposalForParliament(contract, method, input, + parliamentOrganization); + var releaseResult = await ParliamentContractStub.Release.SendWithExceptionAsync(proposalHash); + return releaseResult.TransactionResult; + } + + private async Task ApproveByParliamentMembers(Hash proposalId) + { + foreach (var bp in InitialCoreDataCenterKeyPairs) + { + var tester = GetParliamentContractTester(bp); + await tester.Approve.SendAsync(proposalId); + } + } + #region Contract Address protected Address TokenContractAddress => diff --git a/test/AElf.Contracts.TokenHolder.Tests/AElf.Contracts.TokenHolder.Tests.csproj b/test/AElf.Contracts.TokenHolder.Tests/AElf.Contracts.TokenHolder.Tests.csproj index ff8f4950ef..92957ed179 100644 --- a/test/AElf.Contracts.TokenHolder.Tests/AElf.Contracts.TokenHolder.Tests.csproj +++ b/test/AElf.Contracts.TokenHolder.Tests/AElf.Contracts.TokenHolder.Tests.csproj @@ -15,7 +15,7 @@ runtime; build; native; contentfiles; analyzers - + all @@ -27,15 +27,15 @@ - - - - - - - - - + + + + + + + + + @@ -91,6 +91,9 @@ Protobuf\Proto\aedpos_contract_impl.proto + + Protobuf\Proto\acs3.proto + Protobuf\Proto\acs4.proto diff --git a/test/AElf.Contracts.TokenHolder.Tests/TokenHolderContractTestBase.cs b/test/AElf.Contracts.TokenHolder.Tests/TokenHolderContractTestBase.cs index 2e4a17a843..e127c3a78b 100644 --- a/test/AElf.Contracts.TokenHolder.Tests/TokenHolderContractTestBase.cs +++ b/test/AElf.Contracts.TokenHolder.Tests/TokenHolderContractTestBase.cs @@ -91,6 +91,29 @@ protected void InitializeContracts() })).Output; TokenHolderContractStub = GetTokenHolderContractTester(StarterKeyPair); + //deploy parliament auth contract + ParliamentContractAddress = AsyncHelper.RunSync(() => GetContractZeroTester(StarterKeyPair) + .DeploySystemSmartContract.SendAsync( + new SystemContractDeploymentInput + { + Category = KernelConstants.CodeCoverageRunnerCategory, + Code = ByteString.CopyFrom(File.ReadAllBytes(typeof(ParliamentContract).Assembly.Location)), + Name = ParliamentSmartContractAddressNameProvider.Name, + TransactionMethodCallList = GenerateParliamentInitializationCallList() + })).Output; + ParliamentContractStub = GetParliamentContractTester(StarterKeyPair); + + //deploy DApp contract + DAppContractAddress = AsyncHelper.RunSync(() => GetContractZeroTester(StarterKeyPair) + .DeploySystemSmartContract.SendAsync( + new SystemContractDeploymentInput + { + Category = KernelConstants.CodeCoverageRunnerCategory, + Code = ByteString.CopyFrom(File.ReadAllBytes(typeof(DAppContract).Assembly.Location)), + Name = DappSmartContractAddressNameProvider.Name + })).Output; + DAppContractStub = GetTester(DAppContractAddress, UserKeyPairs.First()); + //deploy token contract TokenContractAddress = AsyncHelper.RunSync(() => GetContractZeroTester(StarterKeyPair) .DeploySystemSmartContract.SendAsync( @@ -102,18 +125,99 @@ protected void InitializeContracts() TransactionMethodCallList = GenerateTokenInitializationCallList() })).Output; TokenContractStub = GetTokenContractTester(StarterKeyPair); - - //deploy parliament auth contract - ParliamentContractAddress = AsyncHelper.RunSync(() => GetContractZeroTester(StarterKeyPair) - .DeploySystemSmartContract.SendAsync( - new SystemContractDeploymentInput + + AsyncHelper.RunSync(() => TokenContractStub.Create.SendAsync(new CreateInput + { + Symbol = "SEED-1", + Decimals = 0, + IsBurnable = true, + TokenName = "collection", + TotalSupply = 1, + Issuer = Starter, + Owner = Starter, + LockWhiteList = + { + TokenContractAddress + }, + ExternalInfo = new ExternalInfo + { + Value = { - Category = KernelConstants.CodeCoverageRunnerCategory, - Code = ByteString.CopyFrom(File.ReadAllBytes(typeof(ParliamentContract).Assembly.Location)), - Name = ParliamentSmartContractAddressNameProvider.Name, - TransactionMethodCallList = GenerateParliamentInitializationCallList() - })).Output; - ParliamentContractStub = GetParliamentContractTester(StarterKeyPair); + { "__seed_owned_symbol", "APP" }, + { "__seed_exp_time", TimestampHelper.GetUtcNow().AddDays(1).Seconds.ToString() } + } + } + })); + + AsyncHelper.RunSync(() => TokenContractStub.Create.SendAsync(new CreateInput + { + Symbol = "SEED-2", + Decimals = 0, + IsBurnable = true, + TokenName = "collection", + TotalSupply = 1, + Issuer = Starter, + Owner = Starter, + LockWhiteList = + { + TokenContractAddress + }, + ExternalInfo = new ExternalInfo + { + Value = + { + { "__seed_owned_symbol", "AUG" }, + { "__seed_exp_time", TimestampHelper.GetUtcNow().AddDays(1).Seconds.ToString() } + } + } + })); + + AsyncHelper.RunSync(() => TokenContractStub.Create.SendAsync(new CreateInput + { + Symbol = "SEED-3", + Decimals = 0, + IsBurnable = true, + TokenName = "collection", + TotalSupply = 1, + Issuer = Starter, + Owner = Starter, + LockWhiteList = + { + TokenContractAddress + }, + ExternalInfo = new ExternalInfo + { + Value = + { + { "__seed_owned_symbol", "JUN" }, + { "__seed_exp_time", TimestampHelper.GetUtcNow().AddDays(1).Seconds.ToString() } + } + } + })); + + AsyncHelper.RunSync(() => TokenContractStub.Issue.SendAsync(new IssueInput + { + Amount = 1, + Memo = "test", + Symbol = "SEED-1", + To = DAppContractAddress + })); + + AsyncHelper.RunSync(() => TokenContractStub.Issue.SendAsync(new IssueInput + { + Amount = 1, + Memo = "test", + Symbol = "SEED-2", + To = Starter + })); + + AsyncHelper.RunSync(() => TokenContractStub.Issue.SendAsync(new IssueInput + { + Amount = 1, + Memo = "test", + Symbol = "SEED-3", + To = Starter + })); ConsensusContractAddress = AsyncHelper.RunSync(() => GetContractZeroTester(StarterKeyPair) .DeploySystemSmartContract.SendAsync( @@ -126,16 +230,6 @@ protected void InitializeContracts() })).Output; AEDPoSContractStub = GetConsensusContractTester(StarterKeyPair); - //deploy DApp contract - DAppContractAddress = AsyncHelper.RunSync(() => GetContractZeroTester(StarterKeyPair) - .DeploySystemSmartContract.SendAsync( - new SystemContractDeploymentInput - { - Category = KernelConstants.CodeCoverageRunnerCategory, - Code = ByteString.CopyFrom(File.ReadAllBytes(typeof(DAppContract).Assembly.Location)), - Name = DappSmartContractAddressNameProvider.Name - })).Output; - DAppContractStub = GetTester(DAppContractAddress, UserKeyPairs.First()); AsyncHelper.RunSync(TransferToContract); AsyncHelper.RunSync(async () => { @@ -198,6 +292,7 @@ private SystemContractDeploymentInput.Types.SystemTransactionMethodCallList TokenName = "elf token", TotalSupply = TokenHolderContractTestConstants.NativeTokenTotalSupply, Issuer = Starter, + Owner = Starter, LockWhiteList = { ProfitContractAddress, @@ -222,6 +317,17 @@ private SystemContractDeploymentInput.Types.SystemTransactionMethodCallList To = Address.FromPublicKey(creatorKeyPair.PublicKey), Memo = "set voters few amount for voting." })); + + tokenContractCallList.Add(nameof(TokenContract.Create), new CreateInput + { + Symbol = "SEED-0", + Decimals = 0, + IsBurnable = true, + TokenName = "collection", + TotalSupply = 1, + Issuer = Starter, + Owner = Starter, + }); return tokenContractCallList; } @@ -286,7 +392,7 @@ protected async Task ApproveWithMinersAsync(Hash proposalId) approveResult.TransactionResult.Error.ShouldBeNullOrEmpty(); } } - + private async Task TransferToContract() { await TokenContractStub.Transfer.SendAsync(new TransferInput diff --git a/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs b/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs index 959bc2cb56..68698e8177 100644 --- a/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs +++ b/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs @@ -504,6 +504,7 @@ await TokenContractStub.Create.SendAsync(new CreateInput TokenName = symbol + " name", TotalSupply = totalSupply, Issuer = Starter, + Owner = Starter, LockWhiteList = { ProfitContractAddress } }); await TokenContractStub.Issue.SendAsync(new IssueInput diff --git a/test/AElf.Contracts.Vote.Tests/AElf.Contracts.Vote.Tests.csproj b/test/AElf.Contracts.Vote.Tests/AElf.Contracts.Vote.Tests.csproj index 896f50b0ab..5ad16fa9b5 100644 --- a/test/AElf.Contracts.Vote.Tests/AElf.Contracts.Vote.Tests.csproj +++ b/test/AElf.Contracts.Vote.Tests/AElf.Contracts.Vote.Tests.csproj @@ -34,6 +34,11 @@ + + false + Contract + PreserveNewest + @@ -81,5 +86,8 @@ Protobuf\Proto\aedpos_contract_impl.proto + + Protobuf\Proto\test_virtual_address_contract.proto + diff --git a/test/AElf.Contracts.Vote.Tests/BVT/BasicTests.cs b/test/AElf.Contracts.Vote.Tests/BVT/BasicTests.cs index a4b112bf30..c7ca327187 100644 --- a/test/AElf.Contracts.Vote.Tests/BVT/BasicTests.cs +++ b/test/AElf.Contracts.Vote.Tests/BVT/BasicTests.cs @@ -2,10 +2,13 @@ using System.Text; using System.Threading.Tasks; using AElf.Contracts.MultiToken; +using AElf.Contracts.TestContract.VirtualAddress; using AElf.CSharp.Core; using AElf.CSharp.Core.Extension; using AElf.Kernel; using AElf.Types; +using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; using Shouldly; using Xunit; @@ -661,4 +664,77 @@ public async Task VoteContract_GetVotingIds_Test() }); voteIds.ActiveVotes.Count.ShouldBe(1); } + + [Fact] + public async Task Vote_VirtualAddress_Success() + { + var registerItem = await RegisterVotingItemAsync(100, 3, true, DefaultSender, 1); + var voteItemId = registerItem.VotingItemId; + var voter = await VirtualAddressContractStub.GetVirtualAddress.CallAsync(new Empty()); + var voteAmount = 100; + await TokenContractStub.Transfer.SendAsync(new TransferInput + { + Symbol = TestTokenSymbol, + Amount = 1000, + To = voter, + Memo = "transfer token to voter" + }); + var beforeVoteBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = TestTokenSymbol, + Owner = voter + }); + var sendResult = await VirtualAddressContractStub.ForwardCall.SendAsync(new ForwardCallInput + { + ContractAddress = VoteContractAddress, + MethodName = "Vote", + VirtualAddress = HashHelper.ComputeFrom("test"), + Args = (new VoteInput + { + VotingItemId = voteItemId, + Option = registerItem.Options[1], + Amount = voteAmount + }).ToByteString() + }); + + sendResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + var voteResult = await VoteContractStub.GetVotingResult.CallAsync(new GetVotingResultInput + { + SnapshotNumber = 1, + VotingItemId = voteItemId + }); + voteResult.Results[registerItem.Options[1]].ShouldBe(voteAmount); + voteResult.VotesAmount.ShouldBe(voteAmount); + voteResult.VotersCount.ShouldBe(1); + var afterVoteBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = TestTokenSymbol, + Owner = voter + }); + beforeVoteBalance.Balance.Sub(afterVoteBalance.Balance).ShouldBe(voteAmount); + var voteItems = await VoteContractStub.GetVotedItems.CallAsync(voter); + voteItems.VotedItemVoteIds.Count.ShouldBe(1); + + var votingIds = await VoteContractStub.GetVotingIds.CallAsync(new GetVotingIdsInput + { + Voter = voter, + VotingItemId = voteItemId + }); + var currentVoteId = votingIds.ActiveVotes.First(); + + sendResult = await VirtualAddressContractStub.ForwardCall.SendAsync(new ForwardCallInput + { + ContractAddress = VoteContractAddress, + MethodName = "Withdraw", + VirtualAddress = HashHelper.ComputeFrom("test"), + Args = (new WithdrawInput + { + VoteId = currentVoteId + }).ToByteString() + }); + sendResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + + var afterWithdrawBalance = GetUserBalance(voter); + afterWithdrawBalance.ShouldBe(beforeVoteBalance.Balance); + } } \ No newline at end of file diff --git a/test/AElf.Contracts.Vote.Tests/VoteContractTestBase.cs b/test/AElf.Contracts.Vote.Tests/VoteContractTestBase.cs index 779aaa37b1..e07dee28f0 100644 --- a/test/AElf.Contracts.Vote.Tests/VoteContractTestBase.cs +++ b/test/AElf.Contracts.Vote.Tests/VoteContractTestBase.cs @@ -4,6 +4,7 @@ using AElf.Contracts.Consensus.AEDPoS; using AElf.Contracts.MultiToken; using AElf.Contracts.Parliament; +using AElf.Contracts.TestContract.VirtualAddress; using AElf.ContractTestKit; using AElf.Cryptography.ECDSA; using AElf.GovernmentSystem; @@ -32,11 +33,13 @@ public class VoteContractTestBase : ContractTestBase protected Address ParliamentContractAddress { get; set; } protected new Address ContractZeroAddress => ContractAddressService.GetZeroSmartContractAddress(); protected Address ConsensusContractAddress { get; set; } + protected Address VirtualAddressContractAddress { get; set; } internal ACS0Container.ACS0Stub BasicContractZeroStub { get; set; } internal TokenContractContainer.TokenContractStub TokenContractStub { get; set; } internal VoteContractImplContainer.VoteContractImplStub VoteContractStub { get; set; } internal ParliamentContractImplContainer.ParliamentContractImplStub ParliamentContractStub { get; set; } internal AEDPoSContractImplContainer.AEDPoSContractImplStub AEDPoSContractStub { get; set; } + internal VirtualAddressContractContainer.VirtualAddressContractStub VirtualAddressContractStub { get; set; } protected void InitializeContracts() { @@ -88,6 +91,14 @@ protected void InitializeContracts() TransactionMethodCallList = GenerateConsensusInitializationCallList() })).Output; AEDPoSContractStub = GetConsensusContractTester(DefaultSenderKeyPair); + + VirtualAddressContractAddress = AsyncHelper.RunSync(async () => + await DeployContractAsync( + KernelConstants.CodeCoverageRunnerCategory, + Codes.Single(kv => kv.Key.EndsWith("VirtualAddress")).Value, + HashHelper.ComputeFrom("AElf.Contracts.TestContract.VirtualAddress"), + DefaultSenderKeyPair)); + VirtualAddressContractStub = GetTester(VirtualAddressContractAddress, DefaultSenderKeyPair); } internal ACS0Container.ACS0Stub GetContractZeroTester(ECKeyPair keyPair) @@ -134,6 +145,7 @@ private SystemContractDeploymentInput.Types.SystemTransactionMethodCallList Gene TokenName = "elf token for testing", TotalSupply = totalSupply, Issuer = DefaultSender, + Owner = DefaultSender, LockWhiteList = { VoteContractAddress diff --git a/test/AElf.Cryptography.Tests/AElf.Cryptography.Tests.csproj b/test/AElf.Cryptography.Tests/AElf.Cryptography.Tests.csproj index b42e0e1bd3..7ef8543522 100644 --- a/test/AElf.Cryptography.Tests/AElf.Cryptography.Tests.csproj +++ b/test/AElf.Cryptography.Tests/AElf.Cryptography.Tests.csproj @@ -9,18 +9,22 @@ runtime; build; native; contentfiles; analyzers - + + - all - runtime; build; native; contentfiles; analyzers - + all + runtime; build; native; contentfiles; analyzers + - all - runtime; build; native; contentfiles; analyzers - + all + runtime; build; native; contentfiles; analyzers + + + + - + \ No newline at end of file diff --git a/test/AElf.Cryptography.Tests/AssemblyInfo.cs b/test/AElf.Cryptography.Tests/AssemblyInfo.cs new file mode 100644 index 0000000000..41a898e37e --- /dev/null +++ b/test/AElf.Cryptography.Tests/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using Xunit; + +[assembly: CollectionBehavior(DisableTestParallelization = true)] \ No newline at end of file diff --git a/test/AElf.Cryptography.Tests/CryptoHelperTests.cs b/test/AElf.Cryptography.Tests/CryptoHelperTests.cs index 0a5578c15f..7ec4e7c59e 100644 --- a/test/AElf.Cryptography.Tests/CryptoHelperTests.cs +++ b/test/AElf.Cryptography.Tests/CryptoHelperTests.cs @@ -1,6 +1,8 @@ using System; using System.Text; using AElf.Cryptography.Exceptions; +using AElf.Types; +using Org.BouncyCastle.Utilities.Encoders; using Shouldly; using Virgil.Crypto; using Xunit; @@ -132,4 +134,16 @@ public void ExceptionTest() Should.Throw(() => throw new SignatureOperationException(message)); Should.Throw(() => throw new InvalidKeyPairException(message, new Exception())); } + + [Fact] + public void VrfTest() + { + var key = CryptoHelper.FromPrivateKey( + ByteArrayHelper.HexStringToByteArray("5945c176c4269dc2aa7daf7078bc63b952832e880da66e5f2237cdf79bc59c5f")); + var alpha = "5cf8151010716e40e5349ad02821da605df22e9ac95450c7e35f04c720fd4db5"; + var alphaBytes = Hash.LoadFromHex(alpha).ToByteArray(); + var pi = CryptoHelper.ECVrfProve(key, alphaBytes); + var beta = CryptoHelper.ECVrfVerify(key.PublicKey, alphaBytes, pi); + beta.ToHex().ShouldBe("43765915e86c205f0c28b4d22e157b8474add7faf890a3aa2a88df756651523f"); + } } \ No newline at end of file diff --git a/test/AElf.Cryptography.Tests/ECVRF/VRFTests.cs b/test/AElf.Cryptography.Tests/ECVRF/VRFTests.cs new file mode 100644 index 0000000000..b9ac5f784c --- /dev/null +++ b/test/AElf.Cryptography.Tests/ECVRF/VRFTests.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using AElf.Cryptography.Core; +using AElf.Cryptography.ECDSA; +using AElf.Cryptography.ECVRF; +using Secp256k1Net; +using Xunit; + +namespace AElf.Cryptography.Tests.ECVRF; + +public class VRFTests +{ + public class TestVector + { + [JsonPropertyName("sk")] + public string Sk { get; set; } + + [JsonPropertyName("pk")] + public string Pk { get; set; } + + [JsonPropertyName("alpha")] + public string Alpha { get; set; } + + [JsonPropertyName("beta")] + public string Beta { get; set; } + + [JsonPropertyName("pi")] + public string Pi { get; set; } + } + + [Fact] + public void HashToCurveTryAndIncrement_Test() + { + var pkSerialized = Convert.FromHexString("0360fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6"); + var alpha = Encoding.ASCII.GetBytes("sample"); + var expectedHashPoint = Convert.FromHexString("027AD7D4C3A454D9ECC905F1E5436A328F2A106A2606EC4B44111CF9DC72A5B9FF"); + + using var curve = new Secp256k1Curve(); + var cfg = new VrfConfig( 0xfe, ECParameters.Curve); + var vrf = new Vrf(cfg); + var output = vrf.HashToCurveTryAndIncrement(curve.DeserializePoint( pkSerialized), alpha); + var outputSerialized = curve.SerializePoint(output, true); + Assert.Equal(outputSerialized, expectedHashPoint); + } + + [Fact] + public void Prove_Test() + { + var path = Path.Combine( Directory.GetCurrentDirectory(), "secp256_k1_sha256_tai.json"); + var text = File.ReadAllText(path); + var vectors = JsonSerializer.Deserialize>(text); + using var secp256k1 = new Secp256k1(); + foreach (var vector in vectors) + { + var sk = AddLeadingZeros(Convert.FromHexString(vector.Sk)); + var kp = CryptoHelper.FromPrivateKey(sk); + var alpha = Convert.FromHexString(vector.Alpha); + var expectedPi = Convert.FromHexString(vector.Pi); + var cfg = new VrfConfig( 0xfe, ECParameters.Curve); + var vrf = new Vrf(cfg); + var pi = vrf.Prove(kp, alpha); + Assert.Equal(expectedPi, pi); + } + } + + [Fact] + public void Verify_Test() + { + var c = ECParameters.Curve; + var path = Path.Combine( Directory.GetCurrentDirectory(), "secp256_k1_sha256_tai.json"); + var text = File.ReadAllText(path); + var vectors = JsonSerializer.Deserialize>(text); + using var secp256k1 = new Secp256k1(); + foreach (var vector in vectors) + { + var pk =Convert.FromHexString(vector.Pk); + var alpha = Convert.FromHexString(vector.Alpha); + var pi = Convert.FromHexString(vector.Pi); + var expectedBeta = Convert.FromHexString(vector.Beta); + var cfg = new VrfConfig( 0xfe, ECParameters.Curve); + var vrf = new Vrf(cfg); + var beta = vrf.Verify(pk, alpha, pi); + Assert.Equal(expectedBeta, beta); + } + } + + [Fact] + public void Verify_BadMessageFailsVerification_Test() + { + var path = Path.Combine( Directory.GetCurrentDirectory(), "secp256_k1_sha256_tai.json"); + var text = File.ReadAllText(path); + var vectors = JsonSerializer.Deserialize>(text); + using var secp256k1 = new Secp256k1(); + foreach (var vector in vectors) + { + var pk =Convert.FromHexString(vector.Pk); + var alpha = Encoding.ASCII.GetBytes("this is a wrong message"); + var pi = Convert.FromHexString(vector.Pi); + var cfg = new VrfConfig( 0xfe, ECParameters.Curve); + var vrf = new Vrf(cfg); + Assert.Throws(() => vrf.Verify(pk, alpha, pi)); + } + } + private byte[] AddLeadingZeros(byte[] sk) + { + if (sk.Length < 32) + { + var output = new byte[32]; + var zeroBytesLength = 32 - sk.Length; + for (int i = zeroBytesLength - 1; i >= 0; i--) + { + output[i] = 0; + } + Buffer.BlockCopy(sk, 0, output, zeroBytesLength, sk.Length); + return output; + } + + return sk; + + } +} \ No newline at end of file diff --git a/test/AElf.Cryptography.Tests/secp256_k1_sha256_tai.json b/test/AElf.Cryptography.Tests/secp256_k1_sha256_tai.json new file mode 100644 index 0000000000..40900f6b17 --- /dev/null +++ b/test/AElf.Cryptography.Tests/secp256_k1_sha256_tai.json @@ -0,0 +1,324 @@ +[ + { + "beta": "612065e309e937ef46c2ef04d5886b9c6efd2991ac484ec64a9b014366fc5d81", + "alpha": "73616d706c65", + "pi": "031f4dbca087a1972d04a07a779b7df1caa99e0f5db2aa21f3aecc4f9e10e85d08748c9fbe6b95d17359707bfb8e8ab0c93ba0c515333adcb8b64f372c535e115ccf66ebf5abe6fadb01b5efb37c0a0ec9", + "sk": "c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721", + "pk": "032c8c31fc9f990c6b55e3865a184a4ce50e09481f2eaeb3e60ec1cea13a6ae645" + }, + { + "beta": "00acd42d48046e13552f54919286c2085aec6fb874854d036f66ad572c99e7ab", + "alpha": "73616d706c65", + "pi": "029a2df6ca1d5f734945fb6847669f839eb9ecf127fa8314e5a6a5c4695c3f4d159009b3741cdec6b0d7c70e3aae6b82aeb1aad555499bd6ce10b35fa230079e6fa752e8d4755ffd285aef5133dad7a64b", + "sk": "01", + "pk": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + }, + { + "beta": "c355718640883112731fce0b5dd97c34492d226280654dcf0ada1d6b32e3384b", + "alpha": "73616d706c65", + "pi": "0205c5a6ed80f7ffbf9f47583e873717e86c8405349266745a0504ea7ca68876ce6afe0e3cccfc7bba83a6d16771d80e26a46ad25be631869d6f60a34c12a19b868815182657288f57afb91166ceed3cc5", + "sk": "02", + "pk": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5" + }, + { + "beta": "09a93f6e8a6037db146d862eec33f56c8053bb4fda309f8ecbcbe04592aabd37", + "alpha": "73616d706c65", + "pi": "02a14b92076becc501b9ac761c18cacd792e0b30ad2b6907e1273dbe3762a9d29cf97fe3a904d2123ef98030a929ea91b40f1d5f78d9eb09c85ee2caa962b17de41abd4cf6be036e90c9826ae4e6e3673a", + "sk": "03", + "pk": "02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9" + }, + { + "beta": "5a0bce08f650d5ef80b0254ee814360e1f2c944a6c2b15e3171374f362cd92b4", + "alpha": "73616d706c65", + "pi": "03a3ca50b6f1873b8ca55348d0c62b0a2fd774aa9b96d1061c0917d6f9da5fe560dcddbda349d2db15751c965baf88ddb67075b5a441400c5ef85d9dffd6a16833993af6f05901c2421210d72d114c4ba0", + "sk": "04", + "pk": "02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13" + }, + { + "beta": "e06dc093da223b40225633628c94a0bf909a3bb19a2f838932b4627f740354a1", + "alpha": "73616d706c65", + "pi": "030b4806f7f7398ba03951990740243d3f84a0815d85e2be439ee42bd8f249bd44e56800354296f9c05d30e06966009baf98c963028070217f28c0a298f9ecd56726bbc7c4faf1c62691972bfbc9fbd684", + "sk": "05", + "pk": "022f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4" + }, + { + "beta": "811a4fe231758d65b9b20d436a19256765776c812966032b16ce2b21e5c69be7", + "alpha": "73616d706c65", + "pi": "02f0fdc752a0c63cae4d33f65af4167a7a8b04711d840230df0d211ee3d6e2a2ad66625f4d9db7798ab664bb2da77736db6c9b7828cd8310a2e5f677224e1676afbb731cf92018a49c0452d1080aac5f3a", + "sk": "06", + "pk": "03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556" + }, + { + "beta": "0b772c54b2125194bd26b7e3ce2c4606043f65d99d3c65aef6e3b61dffd20aae", + "alpha": "73616d706c65", + "pi": "036e041791df3880003115548bf1c491700133a23fb6c1edbef6a23f7dd67c05d5e227f496df589bdd68ffc28fb00246681abb9399a4207fbf42c2a64fa47615b4a64dc25cf0f6bc368353bfb25f63c95b", + "sk": "07", + "pk": "025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc" + }, + { + "beta": "8f7729e4288e7630b358a6cef560479f0e441926ae0fa6a3a96f17b63b3d1fb8", + "alpha": "73616d706c65", + "pi": "02b5a04c6340c0d09b26da8f75c7713ed871e9f673bb87c00ee0e76e8045faad9ece28cfdbc41eab04205963c3252b7229d94793491e4a7f48e09c2d85b3ee2a1131a27b670b07c016dc00e4d8f3daa242", + "sk": "08", + "pk": "022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01" + }, + { + "beta": "ce465b7e061bae415e10d8aafd32d92e2f499d959632fe5983cb0b2d4ee7b3b5", + "alpha": "73616d706c65", + "pi": "02cf1639991c8eb219993f1b9921a413e2ebc72d7b533ab952c92188b9b4ee2bd8cd5417a75c80d3022a3719297b2699b958d27e5e9fe03c8fc96766234fa497bee8287a76f6db22ff482cec9505e51a5f", + "sk": "09", + "pk": "03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe" + }, + { + "beta": "941b5f76d95772045e824e30d85106a4a3b5920b901e178d3f62ec8e473ccf69", + "alpha": "73616d706c65", + "pi": "034e371e26303b9deb6f650f8faee60a2673b2bfef5b5a9af066aca5e7c12695becd27149218141ceb7f7e58da293679388c3b7b5109ec360a1f50decb1fa89c11b19476f5b2786ecba75f3642d942ba31", + "sk": "0a", + "pk": "03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7" + }, + { + "beta": "79403a19944c3516d102cfa42cd5dd3f16c5c9bd457bf0659e2305374af3dccd", + "alpha": "73616d706c65", + "pi": "0384e3011d9e8235de77c211c0e57b0ed4bbffbe4b1d1a946278d13943be6c4280206842bfc58d564262a3214cfab3098d2887923eb659ff543aaa9c48d821acf1198bde5d824d27e9a9a6c2f84c6bdfdb", + "sk": "0b", + "pk": "03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb" + }, + { + "beta": "746ca26b7c90df37d309ecce42757e6bf9423ad31211a02e8d9d86aea72f2f3a", + "alpha": "73616d706c65", + "pi": "03ad49eb72e2c7789d851c7bf2a3137cee17ab304ae7acb7b22739c5aae48eb339199381d85ae4654b2f5488499b4d3a13d6055ca740de70bb6d9b2c0cb962d873e13cd1d1d1c0c4eef9f9809f88e36fc0", + "sk": "0c", + "pk": "03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a" + }, + { + "beta": "51096a3de69492e852477271a77a5b95de5a0cc2fc43128177b9a85e3184318d", + "alpha": "73616d706c65", + "pi": "038be9f8885740d6a336f0ec8249ec010f99fb4f0e42f09804c6f98f0be907327419f8249ebc3fb6fb372402893c55422987d9d36f6dfbdc7cfa88fb75dca13b095f412a1d82b358b1d17406991af576e4", + "sk": "0d", + "pk": "03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8" + }, + { + "beta": "b3a048bc1682c3d5ba4d79a951b38f6d9729a328afb38fdd22c5f17247eeb642", + "alpha": "73616d706c65", + "pi": "0354a5641699f62565bec88e75ed465c052f655048a2de85ae39f32e968a80faeb6e21ac4919dc0710e1f1a76184988926a2b354c52cf1ca8529157aa5574c385b86def3a76a7cb9e0d0a11227bc651dac", + "sk": "0e", + "pk": "03499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4" + }, + { + "beta": "eb5c23bc8773fa4c83ef3bf88bf63aceffe6b042e220d8826db222dc55f915fe", + "alpha": "73616d706c65", + "pi": "0398f53f2ed4d687e6a65eaf7ce4d63e99e2db78a12302c782ffe6012737eb2d10257f7a86288479153dafa74c14092fd5acc60c953f225840ee6c92ec67106a979ead5f8c12142373b119190a012ac780", + "sk": "0f", + "pk": "02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e" + }, + { + "beta": "e96f1bab42d66b32d2fb8ea1e4655808881cd9f208279a0757ef990575e7adc0", + "alpha": "73616d706c65", + "pi": "024ca2c0b270a6c632cade38ff097d66de362c54064e847fd96bd4067b71028db4ead3e112b4cc78b826165d6fde084924f58ae74f69c4542d68cdd5a33e03d5bd2e7fc5f5fda4c9e3d157fbb213fddd3d", + "sk": "10", + "pk": "03e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a" + }, + { + "beta": "dbfeac68bfc25d1fd3f9d0bfe093edfef9e90c1b4d2940ac961c1d08ba66deb4", + "alpha": "73616d706c65", + "pi": "028594a13f389fbb3618bb5db54059ea673f087d7193ed8513d1abc7f3a5228d0e99a976d08666bdb46b287ec41945de29cce12c2dac0ba226ef3ef383a0fba8e138b415816c5243af66ef5a51244d9616", + "sk": "11", + "pk": "03defdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34" + }, + { + "beta": "87a0ff6de5a282746082ed187abc29f534c1ee4f641e604c7d241fea5155b2a2", + "alpha": "73616d706c65", + "pi": "03e9593d98552a6e3c3f45a8a566b4d2de9bd5ff1837b4ca91655dc6e91d59fb5b335fd59c15668b371093ee81df275c876761ebcbd60fa8127c7e601f99ce340a3ae8c00864c0e38ebe749e566c151240", + "sk": "12", + "pk": "025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc" + }, + { + "beta": "32306c22ad369a6e0fd638f503c0dbf674cd9d66b5da0a8c83c1d78157814f90", + "alpha": "73616d706c65", + "pi": "02833a4a802ec7f90b91dc59cfc8138669d2e6ec3f933c4fc7e44389a5e02c6032816f05729899c003ae77025e22c703809f8ee1b35d04d250800c7e90533dc4f29b3667065fc2cae0cc3cd356962ac62a", + "sk": "13", + "pk": "022b4ea0a797a443d293ef5cff444f4979f06acfebd7e86d277475656138385b6c" + }, + { + "beta": "4e853d2b79f1d2dd8e23be493efa8c685afc30198070aa5acbc53fddcace9aa9", + "alpha": "73616d706c65", + "pi": "022d2bb6961468e5a3aadae02157e584a4d45f58121185cafa11b93fae6eb4a4604d03e431646fb1ee06c04e562cbd0e7d8c4fc01bc8dc0ca0f7f5d4e138be2963f6ab25843a12dd6c5e34cfda1ecabf04", + "sk": "14", + "pk": "024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97" + }, + { + "beta": "642a4819a8e8e5739cc0f4bd5221d641774f2d33b75007c4de61f4f2089df92c", + "alpha": "73616d706c65", + "pi": "02570056bade9dc5204721d0ecd39906a72468d027dfa1a896b8f6a88d50fc5526f7afb112bb95a2e13f326cbf1c8de77b7596195575b4dd712edbfd96acafdcbc0fa1dfae4041bb64d8e25b276426ea5a", + "sk": "018ebbb95eed0e13", + "pk": "02a90cc3d3f3e146daadfc74ca1372207cb4b725ae708cef713a98edd73d99ef29" + }, + { + "beta": "b042e27b4d0e1f1a1c8ec7d4c2c0b2ae988d4cbc526d1ffb11b0087bea97a630", + "alpha": "73616d706c65", + "pi": "02b87402d5f26e06c14a3136a6c868316d2a2d413d5ef2ef91cae3625d57e87ea326b2bb5f67e4fe3f5c65df958d1ecd7a9bebea344b1facd08fbc7512701bcababbc39c410d9d1c0980e688feada47468", + "sk": "159d893d4cdd747246cdca43590e13", + "pk": "03e5a2636bcfd412ebf36ec45b19bfb68a1bc5f8632e678132b885f7df99c5e9b3" + }, + { + "beta": "3a36fc2ff539e516897c53d61951209dcac171500ed79692434a28e0cb9c3272", + "alpha": "73616d706c65", + "pi": "02e111fc96dd022c02f45df180c3707d5a48a5eb669aad2f7b45345b5f95a38f68b82b8cfa5dee906023fe4c2a15723636e29c8fc8d65ade1620b0d6453331f0237f632e89f0352fd25662d5630b3a6034", + "sk": "3fffffffffffffffffffffffffffffffaeabb739abd2280eeff497a3340d9050", + "pk": "03a6b594b38fb3e77c6edf78161fade2041f4e09fd8497db776e546c41567feb3c" + }, + { + "beta": "c6e3b662984301fc84c5eb2f5c0f435aee2975a731e6707bb9e50113e4bc2809", + "alpha": "73616d706c65", + "pi": "023a435fc5fab74b0b33eeb7c62447efc323e6e33a19657e7a0a473451b885fe841f28b91f43834ab659f26ea94d9dbca325192c45589afc1415e508b72247c64e385d3f9aa13c2e571d252335b8f63a3e", + "sk": "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", + "pk": "0300000000000000000000003b78ce563f89a0ed9414f5aa28ad0d96d6795f9c63" + }, + { + "beta": "a25353782f363555d90c822a347151272d364103aa49513105bcc247287ff6a9", + "alpha": "73616d706c65", + "pi": "02f769ca0cb1a96046265c19d5ef44deade0eb42a6aab094c7fde5bcdae09455db2ba3ffa7e72d01ead1fedc0162507c5d8ccbde61e09cd533b807404b75a4acb8573881f86abbf41bc0877acb3362604f", + "sk": "bfffffffffffffffffffffffffffffff0c0325ad0376782ccfddc6e99c28b0f0", + "pk": "02e24ce4beee294aa6350faa67512b99d388693ae4e7f53d19882a6ea169fc1ce1" + }, + { + "beta": "9731f862d34587fb91521d785a30ff57188c57efd4b55239199ae4ed31bcebf8", + "alpha": "73616d706c65", + "pi": "02d196a5eb787d5d8a33247607d78d5a48164ac4d899dba33cb3a5f68032124ba1b2221b96ae997d450ddd8e5863a0cc7cf1b3f50431aa6031b6807396edfab5d1f5812c7e8d40f157028afb7aa51a41d5", + "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036412d", + "pk": "034ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97" + }, + { + "beta": "54c8f3e98a8a8e5b77d327cdb7a71e5959996b5619972a8b472d7a3799a79387", + "alpha": "73616d706c65", + "pi": "0391aeaecb887e9d6f0c0f64d20f28acbe687bffabaaea0ff5237f236693eb5b56c88099425415cd9d242c70249f7abb8958a327ad341b4fca735f8ba61b57c9735252b72afab7e0cb6e49c9436366e800", + "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036412e", + "pk": "032b4ea0a797a443d293ef5cff444f4979f06acfebd7e86d277475656138385b6c" + }, + { + "beta": "f9fba571cb27776c07d2bc42d670952e1965357942eca3edb5f80e28bc9aaed0", + "alpha": "73616d706c65", + "pi": "03b9ffede3d97b9753f8f31cd5d56442c525a5bccc2de1fc547886ee08bca9b4f3c1d44da0826ddfd763801c42875d41deeae99422c0e9e6a97e07e2689f58289bffe499069785dcffa4ff93a5b1e15856", + "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036412f", + "pk": "035601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc" + }, + { + "beta": "c66931ad96cb4e1a44202fcd7882089b1cf77b07c426d292e0e15deca5c1b027", + "alpha": "73616d706c65", + "pi": "03ca01e6d80f99bd12c5c00142a9eb0c0e029f999a1e945a70110c944d5d5981c9814fb051e88c36f0c14d9acdfc3040b37fcc77fa1bfc23466730a108849e0a08f5e9c79331b04803e568ea4b553ce3f1", + "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364130", + "pk": "02defdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34" + }, + { + "beta": "4b00d6d00864c7972105755b538d5f62a3585b6e8e7061fd107317fa1004efc0", + "alpha": "73616d706c65", + "pi": "03df963611501cf382e2730131618377ab38486f483db1eab7feb6ade0e1b0141bd3d291b7e45a1b94cabbafa5fca3fb7ba36b158bbcdb2292383689a6231e201a3f78f9f40757e99f80e8032adbc8d4ce", + "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364131", + "pk": "02e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a" + }, + { + "beta": "e1e9b8491278b6faf79d433cee7d9b01256f18b3d63601f6231332ce3751411c", + "alpha": "73616d706c65", + "pi": "03ee58341c2222f7671318eff4bf2bd5588221d37d133a8aafcff5162d56af906581ce8cc5d45d546b8cf2c6d22026b934688e9a68555d4386d75f9b9e554b58b886ad91285872a2a6f137576b6bf9513f", + "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364132", + "pk": "03d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e" + }, + { + "beta": "72c8e10530d3f0c6e452f8f20d911908eb01887c62bbae0b1eb35cb1f36b7985", + "alpha": "73616d706c65", + "pi": "03ae19c4bac9d64009b7dabf9095c3ee3c848249269d41d5ee492683cef4a0b8464fa567b84a2bfe1c7359696522d01e083defdf2c4fe5aaad7bf67c93aab74d23069d05419c59c5cd5daed14e63bdc26f", + "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364133", + "pk": "02499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4" + }, + { + "beta": "11fee8e23d484d9aa8ed151f8452be11e70cfad8a44f707c00b04a11270c3d7a", + "alpha": "73616d706c65", + "pi": "0261cb37ca1f9c0ee11e41aadf4637fdddccb3f70f8ff1903727fbc2bd220720e737048df6aa7ddec95dc6a5f93f6808b5bbf2983c1733ce7b686dddc457001dbbae277d251d61f69e1af22d69b60c975e", + "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364134", + "pk": "02f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8" + }, + { + "beta": "dfa3543db662a08aac90bfd7dd9b39b77dacf16dda462fe5eaa26b0f595fc0f5", + "alpha": "73616d706c65", + "pi": "0376661cbff92aae582298a7348f4d8f7834e2d8f6707c9706f52e65aacf968d80b24c972d16acca689cf66a1d100c26d2b141b1c8b9835e6710db5126284b9540f43cf31394f9b3e0a87449df7ef6aee6", + "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364135", + "pk": "02d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a" + }, + { + "beta": "caae0b8dd19e20fd52f43b2fd416228b46ad625aa68ef6424ff388fb4727e0dc", + "alpha": "73616d706c65", + "pi": "0204fa576f63771c34e6cdb98f59997584528d109c7592ab867374d9b91051a4d1e875685ea35673d901ff06f18d7e89bc098be8762abf7688dba945d09d9b71348624b40c4b903de2bd3cc44abb2fafc7", + "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364136", + "pk": "02774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb" + }, + { + "beta": "97e0785b78305d909af2a255e1b26d4faf5879d4e7640bceac71e56b3851bec0", + "alpha": "73616d706c65", + "pi": "024d4ff3ad7689b905b5c4be9de0bd8d7960e30f145903fe715af943852229f269122fadc5f835ee029d306ad7d90f5c6011ad67d24a327cccf3f39018e34df7a1544d6748755fa07ce2013729816e2330", + "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364137", + "pk": "02a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7" + }, + { + "beta": "e0a3519f3dd1597039b5617d5b09c8ed5c723d1589010c1d6284dd8fb9d5ea7b", + "alpha": "73616d706c65", + "pi": "022eb72eccc7228307eaf7946a28feac02de8223534800cc71d7d1195fb0a7c88630d9be168212ecee12897644c456c22eaa9be58c9c8bb92a86a73e787f44f55a57203ddd9d80bf4e1ad4ac21c03a5f2c", + "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364138", + "pk": "02acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe" + }, + { + "beta": "ba9235a6d8c3a2efa2b6cc2d8f23d3b9169ae0a0363db2192465aefbff07ed09", + "alpha": "73616d706c65", + "pi": "038fbe2d674ae973b17ee720413e94ee0387d1f794766ec9649d97ccbf6afaa22ce90783ffc7eb0c082db401cb81203ced6223bf24f66cd2c19351f4e18c9fd7a2884d82870ea357bd5bbb4ce9e1e4e840", + "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364139", + "pk": "032f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01" + }, + { + "beta": "9c9dc6b4b61b59950c15c35fb665cc94879b0297fac4edf2803de529b00f8c0c", + "alpha": "73616d706c65", + "pi": "0235731391a2ed6ff06cdb279b71ae0151d4f43041cfa8c27d958ab95d08b1bcae807694c56c2ec4ebc855253e3a66e798ea6701ae6861a2ad67a8c2a3ce14c3e8af2f3936fabd5a13dde062afd040e50a", + "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413a", + "pk": "035cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc" + }, + { + "beta": "110f1fa93881cf624c22b72f1b79e6138a052a462ff10d7aa56f501835a8f6b8", + "alpha": "73616d706c65", + "pi": "03913bdfa315ab0963b03c34ebe265751e8b5904837bbda75629423b485924fee45b397e9d697239fdcfd435f1b21082d94f1917c792300c4ada68c0ac4e9da7cf2db99c5cbf2775ac4161ea2ed3c589a6", + "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413b", + "pk": "02fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556" + }, + { + "beta": "a43ebaef2262310b95e140287c861c53edefc13c37696c3f89234d1a45eccf17", + "alpha": "73616d706c65", + "pi": "024dbc319514312b5544e6b587a978dfccbdc862d7fc33c5dda706efb569613df99fa8b04b237169cb92397564f92bd1e45e041817e7a3368fa1f47fcca1e9bd01a6915e645b2d411815b2b95c9efef590", + "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413c", + "pk": "032f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4" + }, + { + "beta": "c8952a9439d26d3e761399de1fd734a2338c15893ece5a3efc72e25c9f007da8", + "alpha": "73616d706c65", + "pi": "03e30118c907034baf1456063bf7b423972e13e1743bf8dbb2e00fd8ba4a8c367a299bc3859123464d87fd4a508e5a1321f6bde3f00104b2c8af1769781dfb02e749946f4e17f6e429be0f4e4e3085e320", + "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413d", + "pk": "03e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13" + }, + { + "beta": "25daded1cb7561c8e0013315a6f6d9dd1611d95c92caf5f920bc437ae0180a55", + "alpha": "73616d706c65", + "pi": "02ed1bb54a9092c8fd50ae8cea3322e127600a0e32840d9bc4664cfab08b1c6ba3a36ad7913367088f6e6cdbc91a061cfbd0fab0093344414aa16e43dcc5394c7a06cb46afb049eeffc2e99d16992bc228", + "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e", + "pk": "03f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9" + }, + { + "beta": "3bcee6576d82d011563480c8fcc5751ba6aea58313dbba4cb278d2f74eaee3ea", + "alpha": "73616d706c65", + "pi": "03359425334b14173856433b4e695f1d19c7c0cb4eb9b5c72b0b00afe170ce7fd738334a976a8be4582b05a480cdecf8a4f4dd9d0694ae1dcb384429a1c99082bdb2845e2c3010054071489f41fc4b65a5", + "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413f", + "pk": "03c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5" + }, + { + "beta": "8efc7ce3fa0ee91c2e7d45ead94883776e37bdb3af67f386e7ec500e76e066dd", + "alpha": "73616d706c65", + "pi": "03cc27d840191d06dfca94d9346cc5b85830dcf9c9e7e4a41cc857d841bd48186c6c5d463591b9632297b3aab781d23263fd9cfc41fbb6affa02840bb903f51b494dc492087ba6fe04acf7c5b54ec0de24", + "sk": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "pk": "0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + } +] \ No newline at end of file diff --git a/test/AElf.Kernel.Consensus.AEDPoS.Tests/AEDPoSTestAElfModule.cs b/test/AElf.Kernel.Consensus.AEDPoS.Tests/AEDPoSTestAElfModule.cs index 685b78a9ab..fd63a97d83 100644 --- a/test/AElf.Kernel.Consensus.AEDPoS.Tests/AEDPoSTestAElfModule.cs +++ b/test/AElf.Kernel.Consensus.AEDPoS.Tests/AEDPoSTestAElfModule.cs @@ -170,6 +170,17 @@ public override void ConfigureServices(ServiceConfigurationContext context) } }.ToByteArray()) })); + + mockService.Setup(m => + m.ExecuteAsync(It.IsAny(), + It.Is(tx => + tx.MethodName == "GetRandomHash"), + It.IsAny())) + .Returns(Task.FromResult(new TransactionTrace + { + ExecutionStatus = ExecutionStatus.Executed, + ReturnValue = Hash.Empty.ToByteString() + })); return mockService.Object; }); diff --git a/test/AElf.Kernel.Consensus.AEDPoS.Tests/Application/AEDPoSTriggerInformationProviderTests.cs b/test/AElf.Kernel.Consensus.AEDPoS.Tests/Application/AEDPoSTriggerInformationProviderTests.cs index e17515599c..4e4172f345 100644 --- a/test/AElf.Kernel.Consensus.AEDPoS.Tests/Application/AEDPoSTriggerInformationProviderTests.cs +++ b/test/AElf.Kernel.Consensus.AEDPoS.Tests/Application/AEDPoSTriggerInformationProviderTests.cs @@ -4,6 +4,7 @@ using AElf.Kernel.Consensus.AEDPoS.Application; using AElf.Kernel.Consensus.Application; using AElf.Standards.ACS4; +using AElf.Types; using Google.Protobuf; using Google.Protobuf.WellKnownTypes; using Shouldly; @@ -43,7 +44,7 @@ public Task GetTriggerInformationForBlockHeaderExtraData_ConsensusCommand_Test() [Fact] public async Task GetCurrentMinerList_Test() { - var result = await _aedpoSInformationProvider.GetCurrentMinerList(new ChainContext()); + var result = await _aedpoSInformationProvider.GetCurrentMinerListAsync(new ChainContext()); result.Count().ShouldBe(3); } @@ -60,7 +61,7 @@ public void GetTriggerInformationForBlockHeaderExtraData_CommandIsNull_Test() public void GetTriggerInformationForConsensusTransactions_CommandIsNull_Test() { var result = - _triggerInformationProvider.GetTriggerInformationForConsensusTransactions(null); + _triggerInformationProvider.GetTriggerInformationForConsensusTransactions(new ChainContext(), null); var triggerInformation = AElfConsensusTriggerInformation.Parser.ParseFrom(result.Value); triggerInformation.Behaviour.ShouldBe(AElfConsensusBehaviour.UpdateValue); } diff --git a/test/AElf.Kernel.Consensus.Tests/ConsensusTestAElfModule.cs b/test/AElf.Kernel.Consensus.Tests/ConsensusTestAElfModule.cs index b31b834a82..8f05ef3b78 100644 --- a/test/AElf.Kernel.Consensus.Tests/ConsensusTestAElfModule.cs +++ b/test/AElf.Kernel.Consensus.Tests/ConsensusTestAElfModule.cs @@ -38,7 +38,7 @@ public override void ConfigureServices(ServiceConfigurationContext context) .Returns(new BytesValue()); mockService.Setup(m => m.GetTriggerInformationForBlockHeaderExtraData(It.IsAny())) .Returns(new BytesValue()); - mockService.Setup(m => m.GetTriggerInformationForConsensusTransactions(It.IsAny())) + mockService.Setup(m => m.GetTriggerInformationForConsensusTransactions(It.IsAny(), It.IsAny())) .Returns(new BytesValue()); return mockService.Object; diff --git a/test/AElf.Kernel.Core.Tests/Account/Application/AccountServiceTests.cs b/test/AElf.Kernel.Core.Tests/Account/Application/AccountServiceTests.cs index 9d9a44ed36..74bfda07dd 100644 --- a/test/AElf.Kernel.Core.Tests/Account/Application/AccountServiceTests.cs +++ b/test/AElf.Kernel.Core.Tests/Account/Application/AccountServiceTests.cs @@ -59,4 +59,15 @@ public async Task EncryptAndDecryptMessage_Test() decryptMessage.ShouldBe(plainMessage); } + + [Fact] + public async Task Vrf_Test() + { + var alpha = "5cf8151010716e40e5349ad02821da605df22e9ac95450c7e35f04c720fd4db5"; + var alphaBytes = Hash.LoadFromHex(alpha).ToByteArray(); + var pi = await _accountService.ECVrfProveAsync(alphaBytes); + var pubkey = await _accountService.GetPublicKeyAsync(); + var beta = CryptoHelper.ECVrfVerify(pubkey, alphaBytes, pi); + beta.ShouldNotBeEmpty(); + } } \ No newline at end of file diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForCallThreshold.Tests/AElf.Kernel.SmartContract.ExecutionPluginForCallThreshold.Tests.csproj b/test/AElf.Kernel.SmartContract.ExecutionPluginForCallThreshold.Tests/AElf.Kernel.SmartContract.ExecutionPluginForCallThreshold.Tests.csproj index 2577e808ad..572810cff3 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForCallThreshold.Tests/AElf.Kernel.SmartContract.ExecutionPluginForCallThreshold.Tests.csproj +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForCallThreshold.Tests/AElf.Kernel.SmartContract.ExecutionPluginForCallThreshold.Tests.csproj @@ -47,6 +47,16 @@ Contract PreserveNewest + + false + Contract + PreserveNewest + + + false + Contract + PreserveNewest + @@ -72,6 +82,15 @@ Protobuf\Proto\acs5_plugin_test_contract.proto + + Protobuf\Proto\parliament_contract.proto + + + Protobuf\Proto\parliament_contract_impl.proto + + + Protobuf\Proto\acs3.proto + diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForCallThreshold.Tests/ExecutionPluginForCallThresholdTestBase.cs b/test/AElf.Kernel.SmartContract.ExecutionPluginForCallThreshold.Tests/ExecutionPluginForCallThresholdTestBase.cs index 62a7a04a85..5dce8cc994 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForCallThreshold.Tests/ExecutionPluginForCallThresholdTestBase.cs +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForCallThreshold.Tests/ExecutionPluginForCallThresholdTestBase.cs @@ -1,14 +1,17 @@ using System.Linq; using System.Threading.Tasks; using AElf.Contracts.MultiToken; +using AElf.Contracts.Parliament; using AElf.Contracts.TokenConverter; using AElf.ContractTestKit; using AElf.Cryptography.ECDSA; using AElf.EconomicSystem; +using AElf.Kernel.Proposal; using AElf.Kernel.SmartContract.ExecutionPluginForCallThreshold.Tests.TestContract; using AElf.Kernel.Token; using AElf.Types; using Shouldly; +using InitializeInput = AElf.Contracts.TokenConverter.InitializeInput; namespace AElf.Kernel.SmartContract.ExecutionPluginForCallThreshold.Tests; @@ -39,6 +42,8 @@ public class ExecutionPluginForCallThresholdTestBase : ContractTestBase Accounts[0].KeyPair; internal ECKeyPair OtherTester => Accounts[1].KeyPair; @@ -51,6 +56,7 @@ public class ExecutionPluginForCallThresholdTestBase : ContractTestBase kv.Key.Contains("TestContract")).Value; + var code = Codes.Single(kv => kv.Key.Contains("ExecutionPluginForCallThreshold.Tests.TestContract")).Value; TestContractAddress = await DeployContractAsync(category, code, HashHelper.ComputeFrom("TestContract"), DefaultSenderKeyPair); DefaultTester = GetTester(TestContractAddress, DefaultSenderKeyPair); } + // Parliament + { + var code = Codes.Single(kv => kv.Key.Contains("MockParliament")).Value; + ParliamentAddress = await DeploySystemSmartContract(category, code, + ParliamentSmartContractAddressNameProvider.Name, DefaultSenderKeyPair); + ParliamentContractStub = + GetTester(ParliamentAddress, + DefaultSenderKeyPair); + } + } + + private async Task InitializedParliament() + { + await ParliamentContractStub.Initialize.SendAsync(new AElf.Contracts.Parliament.InitializeInput + { + ProposerAuthorityRequired = false, + PrivilegedProposer = DefaultSender + }); } private async Task InitializeTokenAsync() @@ -98,7 +122,8 @@ private async Task InitializeTokenAsync() IsBurnable = true, TokenName = "elf token", TotalSupply = 1000_00000000L, - Issuer = DefaultSender + Issuer = DefaultSender, + Owner = DefaultSender }); createResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); @@ -122,7 +147,8 @@ private async Task InitializeTokenAsync() IsBurnable = true, TokenName = "WRITE token", TotalSupply = 1000_0000L, - Issuer = DefaultSender + Issuer = DefaultSender, + Owner = DefaultSender }); createResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests.csproj b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests.csproj index 17473c3736..8a46bd22dc 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests.csproj +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests.csproj @@ -60,11 +60,6 @@ Contract PreserveNewest - - false - Contract - PreserveNewest - false Contract @@ -85,6 +80,11 @@ Contract PreserveNewest + + false + Contract + PreserveNewest + @@ -107,6 +107,9 @@ Protobuf\Proto\token_contract.proto + + Protobuf\Proto\token_contract_impl.proto + Protobuf\Proto\acs1_plugin_test_contract.proto diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutePluginTransactionDirectlyTest.cs b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutePluginTransactionDirectlyTest.cs index a72b124533..49e50a0205 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutePluginTransactionDirectlyTest.cs +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutePluginTransactionDirectlyTest.cs @@ -1,22 +1,17 @@ -using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.InteropServices; using System.Threading.Tasks; -using AElf.Standards.ACS1; -using AElf.Standards.ACS3; using AElf.Contracts.MultiToken; using AElf.CSharp.Core; -using AElf.CSharp.Core.Extension; +using AElf.Standards.ACS1; using AElf.Types; -using Google.Protobuf; using Google.Protobuf.WellKnownTypes; using Shouldly; using Xunit; namespace AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests; -public class ExecutePluginTransactionDirectlyTest : ExecutePluginTransactionDirectlyForMethodFeeTestBase +public partial class ExecutePluginTransactionDirectlyTest : ExecutePluginTransactionDirectlyForMethodFeeTestBase { [Fact] public async Task ChargeTransactionFees_Invalid_Input_Test() @@ -43,6 +38,20 @@ public async Task ChargeTransactionFees_Without_Primary_Token_Test() // input With Primary Token await SetPrimaryTokenSymbolAsync(); var beforeChargeBalance = await GetBalanceAsync(address, nativeTokenSymbol); + + await TokenContractImplStub.SetMethodFee.SendAsync(new MethodFees + { + MethodName = methodName, + Fees = + { + new MethodFee + { + Symbol = NativeTokenSymbol, + BasicFee = 1000 + } + } + }); + var chargeFeeRet = await TokenContractStub.ChargeTransactionFees.SendAsync(new ChargeTransactionFeesInput { ContractAddress = TokenContractAddress, @@ -79,8 +88,7 @@ public async Task Set_Repeat_Token_Test() } }; var sizeFee = 0; - await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, - nameof(TokenContractImplContainer.TokenContractImplStub.SetMethodFee), methodFee); + await TokenContractImplStub.SetMethodFee.SendAsync(methodFee); var beforeChargeBalance = await GetBalanceAsync(address, NativeTokenSymbol); var chargeTransactionFeesInput = new ChargeTransactionFeesInput { @@ -129,8 +137,7 @@ public async Task ChargeTransactionFees_With_Different_Transaction_Size_Fee_Toke } } }; - await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, - nameof(TokenContractImplContainer.TokenContractImplStub.SetMethodFee), methodFee); + await TokenContractImplStub.SetMethodFee.SendAsync(methodFee); var tokenSymbolList = new[] { NativeTokenSymbol, "CWJ", "YPA" }; var tokenCount = 3; var orderedSymbolList = new string[tokenCount]; @@ -156,8 +163,7 @@ await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, }); } - await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, - nameof(TokenContractImplContainer.TokenContractImplStub.SetSymbolsToPayTxSizeFee), sizeFeeSymbolList); + await TokenContractImplStub.SetSymbolsToPayTxSizeFee.SendAsync(sizeFeeSymbolList); var beforeBalanceList = await GetDefaultBalancesAsync(orderedSymbolList); var chargeTransactionFeesInput = new ChargeTransactionFeesInput @@ -214,15 +220,15 @@ public async Task DonateResourceToken_Test(long[] initialBalances, long[] tokenF if (!isMainChain) { var defaultParliament = await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); - await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, - nameof(TokenContractContainer.TokenContractStub.InitializeFromParentChain), - new InitializeFromParentChainInput - { - Creator = defaultParliament - }); + await TokenContractStub.InitializeFromParentChain.SendAsync(new InitializeFromParentChainInput + { + Creator = defaultParliament + }); } - await TokenContractStub.DonateResourceToken.SendAsync(feeMap); + var result = await TokenContractStub.DonateResourceToken.SendAsync(feeMap); + var transactionFeeClaimedDic = result.TransactionResult.Logs.Where(o => o.Name == nameof(ResourceTokenClaimed)) + .Select(o => ResourceTokenClaimed.Parser.ParseFrom(o.NonIndexed)).ToDictionary(o => o.Symbol, o => o); for (var i = 0; i < symbolList.Length; i++) { @@ -233,6 +239,12 @@ await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, var consensusBalance = await GetBalanceAsync(ConsensusContractAddress, symbolList[i]); consensusBalance.ShouldBe(initialBalances[i] - lastBalances[i]); } + + var transactionFeeClaimed = transactionFeeClaimedDic[symbolList[i]]; + transactionFeeClaimed.Symbol.ShouldBe(symbolList[i]); + transactionFeeClaimed.Amount.ShouldBe(tokenFee[i] > initialBalances[i] ? initialBalances[i] : tokenFee[i]); + transactionFeeClaimed.Payer.ShouldBe(DefaultSender); + transactionFeeClaimed.Receiver.ShouldBe(ConsensusContractAddress); } } @@ -250,7 +262,14 @@ public async Task ClaimTransactionFee_Balance_WithOut_Receiver_Test() { tokenSymbol, feeAmount } } }; - await TokenContractStub.ClaimTransactionFees.SendAsync(claimFeeInput); + var result = await TokenContractStub.ClaimTransactionFees.SendAsync(claimFeeInput); + + var transactionFeeClaimed = TransactionFeeClaimed.Parser.ParseFrom(result.TransactionResult.Logs + .First(l => l.Name.Contains(nameof(TransactionFeeClaimed))).NonIndexed); + transactionFeeClaimed.Symbol.ShouldBe(tokenSymbol); + transactionFeeClaimed.Amount.ShouldBe(feeAmount); + transactionFeeClaimed.Receiver.ShouldBe(TokenContractAddress); + var afterBurned = await GetTokenSupplyAmount(tokenSymbol); (beforeBurned - afterBurned).ShouldBe(feeAmount); } @@ -266,10 +285,8 @@ public async Task ClaimTransactionFee_Balance_With_Receiver_Test() { Creator = receiver }; - await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, - nameof(TokenContractImplContainer.TokenContractImplStub.InitializeFromParentChain), input); - await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, - nameof(TokenContractImplContainer.TokenContractImplStub.SetFeeReceiver), receiver); + await TokenContractImplStub.InitializeFromParentChain.SendAsync(input); + await TokenContractImplStub.SetFeeReceiver.SendAsync(receiver); var beforeBurned = await GetTokenSupplyAmount(tokenSymbol); var beforeBalance = await GetBalanceAsync(receiver, tokenSymbol); var claimFeeInput = new TotalTransactionFeesMap @@ -279,7 +296,14 @@ await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, { tokenSymbol, feeAmount } } }; - await TokenContractStub.ClaimTransactionFees.SendAsync(claimFeeInput); + var result = await TokenContractStub.ClaimTransactionFees.SendAsync(claimFeeInput); + + var transactionFeeClaimed = TransactionFeeClaimed.Parser.ParseFrom(result.TransactionResult.Logs + .First(l => l.Name.Contains(nameof(TransactionFeeClaimed))).NonIndexed); + transactionFeeClaimed.Symbol.ShouldBe(tokenSymbol); + transactionFeeClaimed.Amount.ShouldBe(feeAmount); + transactionFeeClaimed.Receiver.ShouldBe(TokenContractAddress); + var afterBurned = await GetTokenSupplyAmount(tokenSymbol); var afterBalance = await GetBalanceAsync(receiver, tokenSymbol); var shouldBurned = feeAmount.Div(10); @@ -301,22 +325,29 @@ public async Task FreeAllowancesTest(long threshold, long initialBalance, long f await SetPrimaryTokenSymbolAsync(); await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, initialBalance); - await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, - nameof(TokenContractStub.ConfigMethodFeeFreeAllowances), new MethodFeeFreeAllowancesConfig + await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendAsync( + new ConfigTransactionFeeFreeAllowancesInput { - FreeAllowances = new MethodFeeFreeAllowances + Value = { - Value = + new ConfigTransactionFeeFreeAllowance { - new MethodFeeFreeAllowance + Symbol = NativeTokenSymbol, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances { - Symbol = NativeTokenSymbol, - Amount = freeAmount - } + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + Amount = freeAmount + } + } + }, + RefreshSeconds = refreshSeconds, + Threshold = threshold } - }, - RefreshSeconds = refreshSeconds, - Threshold = threshold + } }); var methodFee = new MethodFees @@ -331,19 +362,20 @@ await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, } } }; - await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, - nameof(TokenContractImplContainer.TokenContractImplStub.SetMethodFee), methodFee); + await TokenContractImplStub.SetMethodFee.SendAsync(methodFee); { - var freeAllowances = await TokenContractStub.GetMethodFeeFreeAllowances.CallAsync(DefaultSender); + var freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); if (threshold <= initialBalance) { - freeAllowances.Value.First().Symbol.ShouldBe(NativeTokenSymbol); - freeAllowances.Value.First().Amount.ShouldBe(freeAmount); + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(freeAmount); } else { - freeAllowances.Value.ShouldBeEmpty(); + freeAllowances.Map.ShouldBeEmpty(); } } @@ -359,8 +391,7 @@ await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, } } }; - await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, - nameof(TokenContractImplContainer.TokenContractImplStub.SetSymbolsToPayTxSizeFee), sizeFeeSymbolList); + await TokenContractImplStub.SetSymbolsToPayTxSizeFee.SendAsync(sizeFeeSymbolList); var chargeTransactionFeesInput = new ChargeTransactionFeesInput { @@ -377,26 +408,21 @@ await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, chargeFeeRet.Output.Success.ShouldBe(true); { - var freeAllowances = await TokenContractStub.GetMethodFeeFreeAllowances.CallAsync(DefaultSender); + var freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); if (threshold <= initialBalance) { - freeAllowances.Value.First().Symbol.ShouldBe(NativeTokenSymbol); - freeAllowances.Value.First().Amount.ShouldBe(newFreeAllowance); + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(newFreeAllowance); } else { - freeAllowances.Value.ShouldBeEmpty(); + freeAllowances.Map.ShouldBeEmpty(); } } - { - var balance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput - { - Symbol = NativeTokenSymbol, - Owner = DefaultSender - }); - balance.Balance.ShouldBe(afterBalance); - } + await CheckDefaultSenderTokenAsync(NativeTokenSymbol, afterBalance); } [Theory] @@ -425,27 +451,34 @@ public async Task FreeAllowances_MultToken_Test( await IssueTokenToDefaultSenderAsync(sizeFeeSymbol, sizeFeeBalance); } - await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, - nameof(TokenContractStub.ConfigMethodFeeFreeAllowances), new MethodFeeFreeAllowancesConfig + await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendAsync( + new ConfigTransactionFeeFreeAllowancesInput { - FreeAllowances = new MethodFeeFreeAllowances + Value = { - Value = + new ConfigTransactionFeeFreeAllowance { - new MethodFeeFreeAllowance + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances { - Symbol = basicFeeSymbol, - Amount = baseFeeFreeAmount + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = basicFeeSymbol, + Amount = baseFeeFreeAmount + }, + new TransactionFeeFreeAllowance + { + Symbol = sizeFeeSymbol, + Amount = sizeFeeFreeAmount + } + } }, - new MethodFeeFreeAllowance - { - Symbol = sizeFeeSymbol, - Amount = sizeFeeFreeAmount - } + RefreshSeconds = 100, + Threshold = threshold, + Symbol = NativeTokenSymbol } - }, - RefreshSeconds = 100, - Threshold = threshold + } }); @@ -461,8 +494,7 @@ await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, } } }; - await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, - nameof(TokenContractImplContainer.TokenContractImplStub.SetMethodFee), methodFee); + await TokenContractImplStub.SetMethodFee.SendAsync(methodFee); var sizeFeeSymbolList = new SymbolListToPayTxSizeFee { @@ -482,21 +514,23 @@ await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, } } }; - await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, - nameof(TokenContractImplContainer.TokenContractImplStub.SetSymbolsToPayTxSizeFee), sizeFeeSymbolList); + await TokenContractImplStub.SetSymbolsToPayTxSizeFee.SendAsync(sizeFeeSymbolList); { - var freeAllowances = await TokenContractStub.GetMethodFeeFreeAllowances.CallAsync(DefaultSender); + var freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); if (threshold <= initialBalance) { - freeAllowances.Value.First().Symbol.ShouldBe(basicFeeSymbol); - freeAllowances.Value.First().Amount.ShouldBe(baseFeeFreeAmount); - freeAllowances.Value.Last().Symbol.ShouldBe(sizeFeeSymbol); - freeAllowances.Value.Last().Amount.ShouldBe(sizeFeeFreeAmount); + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(basicFeeSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(basicFeeSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(baseFeeFreeAmount); + freeAllowances.Map.Values.First().Map.Keys.Last().ShouldBe(sizeFeeSymbol); + freeAllowances.Map.Values.First().Map.Values.Last().Symbol.ShouldBe(sizeFeeSymbol); + freeAllowances.Map.Values.First().Map.Values.Last().Amount.ShouldBe(sizeFeeFreeAmount); } else { - freeAllowances.Value.ShouldBeEmpty(); + freeAllowances.Map.ShouldBeEmpty(); } } @@ -512,33 +546,25 @@ await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, chargeFeeRet.Output.Success.ShouldBe(true); { - var freeAllowances = await TokenContractStub.GetMethodFeeFreeAllowances.CallAsync(DefaultSender); + var freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); if (threshold <= initialBalance) { - freeAllowances.Value.First().Symbol.ShouldBe(basicFeeSymbol); - freeAllowances.Value.First().Amount.ShouldBe(newBaseFreeAllowance); - freeAllowances.Value.Last().Symbol.ShouldBe(sizeFeeSymbol); - freeAllowances.Value.Last().Amount.ShouldBe(newSizeFreeAllowance); + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(basicFeeSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(basicFeeSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(newBaseFreeAllowance); + freeAllowances.Map.Values.First().Map.Keys.Last().ShouldBe(sizeFeeSymbol); + freeAllowances.Map.Values.First().Map.Values.Last().Symbol.ShouldBe(sizeFeeSymbol); + freeAllowances.Map.Values.First().Map.Values.Last().Amount.ShouldBe(newSizeFreeAllowance); } else { - freeAllowances.Value.ShouldBeEmpty(); + freeAllowances.Map.ShouldBeEmpty(); } } - var baseFeeSymbolBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput - { - Symbol = basicFeeSymbol, - Owner = DefaultSender - }); - baseFeeSymbolBalance.Balance.ShouldBe(afterBaseFeeBalance); - - var sizeFeeSymbolBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput - { - Symbol = sizeFeeSymbol, - Owner = DefaultSender - }); - sizeFeeSymbolBalance.Balance.ShouldBe(afterSizeFeeBalance); + await CheckDefaultSenderTokenAsync(basicFeeSymbol, afterBaseFeeBalance); + await CheckDefaultSenderTokenAsync(sizeFeeSymbol, afterSizeFeeBalance); } [Theory] @@ -559,7 +585,7 @@ public async Task ChargeTransactionFee_Delegate( await CreateTokenAsync(DefaultSender, sizeFeeSymbol); await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, initialBalance); - await IssueTokenToUserAsync(NativeTokenSymbol, initialDelegateeBalance, delegateeAddress); + await IssueTokenToUserAsync(NativeTokenSymbol, initialDelegateeBalance, DelegateeAddress); await IssueTokenToUserAsync(NativeTokenSymbol, initialUserBalance, userAddress); var delegations = new Dictionary @@ -568,20 +594,21 @@ public async Task ChargeTransactionFee_Delegate( [basicFeeSymbol] = delegateeAmountBasic, [sizeFeeSymbol] = delegateeAmountSize }; - var transactionResult = await TokenContractStub2.SetTransactionFeeDelegations.SendAsync(new SetTransactionFeeDelegationsInput - { - DelegatorAddress = DefaultSender, - Delegations = + var transactionResult = await TokenContractStub2.SetTransactionFeeDelegations.SendAsync( + new SetTransactionFeeDelegationsInput { - delegations - } - }); + DelegatorAddress = DefaultSender, + Delegations = + { + delegations + } + }); // Test Case 11 { var result = await TokenContractStubA.GetTransactionFeeDelegationsOfADelegatee.CallAsync( new GetTransactionFeeDelegationsOfADelegateeInput { - DelegateeAddress = delegateeAddress, + DelegateeAddress = DelegateeAddress, DelegatorAddress = DefaultSender }); result.BlockHeight.ShouldBe(transactionResult.TransactionResult.BlockNumber); @@ -590,7 +617,7 @@ public async Task ChargeTransactionFee_Delegate( var result = await TokenContractStub.GetTransactionFeeDelegationsOfADelegatee.CallAsync( new GetTransactionFeeDelegationsOfADelegateeInput { - DelegateeAddress = delegateeAddress, + DelegateeAddress = DelegateeAddress, DelegatorAddress = DefaultSender }); result.Delegations[NativeTokenSymbol].ShouldBe(delegateeAmountNativeToken); @@ -602,46 +629,57 @@ public async Task ChargeTransactionFee_Delegate( { await IssueTokenToDefaultSenderAsync(basicFeeSymbol, baseFeeBalance); await IssueTokenToDefaultSenderAsync(sizeFeeSymbol, sizeFeeBalance); - await IssueTokenToUserAsync(basicFeeSymbol, baseFeeDelegateBalance, delegateeAddress); - await IssueTokenToUserAsync(sizeFeeSymbol, sizeFeeDelegateBalance, delegateeAddress); + await IssueTokenToUserAsync(basicFeeSymbol, baseFeeDelegateBalance, DelegateeAddress); + await IssueTokenToUserAsync(sizeFeeSymbol, sizeFeeDelegateBalance, DelegateeAddress); await IssueTokenToUserAsync(basicFeeSymbol, baseFeeUserBalance, userAddress); await IssueTokenToUserAsync(sizeFeeSymbol, sizeFeeUserBalance, userAddress); } - await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, - nameof(TokenContractStub.ConfigMethodFeeFreeAllowances), new MethodFeeFreeAllowancesConfig + await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendAsync( + new ConfigTransactionFeeFreeAllowancesInput { - FreeAllowances = new MethodFeeFreeAllowances + Value = { - Value = + new ConfigTransactionFeeFreeAllowance { - new MethodFeeFreeAllowance + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances { - Symbol = basicFeeSymbol, - Amount = baseFeeFreeAmount + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = basicFeeSymbol, + Amount = baseFeeFreeAmount + }, + new TransactionFeeFreeAllowance + { + Symbol = sizeFeeSymbol, + Amount = sizeFeeFreeAmount + } + } }, - new MethodFeeFreeAllowance - { - Symbol = sizeFeeSymbol, - Amount = sizeFeeFreeAmount - } + Symbol = NativeTokenSymbol, + RefreshSeconds = 100, + Threshold = threshold } - }, - RefreshSeconds = 100, - Threshold = threshold + } }); + { - var freeAllowances = await TokenContractStub.GetMethodFeeFreeAllowances.CallAsync(userAddress); + var freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(userAddress); if (threshold <= initialBalance) { - freeAllowances.Value.First().Symbol.ShouldBe(basicFeeSymbol); - freeAllowances.Value.First().Amount.ShouldBe(baseFeeFreeAmount); - freeAllowances.Value.Last().Symbol.ShouldBe(sizeFeeSymbol); - freeAllowances.Value.Last().Amount.ShouldBe(sizeFeeFreeAmount); + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(basicFeeSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(basicFeeSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(baseFeeFreeAmount); + freeAllowances.Map.Values.First().Map.Keys.Last().ShouldBe(sizeFeeSymbol); + freeAllowances.Map.Values.First().Map.Values.Last().Symbol.ShouldBe(sizeFeeSymbol); + freeAllowances.Map.Values.First().Map.Values.Last().Amount.ShouldBe(sizeFeeFreeAmount); } else { - freeAllowances.Value.ShouldBeEmpty(); + freeAllowances.Map.ShouldBeEmpty(); } } @@ -657,8 +695,8 @@ await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, } } }; - await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, - nameof(TokenContractImplContainer.TokenContractImplStub.SetMethodFee), methodFee); + + await TokenContractImplStub.SetMethodFee.SendAsync(methodFee); var sizeFeeSymbolList = new SymbolListToPayTxSizeFee { @@ -678,9 +716,8 @@ await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, } } }; - await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, - nameof(TokenContractImplContainer.TokenContractImplStub.SetSymbolsToPayTxSizeFee), sizeFeeSymbolList); + await TokenContractImplStub.SetSymbolsToPayTxSizeFee.SendAsync(sizeFeeSymbolList); var chargeTransactionFeesInput = new ChargeTransactionFeesInput { @@ -706,7 +743,7 @@ await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, var delegationResult = await TokenContractStub.GetTransactionFeeDelegationsOfADelegatee.CallAsync( new GetTransactionFeeDelegationsOfADelegateeInput { - DelegateeAddress = delegateeAddress, + DelegateeAddress = DelegateeAddress, DelegatorAddress = DefaultSender }); var chargeFeeRetDefault = @@ -714,7 +751,7 @@ await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, if (chargeFeeRetDefault.Transaction.RefBlockNumber >= delegationResult.BlockHeight + 2) { chargeFeeRetDefault.Output.Success.ShouldBe(true); - + var afterBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { Symbol = basicFeeSymbol, @@ -725,13 +762,13 @@ await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, var afterDelegateeBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { Symbol = basicFeeSymbol, - Owner = delegateeAddress + Owner = DelegateeAddress }); afterDelegateeBalance.Balance.ShouldBe(afterBalanceDelegatee); var delegation = await TokenContractStub.GetTransactionFeeDelegationsOfADelegatee.CallAsync( new GetTransactionFeeDelegationsOfADelegateeInput { - DelegateeAddress = delegateeAddress, + DelegateeAddress = DelegateeAddress, DelegatorAddress = DefaultSender }); delegation.Delegations[basicFeeSymbol].ShouldBe(afterDelegateeAmount); @@ -739,11 +776,11 @@ await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, else { chargeFeeRetDefault.Output.Success.ShouldBe(false); - + var delegation = await TokenContractStub.GetTransactionFeeDelegationsOfADelegatee.CallAsync( new GetTransactionFeeDelegationsOfADelegateeInput { - DelegateeAddress = delegateeAddress, + DelegateeAddress = DelegateeAddress, DelegatorAddress = DefaultSender }); delegation.Delegations[basicFeeSymbol].ShouldBe(delegateeAmountBasic); @@ -771,7 +808,7 @@ public async Task ChargeTransactionFee_Delegate_Failed( await CreateTokenAsync(DefaultSender, sizeFeeSymbol); await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, initialBalance); - await IssueTokenToUserAsync(NativeTokenSymbol, initialDelegateeBalance, delegateeAddress); + await IssueTokenToUserAsync(NativeTokenSymbol, initialDelegateeBalance, DelegateeAddress); var delegations = new Dictionary { @@ -791,7 +828,7 @@ await TokenContractStub2.SetTransactionFeeDelegations.SendAsync(new SetTransacti var result = await TokenContractStub.GetTransactionFeeDelegationsOfADelegatee.CallAsync( new GetTransactionFeeDelegationsOfADelegateeInput { - DelegateeAddress = delegateeAddress, + DelegateeAddress = DelegateeAddress, DelegatorAddress = DefaultSender }); result.Delegations[NativeTokenSymbol].ShouldBe(delegateeAmountNativeToken); @@ -802,31 +839,38 @@ await TokenContractStub2.SetTransactionFeeDelegations.SendAsync(new SetTransacti { await IssueTokenToDefaultSenderAsync(basicFeeSymbol, baseFeeBalance); await IssueTokenToDefaultSenderAsync(sizeFeeSymbol, sizeFeeBalance); - await IssueTokenToUserAsync(basicFeeSymbol, baseFeeDelegateBalance, delegateeAddress); - await IssueTokenToUserAsync(sizeFeeSymbol, sizeFeeDelegateBalance, delegateeAddress); + await IssueTokenToUserAsync(basicFeeSymbol, baseFeeDelegateBalance, DelegateeAddress); + await IssueTokenToUserAsync(sizeFeeSymbol, sizeFeeDelegateBalance, DelegateeAddress); } - await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, - nameof(TokenContractStub.ConfigMethodFeeFreeAllowances), new MethodFeeFreeAllowancesConfig + await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendAsync( + new ConfigTransactionFeeFreeAllowancesInput { - FreeAllowances = new MethodFeeFreeAllowances + Value = { - Value = + new ConfigTransactionFeeFreeAllowance { - new MethodFeeFreeAllowance + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances { - Symbol = basicFeeSymbol, - Amount = baseFeeFreeAmount + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = basicFeeSymbol, + Amount = baseFeeFreeAmount + }, + new TransactionFeeFreeAllowance + { + Symbol = sizeFeeSymbol, + Amount = sizeFeeFreeAmount + } + } }, - new MethodFeeFreeAllowance - { - Symbol = sizeFeeSymbol, - Amount = sizeFeeFreeAmount - } + RefreshSeconds = 100, + Threshold = threshold, + Symbol = NativeTokenSymbol } - }, - RefreshSeconds = 100, - Threshold = threshold + } }); var methodFee = new MethodFees @@ -841,8 +885,7 @@ await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, } } }; - await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, - nameof(TokenContractImplContainer.TokenContractImplStub.SetMethodFee), methodFee); + await TokenContractImplStub.SetMethodFee.SendAsync(methodFee); var sizeFeeSymbolList = new SymbolListToPayTxSizeFee { @@ -862,8 +905,7 @@ await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, } } }; - await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, - nameof(TokenContractImplContainer.TokenContractImplStub.SetSymbolsToPayTxSizeFee), sizeFeeSymbolList); + await TokenContractImplStub.SetSymbolsToPayTxSizeFee.SendAsync(sizeFeeSymbolList); var chargeTransactionFeesInput = new ChargeTransactionFeesInput @@ -890,13 +932,13 @@ await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, var afterDelegateeBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { Symbol = basicFeeSymbol, - Owner = delegateeAddress + Owner = DelegateeAddress }); afterDelegateeBalance.Balance.ShouldBe(afterBalanceDelegatee); var delegation = await TokenContractStub.GetTransactionFeeDelegationsOfADelegatee.CallAsync( new GetTransactionFeeDelegationsOfADelegateeInput { - DelegateeAddress = delegateeAddress, + DelegateeAddress = DelegateeAddress, DelegatorAddress = DefaultSender }); delegation.Delegations[sizeFeeSymbol].ShouldBe(afterDelegateeAmount); @@ -913,8 +955,8 @@ public async Task ChargeTransactionFee_Delegate_DefaultSizeFee( await SetPrimaryTokenSymbolAsync(); await CreateTokenAsync(DefaultSender, basicFeeSymbol); - await IssueTokenToUserAsync(NativeTokenSymbol, initialDelegateeBalance, delegateeAddress); - await IssueTokenToUserAsync(basicFeeSymbol, initialDelegateeBalance, delegateeAddress); + await IssueTokenToUserAsync(NativeTokenSymbol, initialDelegateeBalance, DelegateeAddress); + await IssueTokenToUserAsync(basicFeeSymbol, initialDelegateeBalance, DelegateeAddress); var delegations = new Dictionary { @@ -933,7 +975,7 @@ await TokenContractStub2.SetTransactionFeeDelegations.SendAsync(new SetTransacti var result = await TokenContractStub.GetTransactionFeeDelegationsOfADelegatee.CallAsync( new GetTransactionFeeDelegationsOfADelegateeInput { - DelegateeAddress = delegateeAddress, + DelegateeAddress = DelegateeAddress, DelegatorAddress = userAddress }); result.Delegations[basicFeeSymbol].ShouldBe(delegateeAmountBasic); @@ -951,8 +993,7 @@ await TokenContractStub2.SetTransactionFeeDelegations.SendAsync(new SetTransacti } } }; - await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, - nameof(TokenContractImplContainer.TokenContractImplStub.SetMethodFee), methodFee); + await TokenContractImplStub.SetMethodFee.SendAsync(methodFee); var chargeTransactionFeesInput = new ChargeTransactionFeesInput { @@ -975,89 +1016,1570 @@ await SubmitAndPassProposalOfDefaultParliamentAsync(TokenContractAddress, var afterDelegateeBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { Symbol = basicFeeSymbol, - Owner = delegateeAddress + Owner = DelegateeAddress }); afterDelegateeBalance.Balance.ShouldBe(exceptDelegateeBalance); var afterDelegateeElfBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { Symbol = NativeTokenSymbol, - Owner = delegateeAddress + Owner = DelegateeAddress }); afterDelegateeElfBalance.Balance.ShouldBe(exceptDelegateeElfBalance); } } - private async Task GetTokenSupplyAmount(string tokenSymbol) + [Theory] + [InlineData(1000, 1000, 1000, 1000, 1000, 1000, 1000, 50, 50, 100, 100, 50, 50, 10, 10, 80, 80, 50, 30, 920)] + public async Task ChargeTransactionFee_DelegateNew_First( + long threshold, long initialBalance, long initialDelegateeBalance, long initialUserBalance, + long delegateeAmountNativeToken, long delegateeAmountBasic, long delegateeAmountSize, + long baseFeeBalance, long sizeFeeBalance, long baseFeeDelegateBalance, long sizeFeeDelegateBalance, + long baseFeeUserBalance, long sizeFeeUserBalance, long baseFeeFreeAmount, long sizeFeeFreeAmount, + long basicFee, long sizeFee, long afterBalanceDefault, long afterBalanceDelegatee, long afterDelegateeAmount + ) { - var tokenInfo = await TokenContractStub.GetTokenInfo.CallAsync(new GetTokenInfoInput + var basicFeeSymbol = "BASIC"; + var sizeFeeSymbol = "SIZE"; + + await SetPrimaryTokenSymbolAsync(); + await CreateTokenAsync(DefaultSender, basicFeeSymbol); + await CreateTokenAsync(DefaultSender, sizeFeeSymbol); + + await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, initialBalance); + await IssueTokenToUserAsync(NativeTokenSymbol, initialDelegateeBalance, DelegateeAddress); + await IssueTokenToUserAsync(NativeTokenSymbol, initialUserBalance, userAddress); + await IssueTokenToUserAsync(NativeTokenSymbol, initialUserBalance, UserTomSender); + if (baseFeeBalance != 0 && sizeFeeBalance != 0 && + baseFeeDelegateBalance != 0 && sizeFeeDelegateBalance != 0 && + baseFeeUserBalance != 0 && sizeFeeUserBalance != 0) { - Symbol = tokenSymbol - }); - return tokenInfo.Supply; - } + await IssueTokenToDefaultSenderAsync(basicFeeSymbol, baseFeeBalance); + await IssueTokenToDefaultSenderAsync(sizeFeeSymbol, sizeFeeBalance); + await IssueTokenToUserAsync(basicFeeSymbol, baseFeeDelegateBalance, DelegateeAddress); + await IssueTokenToUserAsync(sizeFeeSymbol, sizeFeeDelegateBalance, DelegateeAddress); + await IssueTokenToUserAsync(basicFeeSymbol, baseFeeUserBalance, userAddress); + await IssueTokenToUserAsync(sizeFeeSymbol, sizeFeeUserBalance, userAddress); + await IssueTokenToUserAsync(basicFeeSymbol, baseFeeUserBalance, UserTomSender); + await IssueTokenToUserAsync(sizeFeeSymbol, sizeFeeUserBalance, UserTomSender); + } - private async Task> GetDefaultBalancesAsync(string[] tokenSymbolList) - { - var balances = new List(); - foreach (var symbol in tokenSymbolList) - balances.Add(await GetBalanceAsync(DefaultSender, symbol)); - return balances; - } + var delegations = new Dictionary + { + [NativeTokenSymbol] = delegateeAmountNativeToken, + [basicFeeSymbol] = delegateeAmountBasic, + [sizeFeeSymbol] = delegateeAmountSize + }; + var delegations1 = new Dictionary + { + [NativeTokenSymbol] = 5, + }; + var delegateInfo1 = new DelegateInfo + { + Delegations = { delegations }, + IsUnlimitedDelegate = false, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + }; + var delegateInfo2 = new DelegateInfo + { + Delegations = { delegations }, + IsUnlimitedDelegate = false, + MethodName = nameof(TokenContractContainer.TokenContractStub.TransferFrom), + ContractAddress = TokenContractAddress, + }; + var delegateInfo3 = new DelegateInfo + { + Delegations = { delegations }, + IsUnlimitedDelegate = false, + MethodName = nameof(TokenContractContainer.TokenContractStub.TransferFrom), + ContractAddress = ConsensusContractAddress, + }; + var delegateInfo4 = new DelegateInfo + { + Delegations = { delegations1 }, + IsUnlimitedDelegate = false, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + }; + var transactionResult = await TokenContractStubDelegate1.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = DefaultSender, + DelegateInfoList = { delegateInfo1 } + }); + //delegate User transferFrom method + var transactionResult1 = await TokenContractStubDelegate1.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = userAddress, + DelegateInfoList = { delegateInfo2 } + }); + //delegate User ConsensusContract + var transactionResult2 = await TokenContractStubDelegate1.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = userAddress, + DelegateInfoList = { delegateInfo3 } + }); + var transactionResult3 = await TokenContractStubDelegate1.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = UserTomSender, + DelegateInfoList = { delegateInfo4 } + }); + { + var result = await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = DelegateeAddress, + DelegatorAddress = DefaultSender, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + result.BlockHeight.ShouldBe(transactionResult.TransactionResult.BlockNumber); + result.Delegations[NativeTokenSymbol].ShouldBe(delegateeAmountNativeToken); + } + + var delegationsBefore = await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = DelegateeAddress, + DelegatorAddress = userAddress, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.TransferFrom) + }); + delegationsBefore.BlockHeight.ShouldBe(transactionResult1.TransactionResult.BlockNumber); + delegationsBefore.Delegations[NativeTokenSymbol].ShouldBe(delegateeAmountNativeToken); + + + var delegationBefore2 = await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = DelegateeAddress, + DelegatorAddress = userAddress, + ContractAddress = ConsensusContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.TransferFrom) + }); + delegationBefore2.BlockHeight.ShouldBe(transactionResult2.TransactionResult.BlockNumber); + delegationBefore2.Delegations[NativeTokenSymbol].ShouldBe(delegateeAmountNativeToken); - private async Task CreateTokenAsync(Address creator, string tokenSymbol, bool isBurned = true) - { - await TokenContractStub.Create.SendAsync(new CreateInput { - Symbol = tokenSymbol, - TokenName = tokenSymbol + " name", - TotalSupply = 1000_00000000, - IsBurnable = isBurned, - Issuer = creator, - }); - } + var result = await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = DelegateeAddress, + DelegatorAddress = UserTomSender, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + result.BlockHeight.ShouldBe(transactionResult3.TransactionResult.BlockNumber); + result.Delegations[NativeTokenSymbol].ShouldBe(5); + } - private async Task IssueTokenToDefaultSenderAsync(string tokenSymbol, long amount) - { - var issueResult = await TokenContractStub.Issue.SendAsync(new IssueInput() + await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + RefreshSeconds = 100, + Threshold = threshold, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = basicFeeSymbol, + Amount = baseFeeFreeAmount + }, + new TransactionFeeFreeAllowance + { + Symbol = sizeFeeSymbol, + Amount = sizeFeeFreeAmount + } + } + } + } + } + }); { - Symbol = tokenSymbol, - Amount = amount, - To = DefaultSender, - }); - issueResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); - } + var freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(userAddress); + if (threshold <= initialBalance) + { + freeAllowances.Map.First().Value.Map.Values.First().Symbol.ShouldBe(basicFeeSymbol); + freeAllowances.Map.First().Value.Map.Values.First().Amount.ShouldBe(baseFeeFreeAmount); + freeAllowances.Map.First().Value.Map.Values.Last().Symbol.ShouldBe(sizeFeeSymbol); + freeAllowances.Map.First().Value.Map.Values.Last().Amount.ShouldBe(sizeFeeFreeAmount); + } + else + { + freeAllowances.Map.ShouldBeEmpty(); + } + } + var sizeFeeSymbolList = await SetMethodOrSizeFeeAsync(basicFeeSymbol, sizeFeeSymbol, 80); - private async Task IssueTokenToUserAsync(string tokenSymbol, long amount, Address to) - { - var issueResult = await TokenContractStub.Issue.SendAsync(new IssueInput() + var chargeTransactionFeesInput = new ChargeTransactionFeesInput { - Symbol = tokenSymbol, - Amount = amount, - To = to, - }); - issueResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); - } + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + TransactionSizeFee = sizeFee, + }; - // single node - private async Task SubmitAndPassProposalOfDefaultParliamentAsync(Address contractAddress, string methodName, - IMessage input) - { - var defaultParliament = await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); - var proposal = new CreateProposalInput - { - OrganizationAddress = defaultParliament, - ToAddress = contractAddress, - Params = input.ToByteString(), - ContractMethodName = methodName, - ExpiredTime = TimestampHelper.GetUtcNow().AddHours(1) - }; - var createProposalRet = await ParliamentContractStub.CreateProposal.SendAsync(proposal); - createProposalRet.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); - var proposalId = createProposalRet.Output; - await ParliamentContractStub.Approve.SendAsync(proposalId); - var releaseRet = await ParliamentContractStub.Release.SendAsync(proposalId); - releaseRet.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + chargeTransactionFeesInput.SymbolsToPayTxSizeFee.AddRange(sizeFeeSymbolList.SymbolsToPayTxSizeFee); + { + var chargeFeeRetUser = await TokenContractStubA.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + chargeFeeRetUser.Output.Success.ShouldBe(false); + chargeFeeRetUser.Output.ChargingInformation.ShouldBe("Transaction fee not enough."); + + var afterBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = userAddress + }); + afterBalance.Balance.ShouldBe(0); + } + { + var chargeFeeRetUser = + await TokenContractStubDelegator.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + chargeFeeRetUser.Output.Success.ShouldBe(false); + chargeFeeRetUser.Output.ChargingInformation.ShouldBe("Transaction fee not enough."); + + var afterBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = UserTomSender + }); + afterBalance.Balance.ShouldBe(0); + } + var delegationsAfter = await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = DelegateeAddress, + DelegatorAddress = userAddress, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.TransferFrom) + }); + delegationsAfter.BlockHeight.ShouldBe(transactionResult1.TransactionResult.BlockNumber); + delegationsAfter.Delegations[NativeTokenSymbol].ShouldBe(delegateeAmountNativeToken); + + + var delegationAfter2 = await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = DelegateeAddress, + DelegatorAddress = DefaultSender, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + delegationAfter2.BlockHeight.ShouldBe(transactionResult.TransactionResult.BlockNumber); + delegationAfter2.Delegations[NativeTokenSymbol].ShouldBe(delegateeAmountNativeToken); + { + var delegationResult = + await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = DelegateeAddress, + DelegatorAddress = DefaultSender, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + var chargeFeeRetDefault = + await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + if (chargeFeeRetDefault.Transaction.RefBlockNumber >= delegationResult.BlockHeight + 2) + { + chargeFeeRetDefault.Output.Success.ShouldBe(true); + + var afterBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = DefaultSender + }); + afterBalance.Balance.ShouldBe(afterBalanceDefault); + + var afterDelegateeBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = DelegateeAddress + }); + afterDelegateeBalance.Balance.ShouldBe(afterBalanceDelegatee); + var delegation = await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = DelegateeAddress, + DelegatorAddress = DefaultSender, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + delegation.Delegations[basicFeeSymbol].ShouldBe(afterDelegateeAmount); + } + else + { + chargeFeeRetDefault.Output.Success.ShouldBe(false); + + var delegation = await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = DelegateeAddress, + DelegatorAddress = DefaultSender, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + delegation.Delegations[basicFeeSymbol].ShouldBe(delegateeAmountBasic); + } + } + } + + [Theory] + [InlineData(10, 20, 100, 1000, 1000, 1000, 10, 10, 20, 20, 100, 100, 80, 80, 20, 920)] + public async Task ChargeTransactionFee_DelegationNew_Second_Success( + long initialBalance, long initialDelegateeBalance, long initialDelegatee2Balance, + long delegateeAmountNativeToken, long delegateeAmountBasic, long delegateeAmountSize, + long baseFeeBalance, long sizeFeeBalance, long baseFeeDelegateBalance, long sizeFeeDelegateBalance, + long baseFeeDelegate2Balance, long sizeFeeDelegate2Balance, + long basicFee, long sizeFee, long afterBalanceDelegatee, long afterDelegateeAmount) + { + var basicFeeSymbol = "BASIC"; + var sizeFeeSymbol = "SIZE"; + + await SetPrimaryTokenSymbolAsync(); + await CreateTokenAsync(DefaultSender, basicFeeSymbol); + await CreateTokenAsync(DefaultSender, sizeFeeSymbol); + + await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, initialBalance); + await IssueTokenToUserAsync(NativeTokenSymbol, initialDelegateeBalance, DelegateeAddress); + await IssueTokenToUserAsync(NativeTokenSymbol, initialDelegatee2Balance, Delegatee2Address); + if (baseFeeBalance != 0 && sizeFeeBalance != 0 && + baseFeeDelegateBalance != 0 && sizeFeeDelegateBalance != 0 && + baseFeeDelegate2Balance != 0 && sizeFeeDelegate2Balance != 0) + { + await IssueTokenToDefaultSenderAsync(basicFeeSymbol, baseFeeBalance); + await IssueTokenToDefaultSenderAsync(sizeFeeSymbol, sizeFeeBalance); + await IssueTokenToUserAsync(basicFeeSymbol, baseFeeDelegateBalance, DelegateeAddress); + await IssueTokenToUserAsync(sizeFeeSymbol, sizeFeeDelegateBalance, DelegateeAddress); + await IssueTokenToUserAsync(basicFeeSymbol, baseFeeDelegate2Balance, Delegatee2Address); + await IssueTokenToUserAsync(sizeFeeSymbol, sizeFeeDelegate2Balance, Delegatee2Address); + } + + var delegations = new Dictionary + { + [NativeTokenSymbol] = 5, + }; + var delegations2 = new Dictionary + { + [NativeTokenSymbol] = delegateeAmountNativeToken, + [basicFeeSymbol] = delegateeAmountBasic, + [sizeFeeSymbol] = delegateeAmountSize + }; + var delegateInfo1 = new DelegateInfo + { + Delegations = { delegations }, + IsUnlimitedDelegate = false, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + }; + var delegateInfo2 = new DelegateInfo + { + Delegations = { delegations2 }, + IsUnlimitedDelegate = false, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + }; + await TokenContractStubDelegate1.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = DefaultSender, + DelegateInfoList = { delegateInfo1 } + }); + await TokenContractStubDelegate2.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = DelegateeAddress, + DelegateInfoList = { delegateInfo2 } + }); + + var sizeFeeSymbolList = await SetMethodOrSizeFeeAsync(basicFeeSymbol, sizeFeeSymbol, 80); + + var chargeTransactionFeesInput = new ChargeTransactionFeesInput + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + TransactionSizeFee = sizeFee, + }; + chargeTransactionFeesInput.SymbolsToPayTxSizeFee.AddRange(sizeFeeSymbolList.SymbolsToPayTxSizeFee); + var delegationResult = + await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = DelegateeAddress, + DelegatorAddress = DefaultSender, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + var delegationResult2 = + await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = Delegatee2Address, + DelegatorAddress = DelegateeAddress, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + var chargeFeeRetDefault = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + if (chargeFeeRetDefault.Transaction.RefBlockNumber >= delegationResult.BlockHeight + 2 && + chargeFeeRetDefault.Transaction.RefBlockNumber >= delegationResult2.BlockHeight + 2) + { + chargeFeeRetDefault.Output.Success.ShouldBe(true); + + //no change + var afterBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = DefaultSender + }); + afterBalance.Balance.ShouldBe(10); + + //no change + var afterDelegateeBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = DelegateeAddress + }); + afterDelegateeBalance.Balance.ShouldBe(20); + + var afterSecondaryDelegateeBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = Delegatee2Address + }); + afterSecondaryDelegateeBalance.Balance.ShouldBe(afterBalanceDelegatee); + var delegation = await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = Delegatee2Address, + DelegatorAddress = DelegateeAddress, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + delegation.Delegations[basicFeeSymbol].ShouldBe(afterDelegateeAmount); + } + else + { + chargeFeeRetDefault.Output.Success.ShouldBe(false); + + var delegation = await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = DelegateeAddress, + DelegatorAddress = DefaultSender, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + delegation.Delegations[NativeTokenSymbol].ShouldBe(5); + var delegation2 = await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = Delegatee2Address, + DelegatorAddress = DelegateeAddress, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + delegation2.Delegations[basicFeeSymbol].ShouldBe(delegateeAmountBasic); + } + } + + [Theory] + [InlineData(10, 20, 70, 1000, 1000, 1000, 10, 10, 20, 20, 70, 70, 80, 80, 70, 1000)] + public async Task ChargeTransactionFee_DelegationNew_Second_Failed_DelegateeBalanceIsNotEnough( + long initialBalance, long initialDelegateeBalance, long initialDelegatee2Balance, + long delegateeAmountNativeToken, long delegateeAmountBasic, long delegateeAmountSize, + long baseFeeBalance, long sizeFeeBalance, long baseFeeDelegateBalance, long sizeFeeDelegateBalance, + long baseFeeDelegate2Balance, long sizeFeeDelegate2Balance, + long basicFee, long sizeFee, long afterBalanceDelegatee, long afterDelegateeAmount) + { + var basicFeeSymbol = "BASIC"; + var sizeFeeSymbol = "SIZE"; + + await SetPrimaryTokenSymbolAsync(); + await CreateTokenAsync(DefaultSender, basicFeeSymbol); + await CreateTokenAsync(DefaultSender, sizeFeeSymbol); + + await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, initialBalance); + await IssueTokenToUserAsync(NativeTokenSymbol, initialDelegateeBalance, DelegateeAddress); + await IssueTokenToUserAsync(NativeTokenSymbol, initialDelegatee2Balance, Delegatee2Address); + if (baseFeeBalance != 0 && sizeFeeBalance != 0 && + baseFeeDelegateBalance != 0 && sizeFeeDelegateBalance != 0 && + baseFeeDelegate2Balance != 0 && sizeFeeDelegate2Balance != 0) + { + await IssueTokenToDefaultSenderAsync(basicFeeSymbol, baseFeeBalance); + await IssueTokenToDefaultSenderAsync(sizeFeeSymbol, sizeFeeBalance); + await IssueTokenToUserAsync(basicFeeSymbol, baseFeeDelegateBalance, DelegateeAddress); + await IssueTokenToUserAsync(sizeFeeSymbol, sizeFeeDelegateBalance, DelegateeAddress); + await IssueTokenToUserAsync(basicFeeSymbol, baseFeeDelegate2Balance, Delegatee2Address); + await IssueTokenToUserAsync(sizeFeeSymbol, sizeFeeDelegate2Balance, Delegatee2Address); + } + + var delegations = new Dictionary + { + [NativeTokenSymbol] = 5, + }; + var delegations2 = new Dictionary + { + [NativeTokenSymbol] = delegateeAmountNativeToken, + [basicFeeSymbol] = delegateeAmountBasic, + [sizeFeeSymbol] = delegateeAmountSize + }; + var delegateInfo1 = new DelegateInfo + { + Delegations = { delegations }, + IsUnlimitedDelegate = false, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + }; + var delegateInfo2 = new DelegateInfo + { + Delegations = { delegations2 }, + IsUnlimitedDelegate = false, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + }; + await TokenContractStubDelegate1.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = DefaultSender, + DelegateInfoList = { delegateInfo1 } + }); + await TokenContractStubDelegate2.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = DelegateeAddress, + DelegateInfoList = { delegateInfo2 } + }); + + var sizeFeeSymbolList = await SetMethodOrSizeFeeAsync(basicFeeSymbol, sizeFeeSymbol, 80); + + var chargeTransactionFeesInput = new ChargeTransactionFeesInput + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + TransactionSizeFee = sizeFee, + }; + chargeTransactionFeesInput.SymbolsToPayTxSizeFee.AddRange(sizeFeeSymbolList.SymbolsToPayTxSizeFee); + var delegationResult = + await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = DelegateeAddress, + DelegatorAddress = DefaultSender, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + var delegationResult2 = + await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = Delegatee2Address, + DelegatorAddress = DelegateeAddress, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + var chargeFeeRetDefault = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + if (chargeFeeRetDefault.Transaction.RefBlockNumber >= delegationResult.BlockHeight + 2 && + chargeFeeRetDefault.Transaction.RefBlockNumber >= delegationResult2.BlockHeight + 2) + { + chargeFeeRetDefault.Output.Success.ShouldBe(false); + chargeFeeRetDefault.Output.ChargingInformation.ShouldBe("Transaction fee not enough."); + + //all in + var afterBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = DefaultSender + }); + afterBalance.Balance.ShouldBe(0); + + //no change + var afterDelegateeBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = DelegateeAddress + }); + afterDelegateeBalance.Balance.ShouldBe(20); + + //no change + var afterSecondaryDelegateeBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = Delegatee2Address + }); + afterSecondaryDelegateeBalance.Balance.ShouldBe(afterBalanceDelegatee); + var delegation = await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = Delegatee2Address, + DelegatorAddress = DelegateeAddress, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + delegation.Delegations[basicFeeSymbol].ShouldBe(afterDelegateeAmount); + } + else + { + chargeFeeRetDefault.Output.Success.ShouldBe(false); + } + } + + [Theory] + [InlineData(10, 20, 100, 30, 30, 30, 10, 10, 20, 20, 100, 100, 80, 80, 100, 30)] + public async Task ChargeTransactionFee_DelegationNew_Second_Failed_DelegationIsNotEnough( + long initialBalance, long initialDelegateeBalance, long initialDelegatee2Balance, + long delegateeAmountNativeToken, long delegateeAmountBasic, long delegateeAmountSize, + long baseFeeBalance, long sizeFeeBalance, long baseFeeDelegateBalance, long sizeFeeDelegateBalance, + long baseFeeDelegate2Balance, long sizeFeeDelegate2Balance, + long basicFee, long sizeFee, long afterBalanceDelegatee, long afterDelegateeAmount) + { + var basicFeeSymbol = "BASIC"; + var sizeFeeSymbol = "SIZE"; + + await SetPrimaryTokenSymbolAsync(); + await CreateTokenAsync(DefaultSender, basicFeeSymbol); + await CreateTokenAsync(DefaultSender, sizeFeeSymbol); + + await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, initialBalance); + await IssueTokenToUserAsync(NativeTokenSymbol, initialDelegateeBalance, DelegateeAddress); + await IssueTokenToUserAsync(NativeTokenSymbol, initialDelegatee2Balance, Delegatee2Address); + if (baseFeeBalance != 0 && sizeFeeBalance != 0 && + baseFeeDelegateBalance != 0 && sizeFeeDelegateBalance != 0 && + baseFeeDelegate2Balance != 0 && sizeFeeDelegate2Balance != 0) + { + await IssueTokenToDefaultSenderAsync(basicFeeSymbol, baseFeeBalance); + await IssueTokenToDefaultSenderAsync(sizeFeeSymbol, sizeFeeBalance); + await IssueTokenToUserAsync(basicFeeSymbol, baseFeeDelegateBalance, DelegateeAddress); + await IssueTokenToUserAsync(sizeFeeSymbol, sizeFeeDelegateBalance, DelegateeAddress); + await IssueTokenToUserAsync(basicFeeSymbol, baseFeeDelegate2Balance, Delegatee2Address); + await IssueTokenToUserAsync(sizeFeeSymbol, sizeFeeDelegate2Balance, Delegatee2Address); + } + + var delegations = new Dictionary + { + [NativeTokenSymbol] = 5, + }; + var delegations2 = new Dictionary + { + [NativeTokenSymbol] = delegateeAmountNativeToken, + [basicFeeSymbol] = delegateeAmountBasic, + [sizeFeeSymbol] = delegateeAmountSize + }; + var delegateInfo1 = new DelegateInfo + { + Delegations = { delegations }, + IsUnlimitedDelegate = false, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + }; + var delegateInfo2 = new DelegateInfo + { + Delegations = { delegations2 }, + IsUnlimitedDelegate = false, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + }; + await TokenContractStubDelegate1.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = DefaultSender, + DelegateInfoList = { delegateInfo1 } + }); + await TokenContractStubDelegate2.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = DelegateeAddress, + DelegateInfoList = { delegateInfo2 } + }); + + var sizeFeeSymbolList = await SetMethodOrSizeFeeAsync(basicFeeSymbol, sizeFeeSymbol, 80); + + var chargeTransactionFeesInput = new ChargeTransactionFeesInput + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + TransactionSizeFee = sizeFee, + }; + chargeTransactionFeesInput.SymbolsToPayTxSizeFee.AddRange(sizeFeeSymbolList.SymbolsToPayTxSizeFee); + var chargeFeeRetDefault = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + chargeFeeRetDefault.Output.Success.ShouldBe(false); + + //no change + var afterBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = DefaultSender + }); + afterBalance.Balance.ShouldBe(0); + + //no change + var afterDelegateeBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = DelegateeAddress + }); + afterDelegateeBalance.Balance.ShouldBe(20); + + var afterSecondaryDelegateeBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = Delegatee2Address + }); + afterSecondaryDelegateeBalance.Balance.ShouldBe(afterBalanceDelegatee); + var delegation = await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = Delegatee2Address, + DelegatorAddress = DelegateeAddress, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + delegation.Delegations[basicFeeSymbol].ShouldBe(afterDelegateeAmount); + } + + [Theory] + [InlineData(10, 20, 100, 1000, 1000, 1000, 10, 10, 20, 20, 100, 100, 80, 80, 100, 1000)] + public async Task ChargeTransactionFee_DelegationNew_Second_Failed_DelegateIsNotExist( + long initialBalance, long initialDelegateeBalance, long initialDelegatee2Balance, + long delegateeAmountNativeToken, long delegateeAmountBasic, long delegateeAmountSize, + long baseFeeBalance, long sizeFeeBalance, long baseFeeDelegateBalance, long sizeFeeDelegateBalance, + long baseFeeDelegate2Balance, long sizeFeeDelegate2Balance, + long basicFee, long sizeFee, long afterBalanceDelegatee, long afterDelegateeAmount) + { + var basicFeeSymbol = "BASIC"; + var sizeFeeSymbol = "SIZE"; + + await SetPrimaryTokenSymbolAsync(); + await CreateTokenAsync(DefaultSender, basicFeeSymbol); + await CreateTokenAsync(DefaultSender, sizeFeeSymbol); + + await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, initialBalance); + await IssueTokenToUserAsync(NativeTokenSymbol, initialDelegateeBalance, DelegateeAddress); + await IssueTokenToUserAsync(NativeTokenSymbol, initialDelegatee2Balance, Delegatee2Address); + if (baseFeeBalance != 0 && sizeFeeBalance != 0 && + baseFeeDelegateBalance != 0 && sizeFeeDelegateBalance != 0 && + baseFeeDelegate2Balance != 0 && sizeFeeDelegate2Balance != 0) + { + await IssueTokenToDefaultSenderAsync(basicFeeSymbol, baseFeeBalance); + await IssueTokenToDefaultSenderAsync(sizeFeeSymbol, sizeFeeBalance); + await IssueTokenToUserAsync(basicFeeSymbol, baseFeeDelegateBalance, DelegateeAddress); + await IssueTokenToUserAsync(sizeFeeSymbol, sizeFeeDelegateBalance, DelegateeAddress); + await IssueTokenToUserAsync(basicFeeSymbol, baseFeeDelegate2Balance, Delegatee2Address); + await IssueTokenToUserAsync(sizeFeeSymbol, sizeFeeDelegate2Balance, Delegatee2Address); + } + + var delegations = new Dictionary + { + [NativeTokenSymbol] = 5, + }; + var delegations2 = new Dictionary + { + [NativeTokenSymbol] = delegateeAmountNativeToken, + [basicFeeSymbol] = delegateeAmountBasic, + [sizeFeeSymbol] = delegateeAmountSize + }; + var delegateInfo1 = new DelegateInfo + { + Delegations = { delegations }, + IsUnlimitedDelegate = false, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + }; + var delegateInfo2 = new DelegateInfo + { + Delegations = { delegations2 }, + IsUnlimitedDelegate = false, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = ConsensusContractAddress, + }; + await TokenContractStubDelegate1.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = DefaultSender, + DelegateInfoList = { delegateInfo1 } + }); + await TokenContractStubDelegate2.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = DelegateeAddress, + DelegateInfoList = { delegateInfo2 } + }); + + var sizeFeeSymbolList = await SetMethodOrSizeFeeAsync(basicFeeSymbol, sizeFeeSymbol, basicFee); + + var chargeTransactionFeesInput = new ChargeTransactionFeesInput + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + TransactionSizeFee = sizeFee, + }; + chargeTransactionFeesInput.SymbolsToPayTxSizeFee.AddRange(sizeFeeSymbolList.SymbolsToPayTxSizeFee); + var chargeFeeRetDefault = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + chargeFeeRetDefault.Output.Success.ShouldBe(false); + + //no change + var afterBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = DefaultSender + }); + afterBalance.Balance.ShouldBe(0); + + //no change + var afterDelegateeBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = DelegateeAddress + }); + afterDelegateeBalance.Balance.ShouldBe(20); + + var afterSecondaryDelegateeBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = Delegatee2Address + }); + afterSecondaryDelegateeBalance.Balance.ShouldBe(afterBalanceDelegatee); + var delegation = await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = Delegatee2Address, + DelegatorAddress = DelegateeAddress, + ContractAddress = ConsensusContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + delegation.Delegations[basicFeeSymbol].ShouldBe(afterDelegateeAmount); + } + + [Theory] + [InlineData(10, 20, 100, 1000, 1000, 1000, 10, 10, 20, 20, 100, 100, 80, 80, 20, 920)] + public async Task ChargeTransactionFee_DelegationOldFirst_NewSecond_Success( + long initialBalance, long initialDelegateeBalance, long initialDelegatee2Balance, + long delegateeAmountNativeToken, long delegateeAmountBasic, long delegateeAmountSize, + long baseFeeBalance, long sizeFeeBalance, long baseFeeDelegateBalance, long sizeFeeDelegateBalance, + long baseFeeDelegate2Balance, long sizeFeeDelegate2Balance, + long basicFee, long sizeFee, long afterBalanceDelegatee, long afterDelegateeAmount) + { + var basicFeeSymbol = "BASIC"; + var sizeFeeSymbol = "SIZE"; + + await SetPrimaryTokenSymbolAsync(); + await CreateTokenAsync(DefaultSender, basicFeeSymbol); + await CreateTokenAsync(DefaultSender, sizeFeeSymbol); + + await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, initialBalance); + await IssueTokenToUserAsync(NativeTokenSymbol, initialDelegateeBalance, DelegateeAddress); + await IssueTokenToUserAsync(NativeTokenSymbol, initialDelegatee2Balance, Delegatee2Address); + if (baseFeeBalance != 0 && sizeFeeBalance != 0 && + baseFeeDelegateBalance != 0 && sizeFeeDelegateBalance != 0 && + baseFeeDelegate2Balance != 0 && sizeFeeDelegate2Balance != 0) + { + await IssueTokenToDefaultSenderAsync(basicFeeSymbol, baseFeeBalance); + await IssueTokenToDefaultSenderAsync(sizeFeeSymbol, sizeFeeBalance); + await IssueTokenToUserAsync(basicFeeSymbol, baseFeeDelegateBalance, DelegateeAddress); + await IssueTokenToUserAsync(sizeFeeSymbol, sizeFeeDelegateBalance, DelegateeAddress); + await IssueTokenToUserAsync(basicFeeSymbol, baseFeeDelegate2Balance, Delegatee2Address); + await IssueTokenToUserAsync(sizeFeeSymbol, sizeFeeDelegate2Balance, Delegatee2Address); + } + + var delegations = new Dictionary + { + [NativeTokenSymbol] = 5, + }; + var delegations2 = new Dictionary + { + [NativeTokenSymbol] = delegateeAmountNativeToken, + [basicFeeSymbol] = delegateeAmountBasic, + [sizeFeeSymbol] = delegateeAmountSize + }; + var delegateInfo2 = new DelegateInfo + { + Delegations = { delegations2 }, + IsUnlimitedDelegate = false, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + }; + await TokenContractStubDelegate1.SetTransactionFeeDelegations.SendAsync( + new SetTransactionFeeDelegationsInput + { + DelegatorAddress = DefaultSender, + Delegations = { delegations } + }); + await TokenContractStubDelegate2.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = DelegateeAddress, + DelegateInfoList = { delegateInfo2 } + }); + + var sizeFeeSymbolList = await SetMethodOrSizeFeeAsync(basicFeeSymbol, sizeFeeSymbol, basicFee); + + var chargeTransactionFeesInput = new ChargeTransactionFeesInput + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + TransactionSizeFee = sizeFee, + }; + chargeTransactionFeesInput.SymbolsToPayTxSizeFee.AddRange(sizeFeeSymbolList.SymbolsToPayTxSizeFee); + var delegationResult = + await TokenContractStubDelegate1.GetTransactionFeeDelegationsOfADelegatee.CallAsync( + new GetTransactionFeeDelegationsOfADelegateeInput + { + DelegateeAddress = DelegateeAddress, + DelegatorAddress = DefaultSender + }); + var delegationResult2 = + await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = Delegatee2Address, + DelegatorAddress = DelegateeAddress, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + var chargeFeeRetDefault = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + if (chargeFeeRetDefault.Transaction.RefBlockNumber >= delegationResult.BlockHeight + 2 && + chargeFeeRetDefault.Transaction.RefBlockNumber >= delegationResult2.BlockHeight + 2) + { + chargeFeeRetDefault.Output.Success.ShouldBe(true); + + //no change + var afterBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = DefaultSender + }); + afterBalance.Balance.ShouldBe(10); + + //no change + var afterDelegateeBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = DelegateeAddress + }); + afterDelegateeBalance.Balance.ShouldBe(20); + + var afterSecondaryDelegateeBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = Delegatee2Address + }); + afterSecondaryDelegateeBalance.Balance.ShouldBe(afterBalanceDelegatee); + var delegation = await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = Delegatee2Address, + DelegatorAddress = DelegateeAddress, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + delegation.Delegations[basicFeeSymbol].ShouldBe(afterDelegateeAmount); + } + else + { + chargeFeeRetDefault.Output.Success.ShouldBe(false); + + var delegation = await TokenContractStubDelegate1.GetTransactionFeeDelegationsOfADelegatee.CallAsync( + new GetTransactionFeeDelegationsOfADelegateeInput + { + DelegateeAddress = DelegateeAddress, + DelegatorAddress = DefaultSender + }); + delegation.Delegations[NativeTokenSymbol].ShouldBe(5); + var delegation2 = await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = Delegatee2Address, + DelegatorAddress = DelegateeAddress, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + delegation2.Delegations[basicFeeSymbol].ShouldBe(delegateeAmountBasic); + } + } + + [Theory] + [InlineData(10, 20, 100, 10, 10, 20, 20, 100, 100, 80, 80, 20)] + public async Task ChargeTransactionFee_DelegationNew_UnlimitedDelegate_Success( + long initialBalance, long initialDelegateeBalance, long initialDelegatee2Balance, + long baseFeeBalance, long sizeFeeBalance, long baseFeeDelegateBalance, long sizeFeeDelegateBalance, + long baseFeeDelegate2Balance, long sizeFeeDelegate2Balance, + long basicFee, long sizeFee, long afterBalanceDelegatee) + { + var basicFeeSymbol = "BASIC"; + var sizeFeeSymbol = "SIZE"; + + await SetPrimaryTokenSymbolAsync(); + await CreateTokenAsync(DefaultSender, basicFeeSymbol); + await CreateTokenAsync(DefaultSender, sizeFeeSymbol); + + await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, initialBalance); + await IssueTokenToUserAsync(NativeTokenSymbol, initialDelegateeBalance, DelegateeAddress); + await IssueTokenToUserAsync(NativeTokenSymbol, initialDelegatee2Balance, Delegatee2Address); + if (baseFeeBalance != 0 && sizeFeeBalance != 0 && + baseFeeDelegateBalance != 0 && sizeFeeDelegateBalance != 0 && + baseFeeDelegate2Balance != 0 && sizeFeeDelegate2Balance != 0) + { + await IssueTokenToDefaultSenderAsync(basicFeeSymbol, baseFeeBalance); + await IssueTokenToDefaultSenderAsync(sizeFeeSymbol, sizeFeeBalance); + await IssueTokenToUserAsync(basicFeeSymbol, baseFeeDelegateBalance, DelegateeAddress); + await IssueTokenToUserAsync(sizeFeeSymbol, sizeFeeDelegateBalance, DelegateeAddress); + await IssueTokenToUserAsync(basicFeeSymbol, baseFeeDelegate2Balance, Delegatee2Address); + await IssueTokenToUserAsync(sizeFeeSymbol, sizeFeeDelegate2Balance, Delegatee2Address); + } + + var delegations = new Dictionary + { + [NativeTokenSymbol] = 5, + }; + var delegateInfo2 = new DelegateInfo + { + IsUnlimitedDelegate = true, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + }; + await TokenContractStubDelegate1.SetTransactionFeeDelegations.SendAsync( + new SetTransactionFeeDelegationsInput + { + DelegatorAddress = DefaultSender, + Delegations = { delegations } + }); + await TokenContractStubDelegate2.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = DelegateeAddress, + DelegateInfoList = { delegateInfo2 } + }); + + var sizeFeeSymbolList = await SetMethodOrSizeFeeAsync(basicFeeSymbol, sizeFeeSymbol, basicFee); + + var chargeTransactionFeesInput = new ChargeTransactionFeesInput + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + TransactionSizeFee = sizeFee, + }; + chargeTransactionFeesInput.SymbolsToPayTxSizeFee.AddRange(sizeFeeSymbolList.SymbolsToPayTxSizeFee); + var delegationResult = + await TokenContractStubDelegate1.GetTransactionFeeDelegationsOfADelegatee.CallAsync( + new GetTransactionFeeDelegationsOfADelegateeInput + { + DelegateeAddress = DelegateeAddress, + DelegatorAddress = DefaultSender + }); + var delegationResult2 = + await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = Delegatee2Address, + DelegatorAddress = DelegateeAddress, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + var chargeFeeRetDefault = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + if (chargeFeeRetDefault.Transaction.RefBlockNumber >= delegationResult.BlockHeight + 2 && + chargeFeeRetDefault.Transaction.RefBlockNumber >= delegationResult2.BlockHeight + 2) + { + chargeFeeRetDefault.Output.Success.ShouldBe(true); + + //no change + var afterBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = DefaultSender + }); + afterBalance.Balance.ShouldBe(10); + + //no change + var afterDelegateeBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = DelegateeAddress + }); + afterDelegateeBalance.Balance.ShouldBe(20); + + var afterSecondaryDelegateeBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = basicFeeSymbol, + Owner = Delegatee2Address + }); + afterSecondaryDelegateeBalance.Balance.ShouldBe(afterBalanceDelegatee); + } + else + { + chargeFeeRetDefault.Output.Success.ShouldBe(false); + + var delegation = await TokenContractStubDelegate1.GetTransactionFeeDelegationsOfADelegatee.CallAsync( + new GetTransactionFeeDelegationsOfADelegateeInput + { + DelegateeAddress = DelegateeAddress, + DelegatorAddress = DefaultSender + }); + delegation.Delegations[NativeTokenSymbol].ShouldBe(5); + } + } + + [Fact] + public async Task ChargeTransactionFee_DelegationNew_MultiDelegate_Success() + { + var basicFeeSymbol = "BASIC"; + var sizeFeeSymbol = "SIZE"; + + await SetPrimaryTokenSymbolAsync(); + await CreateTokenAsync(DefaultSender, basicFeeSymbol); + await CreateTokenAsync(DefaultSender, sizeFeeSymbol); + + var tokenList = new List { NativeTokenSymbol, basicFeeSymbol, sizeFeeSymbol }; + await IssueTokenListToUserAsync(tokenList, 10, DefaultSender); + await IssueTokenListToUserAsync(tokenList, 20, DelegateeAddress); + await IssueTokenListToUserAsync(tokenList, 100, Delegatee2Address); + await IssueTokenListToUserAsync(tokenList, 20, Delegatee3Address); + await IssueTokenListToUserAsync(tokenList, 20, SecondaryDelegatee1Address); + await IssueTokenListToUserAsync(tokenList, 100, SecondaryDelegatee2Address); + await IssueTokenListToUserAsync(tokenList, 20, SecondaryDelegatee3Address); + await IssueTokenListToUserAsync(tokenList, 20, SecondaryDelegatee4Address); + await IssueTokenListToUserAsync(tokenList, 200, SecondaryDelegatee5Address); + await IssueTokenListToUserAsync(tokenList, 100, SecondaryDelegatee6Address); + + //first delegate + //delegatee1 -> default sender, balance is not enough + //delegatee2 -> default sender, delegation is not enough + //delegatee3 -> default sender, balance and delegation is not enough + var delegations1 = new Dictionary + { + [NativeTokenSymbol] = 100, + [basicFeeSymbol] = 100, + [sizeFeeSymbol] = 100, + }; + var delegations2 = new Dictionary + { + [NativeTokenSymbol] = 10 + }; + var delegations3 = new Dictionary + { + [NativeTokenSymbol] = 30, + [basicFeeSymbol] = 100 + }; + var delegateInfo1 = new DelegateInfo + { + IsUnlimitedDelegate = false, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + Delegations = { delegations1 } + }; + var delegateInfo2 = new DelegateInfo + { + IsUnlimitedDelegate = false, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + Delegations = { delegations2 } + }; + var delegateInfo3 = new DelegateInfo + { + IsUnlimitedDelegate = false, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + Delegations = { delegations3 } + }; + await TokenContractStubDelegate1.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = DefaultSender, + DelegateInfoList = { delegateInfo1 }, + }); + await TokenContractStubDelegate2.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = DefaultSender, + DelegateInfoList = { delegateInfo2 } + }); + await TokenContractStubDelegate3.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = DefaultSender, + DelegateInfoList = { delegateInfo3 } + }); + //second delegate + //secondary delegatee1 -> delegatee1, balance is not enough + //secondary delegatee2 -> delegatee1, delegation is not enough + //secondary delegatee3 -> delegatee1, balance and delegation is not enough + //secondary delegatee4 -> delegatee2, balance is not enough + //secondary delegatee5 -> delegatee2, success + //secondary delegatee6 -> delegatee3 + await TokenContractStubSecondaryDelegate1.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = DelegateeAddress, + DelegateInfoList = { delegateInfo1 } + }); + await TokenContractStubSecondaryDelegate2.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = DelegateeAddress, + DelegateInfoList = { delegateInfo2 } + }); + await TokenContractStubSecondaryDelegate3.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = DelegateeAddress, + DelegateInfoList = { delegateInfo3 } + }); + await TokenContractStubSecondaryDelegate4.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = Delegatee2Address, + DelegateInfoList = { delegateInfo1 } + }); + await TokenContractStubSecondaryDelegate5.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = Delegatee2Address, + DelegateInfoList = { delegateInfo1 } + }); + await TokenContractStubSecondaryDelegate6.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = Delegatee3Address, + DelegateInfoList = { delegateInfo3 } + }); + + var sizeFeeSymbolList = await SetMethodOrSizeFeeAsync(basicFeeSymbol, sizeFeeSymbol, 80); + var chargeTransactionFeesInput = new ChargeTransactionFeesInput + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + TransactionSizeFee = 80, + }; + chargeTransactionFeesInput.SymbolsToPayTxSizeFee.AddRange(sizeFeeSymbolList.SymbolsToPayTxSizeFee); + + var delegateList = new List<(Address, Address)> + { + (DelegateeAddress, DefaultSender), + (Delegatee2Address, DefaultSender), + (Delegatee3Address, DefaultSender), + (SecondaryDelegatee1Address, DelegateeAddress), + (SecondaryDelegatee2Address, DelegateeAddress), + (SecondaryDelegatee3Address, DelegateeAddress), + (SecondaryDelegatee4Address, Delegatee2Address), + (SecondaryDelegatee5Address, Delegatee2Address), + (SecondaryDelegatee6Address, Delegatee3Address), + (DelegateeAddress, DefaultSender), + (DelegateeAddress, DefaultSender), + (DelegateeAddress, DefaultSender) + }; + + var chargeFeeRetDefault = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + var result = await GetSetDelegationResultAsync(delegateList, chargeFeeRetDefault.Transaction.RefBlockNumber); + if (result) + { + chargeFeeRetDefault.Output.Success.ShouldBe(true); + await CheckUserBalanceAsync(NativeTokenSymbol, DefaultSender, 10); + await CheckUserBalanceAsync(basicFeeSymbol, DelegateeAddress, 20); + await CheckUserBalanceAsync(basicFeeSymbol, Delegatee2Address, 100); + await CheckUserBalanceAsync(NativeTokenSymbol, Delegatee3Address, 20); + await CheckUserBalanceAsync(basicFeeSymbol, SecondaryDelegatee1Address, 20); + await CheckUserBalanceAsync(basicFeeSymbol, SecondaryDelegatee2Address, 100); + await CheckUserBalanceAsync(NativeTokenSymbol, SecondaryDelegatee3Address, 20); + await CheckUserBalanceAsync(NativeTokenSymbol, SecondaryDelegatee4Address, 20); + //change + await CheckUserBalanceAsync(basicFeeSymbol, SecondaryDelegatee5Address, 120); + await CheckUserBalanceAsync(NativeTokenSymbol, SecondaryDelegatee6Address, 100); + var delegateInfo = + await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = SecondaryDelegatee5Address, + DelegatorAddress = Delegatee2Address, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + delegateInfo.Delegations[basicFeeSymbol].ShouldBe(20); + var delegation = await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = SecondaryDelegatee3Address, + DelegatorAddress = DelegateeAddress, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + delegation.Delegations[NativeTokenSymbol].ShouldBe(30); + delegation.Delegations[basicFeeSymbol].ShouldBe(100); + } + else + { + chargeFeeRetDefault.Output.Success.ShouldBe(false); + var delegateInfo = + await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = SecondaryDelegatee5Address, + DelegatorAddress = Delegatee2Address, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + delegateInfo.Delegations[basicFeeSymbol].ShouldBe(100); + } + } + + [Fact] + public async Task SetPrimaryTokenSymbol_InvalidInput_Test() + { + { + var result = + await TokenContractStub.SetPrimaryTokenSymbol.SendWithExceptionAsync(new SetPrimaryTokenSymbolInput()); + result.TransactionResult.Error.ShouldContain("Invalid input symbol."); + } + { + await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, 1000000); + var result = await TokenContractStub.Transfer.SendWithExceptionAsync(new TransferInput + { + Symbol = NativeTokenSymbol, + Amount = 100 + }); + result.TransactionResult.Error.ShouldContain("Invalid input address."); + } + { + var result = await TokenContractStub.Lock.SendWithExceptionAsync(new LockInput()); + result.TransactionResult.Error.ShouldContain("Invalid input symbol."); + } + { + var result = await TokenContractStub.Lock.SendWithExceptionAsync(new LockInput + { + Symbol = NativeTokenSymbol + }); + result.TransactionResult.Error.ShouldContain("Invalid input address."); + } + { + var result = await TokenContractStub.Unlock.SendWithExceptionAsync(new UnlockInput()); + result.TransactionResult.Error.ShouldContain("Invalid input symbol."); + } + { + var result = await TokenContractStub.Unlock.SendWithExceptionAsync(new UnlockInput + { + Symbol = NativeTokenSymbol + }); + result.TransactionResult.Error.ShouldContain("Invalid input address."); + } + { + var result = await TokenContractStub.TransferFrom.SendWithExceptionAsync(new TransferFromInput + { + Symbol = NativeTokenSymbol, + Amount = 100 + }); + result.TransactionResult.Error.ShouldContain("Invalid input address."); + } + { + var result = await TokenContractStub.TransferFrom.SendWithExceptionAsync(new TransferFromInput + { + Symbol = NativeTokenSymbol, + Amount = 100, + From = DefaultSender + }); + result.TransactionResult.Error.ShouldContain("Invalid input address."); + } + { + var result = await TokenContractStub.Approve.SendWithExceptionAsync(new ApproveInput()); + result.TransactionResult.Error.ShouldContain("Invalid input address."); + } + { + var result = await TokenContractStub.UnApprove.SendWithExceptionAsync(new UnApproveInput()); + result.TransactionResult.Error.ShouldContain("Invalid input address."); + } + { + var result = await TokenContractStub.CheckThreshold.SendWithExceptionAsync(new CheckThresholdInput()); + result.TransactionResult.Error.ShouldContain("Invalid input address."); + } + { + var result = + await TokenContractImplStub.AdvanceResourceToken + .SendWithExceptionAsync(new AdvanceResourceTokenInput()); + result.TransactionResult.Error.ShouldContain("Invalid input address."); + } + { + var result = + await TokenContractImplStub.TakeResourceTokenBack.SendWithExceptionAsync( + new TakeResourceTokenBackInput()); + result.TransactionResult.Error.ShouldContain("Invalid input resource token symbol."); + } + { + var result = await TokenContractImplStub.TakeResourceTokenBack.SendWithExceptionAsync( + new TakeResourceTokenBackInput + { + ResourceTokenSymbol = NativeTokenSymbol + }); + result.TransactionResult.Error.ShouldContain("Invalid input address."); + } + { + var result = + await TokenContractImplStub.ValidateTokenInfoExists.SendWithExceptionAsync( + new ValidateTokenInfoExistsInput()); + result.TransactionResult.Error.ShouldContain("Invalid input symbol."); + } + { + var result = + await TokenContractImplStub.SetTransactionFeeDelegations.SendWithExceptionAsync( + new SetTransactionFeeDelegationsInput()); + result.TransactionResult.Error.ShouldContain("Invalid input address."); + } + { + var result = + await TokenContractImplStub.SetTransactionFeeDelegations.SendWithExceptionAsync( + new SetTransactionFeeDelegationsInput()); + result.TransactionResult.Error.ShouldContain("Invalid input address."); + } + { + var result = + await TokenContractImplStub.UpdateCoefficientsForContract.SendWithExceptionAsync( + new UpdateCoefficientsInput()); + result.TransactionResult.Error.ShouldContain("Invalid input coefficients."); + } + { + var result = + await TokenContractImplStub.UpdateCoefficientsForSender.SendWithExceptionAsync( + new UpdateCoefficientsInput()); + result.TransactionResult.Error.ShouldContain("Invalid input coefficients."); + } + } + + private async Task IssueTokenListToUserAsync(List symbols, long amount, Address address) + { + foreach (var symbol in symbols) + { + await IssueTokenToUserAsync(symbol, amount, address); + } + } + + private async Task GetSetDelegationResultAsync(List<(Address, Address)> delegateAddressList, + long refBlockHeight) + { + var result = true; + foreach (var (delegateeAddress, delegatorAddress) in delegateAddressList) + { + var delegateInfo = + await TokenContractStubDelegate1.GetTransactionFeeDelegateInfo.CallAsync( + new GetTransactionFeeDelegateInfoInput + { + DelegateeAddress = delegateeAddress, + DelegatorAddress = delegatorAddress, + ContractAddress = TokenContractAddress, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer) + }); + result = result && refBlockHeight >= delegateInfo.BlockHeight + 2; + } + + return result; + } + + private async Task CheckUserBalanceAsync(string symbol, Address owner, long balance) + { + var userBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Symbol = symbol, + Owner = owner + }); + userBalance.Balance.ShouldBe(balance); + } + + private async Task SetMethodOrSizeFeeAsync(string basicFeeSymbol, string sizeFeeSymbol, + long basicFee) + { + var methodFee = new MethodFees + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + Fees = + { + new MethodFee + { + Symbol = basicFeeSymbol, + BasicFee = basicFee + } + } + }; + await TokenContractImplStub.SetMethodFee.SendAsync(methodFee); + + var sizeFeeSymbolList = new SymbolListToPayTxSizeFee + { + SymbolsToPayTxSizeFee = + { + new SymbolToPayTxSizeFee + { + TokenSymbol = sizeFeeSymbol, + AddedTokenWeight = 1, + BaseTokenWeight = 1 + }, + new SymbolToPayTxSizeFee + { + TokenSymbol = NativeTokenSymbol, + AddedTokenWeight = 1, + BaseTokenWeight = 1 + } + } + }; + + await TokenContractImplStub.SetSymbolsToPayTxSizeFee.SendAsync(sizeFeeSymbolList); + + return sizeFeeSymbolList; + } + + + private async Task GetTokenSupplyAmount(string tokenSymbol) + { + var tokenInfo = await TokenContractStub.GetTokenInfo.CallAsync(new GetTokenInfoInput + { + Symbol = tokenSymbol + }); + return tokenInfo.Supply; + } + + private async Task> GetDefaultBalancesAsync(string[] tokenSymbolList) + { + var balances = new List(); + foreach (var symbol in tokenSymbolList) + balances.Add(await GetBalanceAsync(DefaultSender, symbol)); + return balances; + } + + private async Task CreateTokenAsync(Address creator, string tokenSymbol, bool isBurned = true) + { + await TokenContractStub.Create.SendAsync(new CreateInput + { + Symbol = tokenSymbol, + TokenName = tokenSymbol + " name", + TotalSupply = 1000_00000000, + IsBurnable = isBurned, + Issuer = creator, + Owner = creator + }); + } + + private async Task IssueTokenToDefaultSenderAsync(string tokenSymbol, long amount) + { + if (amount <= 0) return; + var issueResult = await TokenContractStub.Issue.SendAsync(new IssueInput() + { + Symbol = tokenSymbol, + Amount = amount, + To = DefaultSender, + }); + issueResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + } + + private async Task IssueTokenToUserAsync(string tokenSymbol, long amount, Address to) + { + var issueResult = await TokenContractStub.Issue.SendAsync(new IssueInput() + { + Symbol = tokenSymbol, + Amount = amount, + To = to, + }); + issueResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); } private async Task GetBalanceAsync(Address address, string tokenSymbol) diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutePluginTransactionDirectlyTest_FreeAllowance.cs b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutePluginTransactionDirectlyTest_FreeAllowance.cs new file mode 100644 index 0000000000..0cdb3ab91e --- /dev/null +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutePluginTransactionDirectlyTest_FreeAllowance.cs @@ -0,0 +1,2047 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using AElf.Contracts.MultiToken; +using AElf.CSharp.Core.Extension; +using AElf.Standards.ACS1; +using AElf.Types; +using Google.Protobuf.WellKnownTypes; +using Shouldly; +using Xunit; + +namespace AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests; + +public partial class ExecutePluginTransactionDirectlyTest +{ + [Fact] + public async Task ConfigTransactionFeeFreeAllowances_Test() + { + await SetPrimaryTokenSymbolAsync(); + + await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + Amount = 0 + } + } + }, + RefreshSeconds = 600, + Threshold = 0 + } + } + }); + + { + var config = await TokenContractImplStub.GetTransactionFeeFreeAllowancesConfig.CallAsync(new Empty()); + config.Value.Count.ShouldBe(1); + config.Value.First().Symbol.ShouldBe(NativeTokenSymbol); + config.Value.First().Threshold.ShouldBe(0); + config.Value.First().RefreshSeconds.ShouldBe(600); + config.Value.First().FreeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + config.Value.First().FreeAllowances.Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + config.Value.First().FreeAllowances.Map.Values.First().Amount.ShouldBe(0); + + var freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(0); + } + + await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, 2_00000000); + await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + Amount = 1_00000000 + } + } + }, + RefreshSeconds = 600, + Threshold = 1_00000000 + } + } + }); + + { + var config = await TokenContractImplStub.GetTransactionFeeFreeAllowancesConfig.CallAsync(new Empty()); + config.Value.Count.ShouldBe(1); + config.Value.First().Symbol.ShouldBe(NativeTokenSymbol); + config.Value.First().Threshold.ShouldBe(1_00000000); + config.Value.First().RefreshSeconds.ShouldBe(600); + config.Value.First().FreeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + config.Value.First().FreeAllowances.Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + config.Value.First().FreeAllowances.Map.Values.First().Amount.ShouldBe(1_00000000); + + var freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(1_00000000); + } + } + + [Fact] + public async Task ConfigTransactionFeeFreeAllowances_Unauthorized_Test() + { + var result = + await TokenContractImplStub2.ConfigTransactionFeeFreeAllowances.SendWithExceptionAsync( + new ConfigTransactionFeeFreeAllowancesInput()); + result.TransactionResult.Error.ShouldContain("Unauthorized behavior."); + } + + [Fact] + public async Task ConfigTransactionFeeFreeAllowances_MultipleTokens_OneByOne_Test() + { + await SetPrimaryTokenSymbolAsync(); + await CreateTokenAndIssueAsync(); + + await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + Amount = 1_00000000 + } + } + }, + RefreshSeconds = 600, + Threshold = 1_00000000 + } + } + }); + + { + var config = await TokenContractImplStub.GetTransactionFeeFreeAllowancesConfig.CallAsync(new Empty()); + config.Value.Count.ShouldBe(1); + config.Value.First().Symbol.ShouldBe(NativeTokenSymbol); + config.Value.First().Threshold.ShouldBe(1_00000000); + config.Value.First().RefreshSeconds.ShouldBe(600); + config.Value.First().FreeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + config.Value.First().FreeAllowances.Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + config.Value.First().FreeAllowances.Map.Values.First().Amount.ShouldBe(1_00000000); + + var userAFreeAllowances = TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(UserAAddress); + userAFreeAllowances.Result.Map.Keys.First().ShouldBe(NativeTokenSymbol); + userAFreeAllowances.Result.Map.Values.First().Map.Keys.First().ShouldBe(NativeTokenSymbol); + userAFreeAllowances.Result.Map.Values.First().Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + userAFreeAllowances.Result.Map.Values.First().Map.Values.First().Amount.ShouldBe(1_00000000); + var userBFreeAllowances = TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(UserBAddress); + userBFreeAllowances.Result.Map.Count.ShouldBe(0); + var userCFreeAllowances = TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(UserCAddress); + userCFreeAllowances.Result.Map.ShouldBe(userAFreeAllowances.Result.Map); + } + + await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance + { + Symbol = USDT, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + Amount = 1_00000000 + } + } + }, + RefreshSeconds = 300, + Threshold = 1_000000 + } + } + }); + + { + var config = await TokenContractImplStub.GetTransactionFeeFreeAllowancesConfig.CallAsync(new Empty()); + config.Value.Count.ShouldBe(2); + config.Value.First().Symbol.ShouldBe(NativeTokenSymbol); + config.Value.First().Threshold.ShouldBe(1_00000000); + config.Value.First().RefreshSeconds.ShouldBe(600); + config.Value.First().FreeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + config.Value.First().FreeAllowances.Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + config.Value.First().FreeAllowances.Map.Values.First().Amount.ShouldBe(1_00000000); + + config.Value.Last().Symbol.ShouldBe(USDT); + config.Value.Last().Threshold.ShouldBe(1_000000); + config.Value.Last().RefreshSeconds.ShouldBe(300); + config.Value.Last().FreeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + config.Value.Last().FreeAllowances.Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + config.Value.Last().FreeAllowances.Map.Values.First().Amount.ShouldBe(1_00000000); + + var userAFreeAllowances = TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(UserAAddress); + userAFreeAllowances.Result.Map.Count.ShouldBe(1); + userAFreeAllowances.Result.Map.Keys.First().ShouldBe(NativeTokenSymbol); + userAFreeAllowances.Result.Map.Values.First().Map.Keys.First().ShouldBe(NativeTokenSymbol); + userAFreeAllowances.Result.Map.Values.First().Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + userAFreeAllowances.Result.Map.Values.First().Map.Values.First().Amount.ShouldBe(1_00000000); + var userBFreeAllowances = TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(UserBAddress); + userBFreeAllowances.Result.Map.Count.ShouldBe(1); + userBFreeAllowances.Result.Map.Keys.First().ShouldBe(USDT); + userBFreeAllowances.Result.Map.Values.First().Map.Keys.First().ShouldBe(NativeTokenSymbol); + userBFreeAllowances.Result.Map.Values.First().Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + userBFreeAllowances.Result.Map.Values.First().Map.Values.First().Amount.ShouldBe(1_00000000); + var userCFreeAllowances = TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(UserCAddress); + userAFreeAllowances.Result.Map.Add(USDT, userBFreeAllowances.Result.Map.Values.First()); + userCFreeAllowances.Result.Map.ShouldBe(userAFreeAllowances.Result.Map); + } + } + + [Fact] + public async Task ConfigTransactionFeeFreeAllowances_MultipleTokens_AtOnce_Test() + { + await SetPrimaryTokenSymbolAsync(); + await CreateTokenAndIssueAsync(); + + await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + Amount = 1_00000000 + } + } + }, + RefreshSeconds = 600, + Threshold = 1_00000000 + }, + new ConfigTransactionFeeFreeAllowance + { + Symbol = USDT, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + Amount = 1_00000000 + } + } + }, + RefreshSeconds = 300, + Threshold = 1_000000 + } + } + }); + + var config = await TokenContractImplStub.GetTransactionFeeFreeAllowancesConfig.CallAsync(new Empty()); + config.Value.Count.ShouldBe(2); + config.Value.First().Symbol.ShouldBe(NativeTokenSymbol); + config.Value.First().Threshold.ShouldBe(1_00000000); + config.Value.First().RefreshSeconds.ShouldBe(600); + config.Value.First().FreeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + config.Value.First().FreeAllowances.Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + config.Value.First().FreeAllowances.Map.Values.First().Amount.ShouldBe(1_00000000); + + config.Value.Last().Symbol.ShouldBe(USDT); + config.Value.Last().Threshold.ShouldBe(1_000000); + config.Value.Last().RefreshSeconds.ShouldBe(300); + config.Value.Last().FreeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + config.Value.Last().FreeAllowances.Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + config.Value.Last().FreeAllowances.Map.Values.First().Amount.ShouldBe(1_00000000); + + var userAFreeAllowances = TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(UserAAddress); + userAFreeAllowances.Result.Map.Count.ShouldBe(1); + userAFreeAllowances.Result.Map.Keys.First().ShouldBe(NativeTokenSymbol); + userAFreeAllowances.Result.Map.Values.First().Map.Keys.First().ShouldBe(NativeTokenSymbol); + userAFreeAllowances.Result.Map.Values.First().Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + userAFreeAllowances.Result.Map.Values.First().Map.Values.First().Amount.ShouldBe(1_00000000); + var userBFreeAllowances = TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(UserBAddress); + userBFreeAllowances.Result.Map.Count.ShouldBe(1); + userBFreeAllowances.Result.Map.Keys.First().ShouldBe(USDT); + userBFreeAllowances.Result.Map.Values.First().Map.Keys.First().ShouldBe(NativeTokenSymbol); + userBFreeAllowances.Result.Map.Values.First().Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + userBFreeAllowances.Result.Map.Values.First().Map.Values.First().Amount.ShouldBe(1_00000000); + var userCFreeAllowances = TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(UserCAddress); + userAFreeAllowances.Result.Map.Add(USDT, userBFreeAllowances.Result.Map.Values.First()); + userCFreeAllowances.Result.Map.ShouldBe(userAFreeAllowances.Result.Map); + } + + [Fact] + public async Task ConfigTransactionFeeFreeAllowances_MultipleTokens_Modify_Test() + { + await ConfigTransactionFeeFreeAllowances_MultipleTokens_AtOnce_Test(); + await CreateTokenAsync(DefaultSender, "ABC"); + + await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance + { + Symbol = USDT, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + Amount = 2_00000000 + }, + new TransactionFeeFreeAllowance + { + Symbol = "ABC", + Amount = 2_00000000 + } + } + }, + RefreshSeconds = 1200, + Threshold = 2_000000 + } + } + }); + + var config = await TokenContractImplStub.GetTransactionFeeFreeAllowancesConfig.CallAsync(new Empty()); + config.Value.Count.ShouldBe(2); + config.Value.First().Symbol.ShouldBe(NativeTokenSymbol); + config.Value.First().Threshold.ShouldBe(1_00000000); + config.Value.First().RefreshSeconds.ShouldBe(600); + config.Value.First().FreeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + config.Value.First().FreeAllowances.Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + config.Value.First().FreeAllowances.Map.Values.First().Amount.ShouldBe(1_00000000); + + config.Value.Last().Symbol.ShouldBe(USDT); + config.Value.Last().Threshold.ShouldBe(2_000000); + config.Value.Last().RefreshSeconds.ShouldBe(1200); + config.Value.Last().FreeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + config.Value.Last().FreeAllowances.Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + config.Value.Last().FreeAllowances.Map.Values.First().Amount.ShouldBe(2_00000000); + config.Value.Last().FreeAllowances.Map.Keys.Last().ShouldBe("ABC"); + config.Value.Last().FreeAllowances.Map.Values.Last().Symbol.ShouldBe("ABC"); + config.Value.Last().FreeAllowances.Map.Values.Last().Amount.ShouldBe(2_00000000); + + var userAFreeAllowances = TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(UserAAddress); + userAFreeAllowances.Result.Map.Count.ShouldBe(1); + userAFreeAllowances.Result.Map.Keys.First().ShouldBe(NativeTokenSymbol); + userAFreeAllowances.Result.Map.Values.First().Map.Keys.First().ShouldBe(NativeTokenSymbol); + userAFreeAllowances.Result.Map.Values.First().Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + userAFreeAllowances.Result.Map.Values.First().Map.Values.First().Amount.ShouldBe(1_00000000); + var userBFreeAllowances = TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(UserBAddress); + userBFreeAllowances.Result.Map.Count.ShouldBe(0); + var userCFreeAllowances = TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(UserCAddress); + userCFreeAllowances.Result.Map.ShouldBe(userAFreeAllowances.Result.Map); + } + + [Fact] + public async Task ConfigTransactionFeeFreeAllowances_InvalidInput_Test() + { + var message = await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendWithExceptionAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance() + } + }); + message.TransactionResult.Error.ShouldContain("Invalid input symbol"); + + message = await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendWithExceptionAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance + { + Symbol = "TEST" + } + } + }); + message.TransactionResult.Error.ShouldContain("Symbol TEST not exist"); + + message = await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendWithExceptionAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol + } + } + }); + message.TransactionResult.Error.ShouldContain("Invalid input allowances"); + + message = await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendWithExceptionAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + Amount = 1_00000000 + } + } + }, + Threshold = -1 + } + } + }); + message.TransactionResult.Error.ShouldContain("Invalid input threshold"); + + message = await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendWithExceptionAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + Amount = 1_00000000 + } + } + }, + Threshold = 1_00000000, + RefreshSeconds = -1 + } + } + }); + message.TransactionResult.Error.ShouldContain("Invalid input refresh seconds"); + } + + [Fact] + public async Task RemoveTransactionFeeFreeAllowancesConfig_Unauthorized_Test() + { + var result = await TokenContractImplStub2.RemoveTransactionFeeFreeAllowancesConfig.SendWithExceptionAsync( + new RemoveTransactionFeeFreeAllowancesConfigInput()); + result.TransactionResult.Error.ShouldContain("Unauthorized behavior."); + } + + [Fact] + public async Task RemoveTransactionFeeFreeAllowancesConfig_Test() + { + await ConfigTransactionFeeFreeAllowances_MultipleTokens_AtOnce_Test(); + await IssueTokenToUserAsync(USDT, 1_000000, UserAAddress); + + var userAFreeAllowances = TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(UserAAddress); + userAFreeAllowances.Result.Map.Count.ShouldBe(2); + var config = TokenContractImplStub.GetTransactionFeeFreeAllowancesConfig.CallAsync(new Empty()); + config.Result.Value.Count.ShouldBe(2); + + await TokenContractImplStub.RemoveTransactionFeeFreeAllowancesConfig.SendAsync( + new RemoveTransactionFeeFreeAllowancesConfigInput + { + Symbols = { USDT } + }); + config = TokenContractImplStub.GetTransactionFeeFreeAllowancesConfig.CallAsync(new Empty()); + config.Result.Value.Count.ShouldBe(1); + config.Result.Value.First().Symbol.ShouldBe(NativeTokenSymbol); + userAFreeAllowances = TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(UserAAddress); + userAFreeAllowances.Result.Map.Count.ShouldBe(1); + userAFreeAllowances.Result.Map.Keys.First().ShouldBe(NativeTokenSymbol); + + // symbol not exist + await TokenContractImplStub.RemoveTransactionFeeFreeAllowancesConfig.SendAsync( + new RemoveTransactionFeeFreeAllowancesConfigInput + { + Symbols = { USDT } + }); + config = TokenContractImplStub.GetTransactionFeeFreeAllowancesConfig.CallAsync(new Empty()); + config.Result.Value.Count.ShouldBe(1); + config.Result.Value.First().Symbol.ShouldBe(NativeTokenSymbol); + userAFreeAllowances = TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(UserAAddress); + userAFreeAllowances.Result.Map.Count.ShouldBe(1); + userAFreeAllowances.Result.Map.Keys.First().ShouldBe(NativeTokenSymbol); + + // Duplicate symbols + await TokenContractImplStub.RemoveTransactionFeeFreeAllowancesConfig.SendAsync( + new RemoveTransactionFeeFreeAllowancesConfigInput + { + Symbols = { NativeTokenSymbol, NativeTokenSymbol } + }); + config = TokenContractImplStub.GetTransactionFeeFreeAllowancesConfig.CallAsync(new Empty()); + config.Result.Value.Count.ShouldBe(0); + userAFreeAllowances = TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(UserAAddress); + userAFreeAllowances.Result.Map.Count.ShouldBe(0); + } + + [Fact] + public async Task RemoveTransactionFeeFreeAllowancesConfig_MultipleTokens_AtOnce_Test() + { + await ConfigTransactionFeeFreeAllowances_MultipleTokens_AtOnce_Test(); + await IssueTokenToUserAsync(USDT, 1_000000, UserAAddress); + + var userAFreeAllowances = TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(UserAAddress); + userAFreeAllowances.Result.Map.Count.ShouldBe(2); + var config = TokenContractImplStub.GetTransactionFeeFreeAllowancesConfig.CallAsync(new Empty()); + config.Result.Value.Count.ShouldBe(2); + + await TokenContractImplStub.RemoveTransactionFeeFreeAllowancesConfig.SendAsync( + new RemoveTransactionFeeFreeAllowancesConfigInput + { + Symbols = { NativeTokenSymbol, USDT } + }); + userAFreeAllowances = TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(UserAAddress); + userAFreeAllowances.Result.Map.Count.ShouldBe(0); + } + + [Fact] + public async Task RemoveTransactionFeeFreeAllowancesConfig_InvalidInput_Test() + { + var message = await TokenContractImplStub.RemoveTransactionFeeFreeAllowancesConfig.SendWithExceptionAsync( + new RemoveTransactionFeeFreeAllowancesConfigInput + { + Symbols = { USDT } + }); + message.TransactionResult.Error.ShouldContain("Method fee free allowances config not set"); + + await ConfigTransactionFeeFreeAllowances_MultipleTokens_AtOnce_Test(); + message = await TokenContractImplStub.RemoveTransactionFeeFreeAllowancesConfig.SendWithExceptionAsync( + new RemoveTransactionFeeFreeAllowancesConfigInput()); + message.TransactionResult.Error.ShouldContain("Invalid input"); + } + + [Theory] + [InlineData(1000, 0, 1000, 0, 100, 0, 0, 0, 800, 0, 1000, 200)] + [InlineData(1000, 0, 100, 0, 100, 0, 0, 0, 0, 0, 900, 200)] + [InlineData(1000, 1000, 500, 10, 100, 500, 20, 100, 300, 500, 1000, 200)] + [InlineData(1000, 1000, 150, 10, 100, 150, 20, 100, 0, 100, 1000, 200)] + public async Task ChargeTransactionFee_Test(long initialELFBalance, long initialUSDTBalance, + long freeAmountELF, long refreshSecondsELF, long thresholdELF, long freeAmountUSDT, long refreshSecondsUSDT, + long thresholdUSDT, long newFreeAmountELF, long newFreeAmountUSDT, long afterBalance, long basicFee) + { + await SetPrimaryTokenSymbolAsync(); + await CreateTokenAsync(DefaultSender, USDT); + + await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, initialELFBalance); + await IssueTokenToDefaultSenderAsync(USDT, initialUSDTBalance); + + await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + Amount = freeAmountELF + } + } + }, + RefreshSeconds = refreshSecondsELF, + Threshold = thresholdELF + }, + new ConfigTransactionFeeFreeAllowance + { + Symbol = USDT, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + Amount = freeAmountUSDT + } + } + }, + RefreshSeconds = refreshSecondsUSDT, + Threshold = thresholdUSDT + } + } + }); + + var methodFee = new MethodFees + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + Fees = + { + new MethodFee + { + Symbol = NativeTokenSymbol, + BasicFee = basicFee + } + } + }; + await TokenContractImplStub.SetMethodFee.SendAsync(methodFee); + + var freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(freeAmountELF); + freeAllowances.Map.Keys.Last().ShouldBe(USDT); + freeAllowances.Map.Values.Last().Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.Last().Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.Last().Map.Values.First().Amount.ShouldBe(freeAmountUSDT); + + var chargeTransactionFeesInput = new ChargeTransactionFeesInput + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress + }; + + var chargeFeeRet = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + chargeFeeRet.Output.Success.ShouldBe(true); + + freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(newFreeAmountELF); + freeAllowances.Map.Keys.Last().ShouldBe(USDT); + freeAllowances.Map.Values.Last().Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.Last().Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.Last().Map.Values.First().Amount.ShouldBe(newFreeAmountUSDT); + + await CheckDefaultSenderTokenAsync(NativeTokenSymbol, afterBalance); + } + + [Theory] + // case 21 + [InlineData(1000, 1000, 1000, 1000, 1000, 10, 100, 1000, 20, 100, 700, 500, 1000, 1000, 1000, 1000, 300, 500)] + // case 22 + [InlineData(1000, 1000, 1000, 1000, 100, 10, 100, 1000, 20, 100, 0, 500, 100, 1000, 1000, 1000, 1000, 500)] + // case 25 + [InlineData(1000, 100, 1000, 1000, 1000, 10, 10000, 1000, 20, 100, 1000, 500, 0, 1000, 1000, 100, 1000, 500)] + public async Task ChargeTransactionFee_MultipleTokens_Test(long initialELFBalance, long initialUSDTBalance, + long initialToken1Balance, long initialToken2Balance, long freeAmountELF, long refreshSecondsELF, + long thresholdELF, long freeAmountUSDT, long refreshSecondsUSDT, long thresholdUSDT, long newFreeAmountELF, + long newFreeAmountUSDT, long afterToken1Balance, long afterToken2Balance, long afterELFBalance, + long afterUSDTBalance, long sizeFee, long basicFee) + { + await SetPrimaryTokenSymbolAsync(); + await CreateTokenAsync(DefaultSender, USDT); + + await CreateTokenAsync(DefaultSender, Token1); + await CreateTokenAsync(DefaultSender, Token2); + + await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, initialELFBalance); + await IssueTokenToDefaultSenderAsync(USDT, initialUSDTBalance); + await IssueTokenToDefaultSenderAsync(Token1, initialToken1Balance); + await IssueTokenToDefaultSenderAsync(Token2, initialToken2Balance); + + await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = Token1, + Amount = freeAmountELF + } + } + }, + RefreshSeconds = refreshSecondsELF, + Threshold = thresholdELF + }, + new ConfigTransactionFeeFreeAllowance + { + Symbol = USDT, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = Token2, + Amount = freeAmountUSDT + } + } + }, + RefreshSeconds = refreshSecondsUSDT, + Threshold = thresholdUSDT + } + } + }); + + var methodFee = new MethodFees + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + Fees = + { + new MethodFee + { + Symbol = Token2, + BasicFee = basicFee + } + } + }; + await TokenContractImplStub.SetMethodFee.SendAsync(methodFee); + + var sizeFeeSymbolList = new SymbolListToPayTxSizeFee + { + SymbolsToPayTxSizeFee = + { + new SymbolToPayTxSizeFee + { + TokenSymbol = Token1, + AddedTokenWeight = 1, + BaseTokenWeight = 1 + }, + new SymbolToPayTxSizeFee + { + TokenSymbol = NativeTokenSymbol, + AddedTokenWeight = 1, + BaseTokenWeight = 1 + } + } + }; + await TokenContractImplStub.SetSymbolsToPayTxSizeFee.SendAsync(sizeFeeSymbolList); + + var freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + if (initialELFBalance >= thresholdELF) + { + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(freeAmountELF); + } + + if (initialUSDTBalance >= thresholdUSDT) + { + freeAllowances.Map.Keys.Last().ShouldBe(USDT); + freeAllowances.Map.Values.Last().Map.Keys.First().ShouldBe(Token2); + freeAllowances.Map.Values.Last().Map.Values.First().Symbol.ShouldBe(Token2); + freeAllowances.Map.Values.Last().Map.Values.First().Amount.ShouldBe(freeAmountUSDT); + } + + var chargeTransactionFeesInput = new ChargeTransactionFeesInput + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + TransactionSizeFee = sizeFee, + }; + chargeTransactionFeesInput.SymbolsToPayTxSizeFee.AddRange(sizeFeeSymbolList.SymbolsToPayTxSizeFee); + + var chargeFeeRet = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + chargeFeeRet.Output.Success.ShouldBe(initialToken1Balance + freeAmountELF >= sizeFee); + + + freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + if (initialELFBalance >= thresholdELF) + { + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(newFreeAmountELF); + } + + if (initialUSDTBalance >= thresholdUSDT) + { + freeAllowances.Map.Keys.Last().ShouldBe(USDT); + freeAllowances.Map.Values.Last().Map.Keys.First().ShouldBe(Token2); + freeAllowances.Map.Values.Last().Map.Values.First().Symbol.ShouldBe(Token2); + freeAllowances.Map.Values.Last().Map.Values.First().Amount.ShouldBe(newFreeAmountUSDT); + } + + await CheckDefaultSenderTokenAsync(Token1, afterToken1Balance); + await CheckDefaultSenderTokenAsync(Token2, afterToken2Balance); + await CheckDefaultSenderTokenAsync(NativeTokenSymbol, afterELFBalance); + await CheckDefaultSenderTokenAsync(USDT, afterUSDTBalance); + } + + [Theory] + [InlineData(1000, 600, 200, 200)] + public async Task ChargeTransactionFee_NoFreeAllowance_Test(long initialBalance, long afterBalance, long sizeFee, + long basicFee) + { + await SetPrimaryTokenSymbolAsync(); + + await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, initialBalance); + + var methodFee = new MethodFees + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + Fees = + { + new MethodFee + { + Symbol = NativeTokenSymbol, + BasicFee = basicFee + } + } + }; + await TokenContractImplStub.SetMethodFee.SendAsync(methodFee); + + var sizeFeeSymbolList = new SymbolListToPayTxSizeFee + { + SymbolsToPayTxSizeFee = + { + new SymbolToPayTxSizeFee + { + TokenSymbol = NativeTokenSymbol, + AddedTokenWeight = 1, + BaseTokenWeight = 1 + } + } + }; + await TokenContractImplStub.SetSymbolsToPayTxSizeFee.SendAsync(sizeFeeSymbolList); + + var chargeTransactionFeesInput = new ChargeTransactionFeesInput + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + TransactionSizeFee = sizeFee, + }; + chargeTransactionFeesInput.SymbolsToPayTxSizeFee.AddRange(sizeFeeSymbolList.SymbolsToPayTxSizeFee); + + var chargeFeeRet = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + chargeFeeRet.Output.Success.ShouldBe(true); + + await CheckDefaultSenderTokenAsync(NativeTokenSymbol, afterBalance); + } + + [Theory] + [InlineData(1000, 1000, 1000, 2000, 2000, 0, 1000, 1000, 500, 1000, 1000, 1000, 1500)] + public async Task ChargeTransactionFee_SingleThreshold_Test(long initialBalance, long initialToken1Balance, + long initialToken2Balance, long freeAmountTokenA, long freeAmountTokenB, long refreshSeconds, + long threshold, long newFreeAmountTokenA, long newFreeAmountTokenB, long afterToken1Balance, + long afterToken2Balance, long sizeFee, long basicFee) + { + await SetPrimaryTokenSymbolAsync(); + + await CreateTokenAsync(DefaultSender, Token1); + await CreateTokenAsync(DefaultSender, Token2); + + await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, initialBalance); + await IssueTokenToDefaultSenderAsync(Token1, initialToken1Balance); + await IssueTokenToDefaultSenderAsync(Token2, initialToken2Balance); + + await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = Token1, + Amount = freeAmountTokenA + }, + new TransactionFeeFreeAllowance + { + Symbol = Token2, + Amount = freeAmountTokenB + } + } + }, + RefreshSeconds = refreshSeconds, + Threshold = threshold + } + } + }); + + var methodFee = new MethodFees + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + Fees = + { + new MethodFee + { + Symbol = Token2, + BasicFee = basicFee + } + } + }; + await TokenContractImplStub.SetMethodFee.SendAsync(methodFee); + + var sizeFeeSymbolList = new SymbolListToPayTxSizeFee + { + SymbolsToPayTxSizeFee = + { + new SymbolToPayTxSizeFee + { + TokenSymbol = Token1, + AddedTokenWeight = 1, + BaseTokenWeight = 1 + }, + new SymbolToPayTxSizeFee + { + TokenSymbol = NativeTokenSymbol, + AddedTokenWeight = 1, + BaseTokenWeight = 1 + } + } + }; + await TokenContractImplStub.SetSymbolsToPayTxSizeFee.SendAsync(sizeFeeSymbolList); + + var freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(freeAmountTokenA); + + freeAllowances.Map.Values.First().Map.Keys.Last().ShouldBe(Token2); + freeAllowances.Map.Values.First().Map.Values.Last().Symbol.ShouldBe(Token2); + freeAllowances.Map.Values.First().Map.Values.Last().Amount.ShouldBe(freeAmountTokenB); + + var chargeTransactionFeesInput = new ChargeTransactionFeesInput + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + TransactionSizeFee = sizeFee, + }; + chargeTransactionFeesInput.SymbolsToPayTxSizeFee.AddRange(sizeFeeSymbolList.SymbolsToPayTxSizeFee); + + var chargeFeeRet = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + chargeFeeRet.Output.Success.ShouldBe(true); + + + freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(newFreeAmountTokenA); + + freeAllowances.Map.Values.First().Map.Keys.Last().ShouldBe(Token2); + freeAllowances.Map.Values.First().Map.Values.Last().Symbol.ShouldBe(Token2); + freeAllowances.Map.Values.First().Map.Values.Last().Amount.ShouldBe(newFreeAmountTokenB); + + await CheckDefaultSenderTokenAsync(Token1, afterToken1Balance); + await CheckDefaultSenderTokenAsync(NativeTokenSymbol, afterToken2Balance); + } + + [Theory] + [InlineData(1000, 1000, 1000, 1000, 1000, 10, 100, 1000, 20, 100, 0, 1000, 0, 1000, 3000, 5000)] + public async Task ChargeTransactionFee_NotEnough_Test(long initialELFBalance, long initialUSDTBalance, + long initialToken1Balance, long initialToken2Balance, long freeAmountELF, long refreshSecondsELF, + long thresholdELF, long freeAmountUSDT, long refreshSecondsUSDT, long thresholdUSDT, long newFreeAmountELF, + long newFreeAmountUSDT, long afterToken1Balance, long afterToken2Balance, long sizeFee, long basicFee) + { + await SetPrimaryTokenSymbolAsync(); + await CreateTokenAsync(DefaultSender, USDT); + + await CreateTokenAsync(DefaultSender, Token1); + await CreateTokenAsync(DefaultSender, Token2); + + await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, initialELFBalance); + await IssueTokenToDefaultSenderAsync(USDT, initialUSDTBalance); + await IssueTokenToDefaultSenderAsync(Token1, initialToken1Balance); + await IssueTokenToDefaultSenderAsync(Token2, initialToken2Balance); + + await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = Token1, + Amount = freeAmountELF + } + } + }, + RefreshSeconds = refreshSecondsELF, + Threshold = thresholdELF + }, + new ConfigTransactionFeeFreeAllowance + { + Symbol = USDT, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = Token2, + Amount = freeAmountUSDT + } + } + }, + RefreshSeconds = refreshSecondsUSDT, + Threshold = thresholdUSDT + } + } + }); + + var methodFee = new MethodFees + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + Fees = + { + new MethodFee + { + Symbol = Token1, + BasicFee = basicFee + } + } + }; + await TokenContractImplStub.SetMethodFee.SendAsync(methodFee); + + var sizeFeeSymbolList = new SymbolListToPayTxSizeFee + { + SymbolsToPayTxSizeFee = + { + new SymbolToPayTxSizeFee + { + TokenSymbol = Token1, + AddedTokenWeight = 1, + BaseTokenWeight = 1 + }, + new SymbolToPayTxSizeFee + { + TokenSymbol = NativeTokenSymbol, + AddedTokenWeight = 1, + BaseTokenWeight = 1 + } + } + }; + await TokenContractImplStub.SetSymbolsToPayTxSizeFee.SendAsync(sizeFeeSymbolList); + + var freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(freeAmountELF); + freeAllowances.Map.Keys.Last().ShouldBe(USDT); + freeAllowances.Map.Values.Last().Map.Keys.First().ShouldBe(Token2); + freeAllowances.Map.Values.Last().Map.Values.First().Symbol.ShouldBe(Token2); + freeAllowances.Map.Values.Last().Map.Values.First().Amount.ShouldBe(freeAmountUSDT); + + var chargeTransactionFeesInput = new ChargeTransactionFeesInput + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + TransactionSizeFee = sizeFee, + }; + chargeTransactionFeesInput.SymbolsToPayTxSizeFee.AddRange(sizeFeeSymbolList.SymbolsToPayTxSizeFee); + + var chargeFeeRet = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + chargeFeeRet.Output.Success.ShouldBe(false); + + + freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + + freeAllowances.Map.Keys.Last().ShouldBe(USDT); + freeAllowances.Map.Values.Last().Map.Keys.First().ShouldBe(Token2); + freeAllowances.Map.Values.Last().Map.Values.First().Symbol.ShouldBe(Token2); + freeAllowances.Map.Values.Last().Map.Values.First().Amount.ShouldBe(newFreeAmountUSDT); + + await CheckDefaultSenderTokenAsync(Token1, afterToken1Balance); + await CheckDefaultSenderTokenAsync(Token2, afterToken2Balance); + } + + [Theory] + [InlineData(10000, 500, 50, 10000, 300, 100, 100, 100, 2000, 10000, 7800, 10000)] + public async Task ChargeTransactionFee_ClearFreeAllowance_Test(long initialBalance, long freeAmount, + long refreshSeconds, long threshold, long firstFreeAmount, long secondFreeAmount, long sizeFee, long basicFee, + long transferAmount, long firstBalance, long secondBalance, long thirdBalance) + { + await SetPrimaryTokenSymbolAsync(); + + await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, initialBalance); + + await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + Amount = freeAmount + } + } + }, + RefreshSeconds = refreshSeconds, + Threshold = threshold + } + } + }); + + var methodFee = new MethodFees + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + Fees = + { + new MethodFee + { + Symbol = NativeTokenSymbol, + BasicFee = basicFee + } + } + }; + await TokenContractImplStub.SetMethodFee.SendAsync(methodFee); + + var sizeFeeSymbolList = new SymbolListToPayTxSizeFee + { + SymbolsToPayTxSizeFee = + { + new SymbolToPayTxSizeFee + { + TokenSymbol = NativeTokenSymbol, + AddedTokenWeight = 1, + BaseTokenWeight = 1 + } + } + }; + await TokenContractImplStub.SetSymbolsToPayTxSizeFee.SendAsync(sizeFeeSymbolList); + + var freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(freeAmount); + + var chargeTransactionFeesInput = new ChargeTransactionFeesInput + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + TransactionSizeFee = sizeFee, + }; + chargeTransactionFeesInput.SymbolsToPayTxSizeFee.AddRange(sizeFeeSymbolList.SymbolsToPayTxSizeFee); + + var chargeFeeRet = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + chargeFeeRet.Output.Success.ShouldBe(true); + + freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(firstFreeAmount); + + await CheckDefaultSenderTokenAsync(NativeTokenSymbol, firstBalance); + + await TokenContractStub.Transfer.SendAsync(new TransferInput + { + Amount = transferAmount, + Symbol = NativeTokenSymbol, + To = UserCAddress, + Memo = "test" + }); + + chargeFeeRet = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + chargeFeeRet.Output.Success.ShouldBe(true); + + await CheckDefaultSenderTokenAsync(NativeTokenSymbol, secondBalance); + + freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + freeAllowances.Map.ShouldBeEmpty(); + + await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, transferAmount + sizeFee + basicFee); + + chargeFeeRet = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + chargeFeeRet.Output.Success.ShouldBe(true); + + await CheckDefaultSenderTokenAsync(NativeTokenSymbol, thirdBalance); + + freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(secondFreeAmount); + } + + [Fact] + public async Task ChargeTransactionFee_RefreshTime_Test() + { + await SetPrimaryTokenSymbolAsync(); + await CreateTokenAsync(DefaultSender, USDT); + + await CreateTokenAsync(DefaultSender, Token1); + await CreateTokenAsync(DefaultSender, Token2); + + await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, 10000); + await IssueTokenToDefaultSenderAsync(USDT, 10000); + await IssueTokenToDefaultSenderAsync(Token1, 10000); + await IssueTokenToDefaultSenderAsync(Token2, 10000); + + await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = Token1, + Amount = 1000 + } + } + }, + RefreshSeconds = 20, + Threshold = 100 + }, + new ConfigTransactionFeeFreeAllowance + { + Symbol = USDT, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = Token2, + Amount = 1000 + } + } + }, + RefreshSeconds = 10, + Threshold = 100 + } + } + }); + + var methodFee = new MethodFees + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + Fees = + { + new MethodFee + { + Symbol = Token2, + BasicFee = 500 + } + } + }; + await TokenContractImplStub.SetMethodFee.SendAsync(methodFee); + + var sizeFeeSymbolList = new SymbolListToPayTxSizeFee + { + SymbolsToPayTxSizeFee = + { + new SymbolToPayTxSizeFee + { + TokenSymbol = Token1, + AddedTokenWeight = 1, + BaseTokenWeight = 1 + }, + new SymbolToPayTxSizeFee + { + TokenSymbol = NativeTokenSymbol, + AddedTokenWeight = 1, + BaseTokenWeight = 1 + } + } + }; + await TokenContractImplStub.SetSymbolsToPayTxSizeFee.SendAsync(sizeFeeSymbolList); + + var freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(1000); + + freeAllowances.Map.Keys.Last().ShouldBe(USDT); + freeAllowances.Map.Values.Last().Map.Keys.First().ShouldBe(Token2); + freeAllowances.Map.Values.Last().Map.Values.First().Symbol.ShouldBe(Token2); + freeAllowances.Map.Values.Last().Map.Values.First().Amount.ShouldBe(1000); + + var chargeTransactionFeesInput = new ChargeTransactionFeesInput + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + TransactionSizeFee = 1000 + }; + chargeTransactionFeesInput.SymbolsToPayTxSizeFee.AddRange(sizeFeeSymbolList.SymbolsToPayTxSizeFee); + + var chargeFeeRet = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + chargeFeeRet.Output.Success.ShouldBe(true); + + freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(0); + + freeAllowances.Map.Keys.Last().ShouldBe(USDT); + freeAllowances.Map.Values.Last().Map.Keys.First().ShouldBe(Token2); + freeAllowances.Map.Values.Last().Map.Values.First().Symbol.ShouldBe(Token2); + freeAllowances.Map.Values.Last().Map.Values.First().Amount.ShouldBe(500); + + await CheckDefaultSenderTokenAsync(NativeTokenSymbol, 10000); + await CheckDefaultSenderTokenAsync(USDT, 10000); + await CheckDefaultSenderTokenAsync(Token1, 10000); + await CheckDefaultSenderTokenAsync(Token2, 10000); + + chargeFeeRet = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + chargeFeeRet.Output.Success.ShouldBe(true); + + freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(0); + + freeAllowances.Map.Keys.Last().ShouldBe(USDT); + freeAllowances.Map.Values.Last().Map.Keys.First().ShouldBe(Token2); + freeAllowances.Map.Values.Last().Map.Values.First().Symbol.ShouldBe(Token2); + freeAllowances.Map.Values.Last().Map.Values.First().Amount.ShouldBe(0); + + await CheckDefaultSenderTokenAsync(NativeTokenSymbol, 10000); + await CheckDefaultSenderTokenAsync(USDT, 10000); + await CheckDefaultSenderTokenAsync(Token1, 9000); + await CheckDefaultSenderTokenAsync(Token2, 10000); + + BlockTimeProvider.SetBlockTime(TimestampHelper.GetUtcNow().AddSeconds(10)); + + chargeFeeRet = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + chargeFeeRet.Output.Success.ShouldBe(true); + + freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(0); + + freeAllowances.Map.Keys.Last().ShouldBe(USDT); + freeAllowances.Map.Values.Last().Map.Keys.First().ShouldBe(Token2); + freeAllowances.Map.Values.Last().Map.Values.First().Symbol.ShouldBe(Token2); + freeAllowances.Map.Values.Last().Map.Values.First().Amount.ShouldBe(500); + + await CheckDefaultSenderTokenAsync(NativeTokenSymbol, 10000); + await CheckDefaultSenderTokenAsync(USDT, 10000); + await CheckDefaultSenderTokenAsync(Token1, 8000); + await CheckDefaultSenderTokenAsync(Token2, 10000); + } + + [Theory] + [InlineData(1000, 1000, 1000, 0, 10, 800, 1000, 1000, 100, 100)] + public async Task ChargeTransactionFee_FreeAllowanceFirst_Test(long initialBalance, long initialToken1Balance, + long freeAmount, long refreshSeconds, long threshold, long newFreeAmount, long afterToken1Balance, + long afterELFBalance, long sizeFee, long basicFee) + { + await SetPrimaryTokenSymbolAsync(); + + await CreateTokenAsync(DefaultSender, Token1); + + await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, initialBalance); + await IssueTokenToDefaultSenderAsync(Token1, initialToken1Balance); + + await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = Token1, + Amount = freeAmount + } + } + }, + RefreshSeconds = refreshSeconds, + Threshold = threshold + } + } + }); + + var methodFee = new MethodFees + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + Fees = + { + new MethodFee + { + Symbol = NativeTokenSymbol, + BasicFee = basicFee + }, + new MethodFee + { + Symbol = Token1, + BasicFee = basicFee + } + } + }; + await TokenContractImplStub.SetMethodFee.SendAsync(methodFee); + + var sizeFeeSymbolList = new SymbolListToPayTxSizeFee + { + SymbolsToPayTxSizeFee = + { + new SymbolToPayTxSizeFee + { + TokenSymbol = NativeTokenSymbol, + AddedTokenWeight = 1, + BaseTokenWeight = 1 + }, + new SymbolToPayTxSizeFee + { + TokenSymbol = Token1, + AddedTokenWeight = 1, + BaseTokenWeight = 1 + } + } + }; + await TokenContractImplStub.SetSymbolsToPayTxSizeFee.SendAsync(sizeFeeSymbolList); + + var freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(freeAmount); + + var chargeTransactionFeesInput = new ChargeTransactionFeesInput + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + TransactionSizeFee = sizeFee, + }; + chargeTransactionFeesInput.SymbolsToPayTxSizeFee.AddRange(sizeFeeSymbolList.SymbolsToPayTxSizeFee); + + var chargeFeeRet = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + chargeFeeRet.Output.Success.ShouldBe(true); + + freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(newFreeAmount); + + await CheckDefaultSenderTokenAsync(Token1, afterToken1Balance); + await CheckDefaultSenderTokenAsync(NativeTokenSymbol, afterELFBalance); + } + + [Theory] + // case 24 + [InlineData(1000, 1000, 1000, 1000, Token1, 1000, Token1, 1000, 600, 10, Token2, 1000, Token2, 1000, 300, 10, 1000, + 1000, 1000, 1000, 0, 0, 1000, 1000, 5000, 1, 1, USDT, 3000, Token2, 3000, false)] + // case 28 + [InlineData(10000, 10000, 1000, 0, Token1, 2000, NativeTokenSymbol, 2000, 600, 10000, Token1, 1000, USDT, 200, 300, + 10000, 2000, 0, 0, 200, 10000, 10000, 1000, 0, 2000, 1, 2, Token1, 1000, USDT, 200, true)] + // case 29 + [InlineData(10000, 10000, 1000, 0, Token1, 2000, NativeTokenSymbol, 1000, 600, 10000, Token1, 1000, USDT, 100, 300, + 10000, 1000, 1000, 0, 100, 10000, 10000, 1000, 0, 2000, 1, 2, Token1, 1000, USDT, 200, true)] + // case 30 + [InlineData(10000, 10000, 1000, 0, Token1, 1000, NativeTokenSymbol, 1000, 600, 100, Token1, 1000, USDT, 100, 300, + 100, 500, 0, 0, 100, 9000, 10000, 1000, 0, 2000, 1, 1, Token1, 1500, USDT, 200, true)] + // case 31 + [InlineData(10000, 10000, 10000, 0, Token1, 1000, NativeTokenSymbol, 1000, 600, 100, Token1, 1000, + NativeTokenSymbol, 1000, 300, 100, 0, 0, 0, 0, 9000, 10000, 7000, 0, 3000, 5, 3, NativeTokenSymbol, 3000, + Token1, 3000, true)] + // case 32 + [InlineData(10000, 10000, 1000, 0, Token1, 1000, NativeTokenSymbol, 1000, 600, 100, Token1, 1000, NativeTokenSymbol, + 1000, 300, 100, 0, 0, 0, 0, 0, 10000, 0, 0, 3000, 5, 3, NativeTokenSymbol, 15000, USDT, 20000, false)] + // case 33 + [InlineData(5000, 10000, 0, 0, Token1, 1000, Token1, 1000, 600, 100, Token2, 1000, Token2, 1000, 300, 100, 0, 0, + 1000, 1000, 0, 8000, 0, 0, 10000, 1, 5, USDT, 2000, NativeTokenSymbol, 0, false)] + [InlineData(2000, 0, 0, 0, Token1, 0, NativeTokenSymbol, 0, 0, 10000, Token2, 0, Token2, 0, 0, 10000, 0, 0, 0, 0, + 500, 0, 0, 0, 0, 1, 1, NativeTokenSymbol, 1500, USDT, 100, true)] + public async Task ChargeTransactionFee_MultipleTxFeeTokens_Test(long initialELFBalance, long initialUSDTBalance, + long initialToken1Balance, long initialToken2Balance, string firstFreeSymbolELF, long firstFreeAmountELF, + string secondFreeSymbolELF, long secondFreeAmountELF, long refreshSecondsELF, long thresholdELF, + string firstFreeSymbolUSDT, long firstFreeAmountUSDT, string secondFreeSymbolUSDT, long secondFreeAmountUSDT, + long refreshSecondsUSDT, long thresholdUSDT, long newFirstFreeAmountELF, long newSecondFreeAmountELF, + long newFirstFreeAmountUSDT, long newSecondFreeAmountUSDT, long afterELFBalance, long afterUSDTBalance, + long afterToken1Balance, long afterToken2Balance, long sizeFee, int addedTokenWeight, int baseTokenWeight, + string firstBaseFeeSymbol, long firstBaseFeeAmount, string secondBaseFeeSymbol, long secondBaseFeeAmount, + bool chargeFeeResult) + { + await SetPrimaryTokenSymbolAsync(); + await CreateTokenAsync(DefaultSender, USDT); + + await CreateTokenAsync(DefaultSender, Token1); + await CreateTokenAsync(DefaultSender, Token2); + + await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, initialELFBalance); + await IssueTokenToDefaultSenderAsync(USDT, initialUSDTBalance); + await IssueTokenToDefaultSenderAsync(Token1, initialToken1Balance); + await IssueTokenToDefaultSenderAsync(Token2, initialToken2Balance); + + await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = firstFreeSymbolELF, + Amount = firstFreeAmountELF + }, + new TransactionFeeFreeAllowance + { + Symbol = secondFreeSymbolELF, + Amount = secondFreeAmountELF + } + } + }, + RefreshSeconds = refreshSecondsELF, + Threshold = thresholdELF + }, + new ConfigTransactionFeeFreeAllowance + { + Symbol = USDT, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = firstFreeSymbolUSDT, + Amount = firstFreeAmountUSDT + }, + new TransactionFeeFreeAllowance + { + Symbol = secondFreeSymbolUSDT, + Amount = secondFreeAmountUSDT + } + } + }, + RefreshSeconds = refreshSecondsUSDT, + Threshold = thresholdUSDT + } + } + }); + + var methodFee = new MethodFees + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + Fees = + { + new MethodFee + { + Symbol = firstBaseFeeSymbol, + BasicFee = firstBaseFeeAmount + } + } + }; + + if (secondBaseFeeAmount > 0) + { + methodFee.Fees.Add(new MethodFee + { + Symbol = secondBaseFeeSymbol, + BasicFee = secondBaseFeeAmount + }); + } + + await TokenContractImplStub.SetMethodFee.SendAsync(methodFee); + + var sizeFeeSymbolList = new SymbolListToPayTxSizeFee + { + SymbolsToPayTxSizeFee = + { + new SymbolToPayTxSizeFee + { + TokenSymbol = NativeTokenSymbol, + AddedTokenWeight = 1, + BaseTokenWeight = 1 + }, + new SymbolToPayTxSizeFee + { + TokenSymbol = Token1, + AddedTokenWeight = addedTokenWeight, + BaseTokenWeight = baseTokenWeight + } + } + }; + await TokenContractImplStub.SetSymbolsToPayTxSizeFee.SendAsync(sizeFeeSymbolList); + + var freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + if (initialELFBalance >= thresholdELF) + { + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(firstFreeSymbolELF); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(firstFreeSymbolELF); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(firstFreeAmountELF); + + freeAllowances.Map.Values.First().Map.Keys.Last().ShouldBe(secondFreeSymbolELF); + freeAllowances.Map.Values.First().Map.Values.Last().Symbol.ShouldBe(secondFreeSymbolELF); + freeAllowances.Map.Values.First().Map.Values.Last().Amount.ShouldBe(secondFreeAmountELF); + } + + if (initialUSDTBalance >= thresholdUSDT) + { + freeAllowances.Map.Keys.Last().ShouldBe(USDT); + freeAllowances.Map.Values.Last().Map.Keys.First().ShouldBe(firstFreeSymbolUSDT); + freeAllowances.Map.Values.Last().Map.Values.First().Symbol.ShouldBe(firstFreeSymbolUSDT); + freeAllowances.Map.Values.Last().Map.Values.First().Amount.ShouldBe(firstFreeAmountUSDT); + + freeAllowances.Map.Values.Last().Map.Keys.Last().ShouldBe(secondFreeSymbolUSDT); + freeAllowances.Map.Values.Last().Map.Values.Last().Symbol.ShouldBe(secondFreeSymbolUSDT); + freeAllowances.Map.Values.Last().Map.Values.Last().Amount.ShouldBe(secondFreeAmountUSDT); + } + + var chargeTransactionFeesInput = new ChargeTransactionFeesInput + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + TransactionSizeFee = sizeFee, + }; + chargeTransactionFeesInput.SymbolsToPayTxSizeFee.AddRange(sizeFeeSymbolList.SymbolsToPayTxSizeFee); + + var chargeFeeRet = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + chargeFeeRet.Output.Success.ShouldBe(chargeFeeResult); + + freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DefaultSender); + if (afterELFBalance >= thresholdELF) + { + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(firstFreeSymbolELF); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(firstFreeSymbolELF); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(newFirstFreeAmountELF); + + freeAllowances.Map.Values.First().Map.Keys.Last().ShouldBe(secondFreeSymbolELF); + freeAllowances.Map.Values.First().Map.Values.Last().Symbol.ShouldBe(secondFreeSymbolELF); + freeAllowances.Map.Values.First().Map.Values.Last().Amount.ShouldBe(newSecondFreeAmountELF); + } + + if (afterUSDTBalance >= thresholdUSDT) + { + freeAllowances.Map.Keys.Last().ShouldBe(USDT); + freeAllowances.Map.Values.Last().Map.Keys.First().ShouldBe(firstFreeSymbolUSDT); + freeAllowances.Map.Values.Last().Map.Values.First().Symbol.ShouldBe(firstFreeSymbolUSDT); + freeAllowances.Map.Values.Last().Map.Values.First().Amount.ShouldBe(newFirstFreeAmountUSDT); + + freeAllowances.Map.Values.Last().Map.Keys.Last().ShouldBe(secondFreeSymbolUSDT); + freeAllowances.Map.Values.Last().Map.Values.Last().Symbol.ShouldBe(secondFreeSymbolUSDT); + freeAllowances.Map.Values.Last().Map.Values.Last().Amount.ShouldBe(newSecondFreeAmountUSDT); + } + + await CheckDefaultSenderTokenAsync(Token1, afterToken1Balance); + await CheckDefaultSenderTokenAsync(Token2, afterToken2Balance); + await CheckDefaultSenderTokenAsync(NativeTokenSymbol, afterELFBalance); + await CheckDefaultSenderTokenAsync(USDT, afterUSDTBalance); + } + + [Theory] + [InlineData(100, 100, 100)] + public async Task ChargeTransactionFee_DelegateMultipleFeeTokens_PriorityAllowance_Success_Test( + long initialELFBalance, long initialUSDTBalance, + long initialToken1Balance) + { + await SetPrimaryTokenSymbolAsync(); + await CreateTokenAsync(DefaultSender, USDT); + await CreateTokenAsync(DefaultSender, Token1); + + await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, 5); + await IssueTokenToDefaultSenderAsync(USDT, 5); + await IssueTokenToDefaultSenderAsync(Token1, 5); + await IssueTokenToUserAsync(NativeTokenSymbol, initialELFBalance, DelegateeAddress); + await IssueTokenToUserAsync(USDT, initialUSDTBalance, DelegateeAddress); + await IssueTokenToUserAsync(Token1, initialToken1Balance, DelegateeAddress); + + await SetDelegateeAsync(); + + await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + Amount = 5 + }, + new TransactionFeeFreeAllowance + { + Symbol = USDT, + Amount = 30 + }, + new TransactionFeeFreeAllowance + { + Symbol = Token1, + Amount = 10 + } + } + }, + RefreshSeconds = 600, + Threshold = 100 + } + } + }); + var methodFee = new MethodFees + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + Fees = + { + new MethodFee + { + Symbol = NativeTokenSymbol, + BasicFee = 6 + }, + new MethodFee + { + Symbol = Token1, + BasicFee = 7 + } + } + }; + + await TokenContractImplStub.SetMethodFee.SendAsync(methodFee); + + var sizeFeeSymbolList = new SymbolListToPayTxSizeFee + { + SymbolsToPayTxSizeFee = + { + new SymbolToPayTxSizeFee + { + TokenSymbol = NativeTokenSymbol, + AddedTokenWeight = 1, + BaseTokenWeight = 1 + }, + new SymbolToPayTxSizeFee + { + TokenSymbol = USDT, + AddedTokenWeight = 3, + BaseTokenWeight = 1 + } + } + }; + await TokenContractImplStub.SetSymbolsToPayTxSizeFee.SendAsync(sizeFeeSymbolList); + + var freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DelegateeAddress); + if (initialELFBalance >= 100) + { + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(5); + + freeAllowances.Map.Values.First().Map.Keys.ElementAt(1).ShouldBe(USDT); + freeAllowances.Map.Values.First().Map.Values.ElementAt(1).Symbol.ShouldBe(USDT); + freeAllowances.Map.Values.First().Map.Values.ElementAt(1).Amount.ShouldBe(30); + + freeAllowances.Map.Values.First().Map.Keys.Last().ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.Last().Symbol.ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.Last().Amount.ShouldBe(10); + } + + var chargeTransactionFeesInput = new ChargeTransactionFeesInput + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + TransactionSizeFee = 7, + }; + chargeTransactionFeesInput.SymbolsToPayTxSizeFee.AddRange(sizeFeeSymbolList.SymbolsToPayTxSizeFee); + + var chargeFeeRet = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + chargeFeeRet.Output.Success.ShouldBe(true); + + freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DelegateeAddress); + var balance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = DelegateeAddress, + Symbol = NativeTokenSymbol + }); + if (balance.Balance >= 100) + { + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(NativeTokenSymbol); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(5); + + freeAllowances.Map.Values.First().Map.Keys.ElementAt(1).ShouldBe(USDT); + freeAllowances.Map.Values.First().Map.Values.ElementAt(1).Symbol.ShouldBe(USDT); + freeAllowances.Map.Values.First().Map.Values.ElementAt(1).Amount.ShouldBe(9); + + freeAllowances.Map.Values.First().Map.Keys.Last().ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.Last().Symbol.ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.Last().Amount.ShouldBe(3); + } + + await CheckUserTokenAsync(DelegateeAddress, Token1, 100); + await CheckUserTokenAsync(DelegateeAddress, NativeTokenSymbol, 100); + await CheckUserTokenAsync(DelegateeAddress, USDT, 100); + } + + [Fact] + public async Task ChargeTransactionFee_DelegateMultipleFeeTokens_PriorityAllowanceGreaterThan0_Success_Test() + { + await SetPrimaryTokenSymbolAsync(); + await CreateTokenAsync(DefaultSender, USDT); + await CreateTokenAsync(DefaultSender, Token1); + + await IssueTokenToDefaultSenderAsync(NativeTokenSymbol, 5); + await IssueTokenToDefaultSenderAsync(USDT, 5); + await IssueTokenToDefaultSenderAsync(Token1, 5); + await IssueTokenToUserAsync(NativeTokenSymbol, 100, DelegateeAddress); + await IssueTokenToUserAsync(USDT, 100, DelegateeAddress); + await IssueTokenToUserAsync(Token1, 100, DelegateeAddress); + + await SetDelegateeAsync(); + + await TokenContractImplStub.ConfigTransactionFeeFreeAllowances.SendAsync( + new ConfigTransactionFeeFreeAllowancesInput + { + Value = + { + new ConfigTransactionFeeFreeAllowance + { + Symbol = NativeTokenSymbol, + TransactionFeeFreeAllowances = new TransactionFeeFreeAllowances + { + Value = + { + new TransactionFeeFreeAllowance + { + Symbol = USDT, + Amount = 10 + }, + new TransactionFeeFreeAllowance + { + Symbol = Token1, + Amount = 10 + } + } + }, + RefreshSeconds = 600, + Threshold = 100 + } + } + }); + var methodFee = new MethodFees + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + Fees = + { + new MethodFee + { + Symbol = NativeTokenSymbol, + BasicFee = 10 + }, + new MethodFee + { + Symbol = Token1, + BasicFee = 30 + } + } + }; + + await TokenContractImplStub.SetMethodFee.SendAsync(methodFee); + + var sizeFeeSymbolList = new SymbolListToPayTxSizeFee + { + SymbolsToPayTxSizeFee = + { + new SymbolToPayTxSizeFee + { + TokenSymbol = NativeTokenSymbol, + AddedTokenWeight = 1, + BaseTokenWeight = 1 + }, + new SymbolToPayTxSizeFee + { + TokenSymbol = USDT, + AddedTokenWeight = 3, + BaseTokenWeight = 1 + } + } + }; + await TokenContractImplStub.SetSymbolsToPayTxSizeFee.SendAsync(sizeFeeSymbolList); + var initialELFBalance = (await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = DelegateeAddress, + Symbol = NativeTokenSymbol + })).Balance; + var freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DelegateeAddress); + if (initialELFBalance >= 100) + { + freeAllowances.Map.Keys.First().ShouldBe(NativeTokenSymbol); + + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(USDT); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(USDT); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(10); + + freeAllowances.Map.Values.First().Map.Keys.Last().ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.Last().Symbol.ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.Last().Amount.ShouldBe(10); + } + + var chargeTransactionFeesInput = new ChargeTransactionFeesInput + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + TransactionSizeFee = 7, + }; + chargeTransactionFeesInput.SymbolsToPayTxSizeFee.AddRange(sizeFeeSymbolList.SymbolsToPayTxSizeFee); + + var chargeFeeRet = await TokenContractStub.ChargeTransactionFees.SendAsync(chargeTransactionFeesInput); + chargeFeeRet.Output.Success.ShouldBe(true); + + freeAllowances = await TokenContractImplStub.GetTransactionFeeFreeAllowances.CallAsync(DelegateeAddress); + var balance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = DelegateeAddress, + Symbol = NativeTokenSymbol + }); + if (balance.Balance >= 100) + { + freeAllowances.Map.Values.First().Map.Keys.First().ShouldBe(USDT); + freeAllowances.Map.Values.First().Map.Values.First().Symbol.ShouldBe(USDT); + freeAllowances.Map.Values.First().Map.Values.First().Amount.ShouldBe(0); + + freeAllowances.Map.Values.First().Map.Keys.Last().ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.Last().Symbol.ShouldBe(Token1); + freeAllowances.Map.Values.First().Map.Values.Last().Amount.ShouldBe(0); + } + + await CheckUserTokenAsync(DelegateeAddress, Token1, 80); + await CheckUserTokenAsync(DelegateeAddress, NativeTokenSymbol, 100); + await CheckUserTokenAsync(DelegateeAddress, USDT, 89); + } + + private async Task SetDelegateeAsync() + { + var delegations = new Dictionary + { + [NativeTokenSymbol] = 100, + [USDT] = 100, + [Token1] = 100 + }; + var delegateInfo1 = new DelegateInfo + { + Delegations = { delegations }, + IsUnlimitedDelegate = false, + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + ContractAddress = TokenContractAddress, + }; + await TokenContractStubDelegate1.SetTransactionFeeDelegateInfos.SendAsync( + new SetTransactionFeeDelegateInfosInput + { + DelegatorAddress = DefaultSender, + DelegateInfoList = { delegateInfo1 } + }); + } + + private async Task CheckDefaultSenderTokenAsync(string symbol, long amount) + { + var balance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = DefaultSender, + Symbol = symbol + }); + + balance.Balance.ShouldBe(amount); + } + + private async Task CheckUserTokenAsync(Address user, string symbol, long amount) + { + var balance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = user, + Symbol = symbol + }); + + balance.Balance.ShouldBe(amount); + } + + private async Task CreateTokenAndIssueAsync() + { + await CreateTokenAsync(DefaultSender, USDT); + await IssueTokenToUserAsync(NativeTokenSymbol, 1_00000000, UserAAddress); + await IssueTokenToUserAsync(USDT, 1_000000, UserBAddress); + await IssueTokenToUserAsync(NativeTokenSymbol, 1_00000000, UserCAddress); + await IssueTokenToUserAsync(USDT, 1_000000, UserCAddress); + } +} \ No newline at end of file diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeTest.cs b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeTest.cs index 006c7697dd..b265f1cca9 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeTest.cs +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeTest.cs @@ -61,7 +61,8 @@ await tokenStub.Create.SendAsync(new CreateInput IsBurnable = true, TokenName = "test token", TotalSupply = 1_000_000_00000000L, - Issuer = DefaultSender + Issuer = DefaultSender, + Owner = DefaultSender }); if (issueAmount != 0) @@ -339,7 +340,7 @@ await tokenContractStub.Transfer.SendAsync(new TransferInput [InlineData(9, 0, 1, 10, 1, 2, "ELF", 9, false)] [InlineData(100000000, 2, 2, 0, 1, 2, "TSA", 1, true)] [InlineData(100000000, 2, 2, 0, 13, 2, "TSB", 2, true)] - [InlineData(100000000, 2, 2, 0, 20, 20, "TSB", 2, false)] + [InlineData(100000000, 2, 2, 0, 20, 20, "TSA", 2, false)] [InlineData(1, 0, 1, 0, 1, 2, "TSB", 1, false)] [InlineData(10, 0, 0, 0, 1, 2, "ELF", 10, false)] // Charge 10 ELFs tx size fee. public async Task ChargeFee_Set_Method_Fees_Tests(long balance1, long balance2, long balance3, long fee1, long fee2, diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeTestBase.cs b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeTestBase.cs index 33fff08c64..62376dcaec 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeTestBase.cs +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeTestBase.cs @@ -10,7 +10,6 @@ using AElf.Cryptography.ECDSA; using AElf.CSharp.Core.Extension; using AElf.Kernel.Blockchain.Application; -using AElf.Kernel.Blockchain.Domain; using AElf.Kernel.Configuration; using AElf.Kernel.Proposal; using AElf.Kernel.SmartContract.Application; @@ -23,18 +22,55 @@ using Microsoft.Extensions.DependencyInjection; using Shouldly; using Volo.Abp.Threading; -using Xunit; namespace AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests; public class ExecutionPluginForMethodFeeTestBase : ContractTestBase { + internal Address ParliamentAddress; + internal ParliamentContractImplContainer.ParliamentContractImplStub ParliamentContractStub; + protected ECKeyPair DefaultSenderKeyPair => Accounts[0].KeyPair; + protected Address DefaultAddress => Accounts[0].Address; + + protected ExecutionPluginForMethodFeeTestBase() + { + AsyncHelper.RunSync(InitializeContracts); + } + + private async Task InitializeContracts() + { + await DeployContractsAsync(); + await InitializedParliament(); + } + + private async Task DeployContractsAsync() + { + const int category = KernelConstants.CodeCoverageRunnerCategory; + // Parliament + { + var code = Codes.Single(kv => kv.Key.Contains("MockParliament")).Value; + ParliamentAddress = await DeploySystemSmartContract(category, code, + ParliamentSmartContractAddressNameProvider.Name, DefaultSenderKeyPair); + ParliamentContractStub = + GetTester(ParliamentAddress, + DefaultSenderKeyPair); + } + } + + private async Task InitializedParliament() + { + await ParliamentContractStub.Initialize.SendAsync(new InitializeInput + { + ProposerAuthorityRequired = false, + PrivilegedProposer = DefaultAddress + }); + } } public class ExecutionPluginForUserContractMethodFeeTestBase : ContractTestBase { protected const string NativeTokenSymbol = "ELF"; - + internal Address ConfigurationAddress; internal Address ParliamentAddress; internal Address ConsensusContractAddress; @@ -46,12 +82,13 @@ public class ExecutionPluginForUserContractMethodFeeTestBase : ContractTestBase< internal AEDPoSContractContainer.AEDPoSContractStub AEDPoSContractStub { get; set; } protected new ISmartContractAddressService ContractAddressService => Application.ServiceProvider.GetRequiredService(); + protected IBlockchainService BlockChainService => Application.ServiceProvider.GetRequiredService(); - + protected ECKeyPair DefaultSenderKeyPair => Accounts[0].KeyPair; protected Address DefaultAddress => Accounts[0].Address; - + protected List InitialCoreDataCenterKeyPairs => Accounts.Take(1).Select(a => a.KeyPair).ToList(); @@ -59,17 +96,19 @@ protected ExecutionPluginForUserContractMethodFeeTestBase() { AsyncHelper.RunSync(InitializeContracts); } + private async Task InitializeContracts() { await DeployContractsAsync(); - AuthorizationContractStub= + AuthorizationContractStub = GetTester(ParliamentAddress, DefaultSenderKeyPair); await InitializeAElfConsensus(); - await InitializedParliament(); + await InitializedParliament(); TokenContractStub = await GetTokenContractStubAsync(); await SetPrimaryTokenSymbolAsync(); } + private async Task
GetTokenContractAddressAsync() { var preBlockHeader = await BlockChainService.GetBestChainLastBlockHeaderAsync(); @@ -83,6 +122,7 @@ private async Task
GetTokenContractAddressAsync() return contractMapping[TokenSmartContractAddressNameProvider.Name]; } + private async Task GetTokenContractStubAsync() { TokenContractAddress = await GetTokenContractAddressAsync(); @@ -115,13 +155,12 @@ private async Task DeployContractsAsync() } // Parliament { - var code = Codes.Single(kv => kv.Key.Contains("Parliament")).Value; + var code = Codes.Single(kv => kv.Key.Contains("MockParliament")).Value; ParliamentAddress = await DeploySystemSmartContract(category, code, ParliamentSmartContractAddressNameProvider.Name, DefaultSenderKeyPair); ParliamentContractStub = GetTester(ParliamentAddress, DefaultSenderKeyPair); - } //Consensus { @@ -133,6 +172,7 @@ private async Task DeployContractsAsync() DefaultSenderKeyPair); } } + private async Task InitializeAElfConsensus() { { @@ -180,6 +220,7 @@ private Round GenerateFirstRoundOfNewTerm(MinerList minerList, int miningInterva return round; } + private async Task SetPrimaryTokenSymbolAsync() { await TokenContractStub.SetPrimaryTokenSymbol.SendAsync(new SetPrimaryTokenSymbolInput @@ -283,9 +324,15 @@ public class ExecutePluginTransactionDirectlyForMethodFeeTestBase : ContractTest ExecutionPluginTransactionDirectlyForMethodFeeTestModule> { protected const string NativeTokenSymbol = "ELF"; + protected const string USDT = "USDT"; + protected const string Token1 = "TOKENA"; + protected const string Token2 = "TOKENB"; + + protected readonly IBlockTimeProvider BlockTimeProvider; protected ExecutePluginTransactionDirectlyForMethodFeeTestBase() { + BlockTimeProvider = GetRequiredService(); AsyncHelper.RunSync(InitializeContracts); } @@ -293,29 +340,75 @@ protected ExecutePluginTransactionDirectlyForMethodFeeTestBase() internal Address TreasuryContractAddress { get; set; } internal Address ConsensusContractAddress { get; set; } internal TokenContractContainer.TokenContractStub TokenContractStub { get; set; } + internal TokenContractImplContainer.TokenContractImplStub TokenContractImplStub { get; set; } + internal TokenContractImplContainer.TokenContractImplStub TokenContractImplStub2 { get; set; } internal TokenContractContainer.TokenContractStub TokenContractStub2 { get; set; } internal TokenContractContainer.TokenContractStub TokenContractStub3 { get; set; } + internal TokenContractContainer.TokenContractStub TokenContractStubDelegator { get; set; } + + internal TokenContractImplContainer.TokenContractImplStub TokenContractStubDelegate1 { get; set; } + internal TokenContractImplContainer.TokenContractImplStub TokenContractStubDelegate2 { get; set; } + internal TokenContractImplContainer.TokenContractImplStub TokenContractStubDelegate3 { get; set; } + internal TokenContractImplContainer.TokenContractImplStub TokenContractStubSecondaryDelegate1 { get; set; } + internal TokenContractImplContainer.TokenContractImplStub TokenContractStubSecondaryDelegate2 { get; set; } + + internal TokenContractImplContainer.TokenContractImplStub TokenContractStubSecondaryDelegate3 { get; set; } + internal TokenContractImplContainer.TokenContractImplStub TokenContractStubSecondaryDelegate4 { get; set; } + internal TokenContractImplContainer.TokenContractImplStub TokenContractStubSecondaryDelegate5 { get; set; } + internal TokenContractImplContainer.TokenContractImplStub TokenContractStubSecondaryDelegate6 { get; set; } + internal ParliamentContractImplContainer.ParliamentContractImplStub ParliamentContractStub { get; set; } internal AEDPoSContractContainer.AEDPoSContractStub AEDPoSContractStub { get; set; } internal ECKeyPair DefaultSenderKeyPair => Accounts[0].KeyPair; internal ECKeyPair DelegateeKeyPair => Accounts[1].KeyPair; + internal ECKeyPair Delegatee2KeyPair => Accounts[3].KeyPair; + internal ECKeyPair Delegatee3KeyPair => Accounts[5].KeyPair; + internal ECKeyPair SecondaryDelegatee1KeyPair => Accounts[6].KeyPair; + internal ECKeyPair SecondaryDelegatee2KeyPair => Accounts[7].KeyPair; + + internal ECKeyPair SecondaryDelegatee3KeyPair => Accounts[8].KeyPair; + + internal ECKeyPair SecondaryDelegatee4KeyPair => Accounts[9].KeyPair; + + internal ECKeyPair SecondaryDelegatee5KeyPair => Accounts[11].KeyPair; + + internal ECKeyPair SecondaryDelegatee6KeyPair => Accounts[12].KeyPair; + + internal ECKeyPair UserKeyPair => Accounts[2].KeyPair; internal ECKeyPair UserAKeyPair => Accounts[3].KeyPair; internal ECKeyPair UserTomSenderKeyPair => Accounts[10].KeyPair; internal Address UserTomSender => Accounts[10].Address; + protected List InitialCoreDataCenterKeyPairs => Accounts.Take(1).Select(a => a.KeyPair).ToList(); internal Address DefaultSender => Accounts[0].Address; - internal Address delegateeAddress => Accounts[1].Address; + internal Address DelegateeAddress => Accounts[1].Address; + internal Address Delegatee2Address => Accounts[3].Address; + internal Address Delegatee3Address => Accounts[5].Address; + internal Address SecondaryDelegatee1Address => Accounts[6].Address; + internal Address SecondaryDelegatee2Address => Accounts[7].Address; + + internal Address SecondaryDelegatee3Address => Accounts[8].Address; + + internal Address SecondaryDelegatee4Address => Accounts[9].Address; + + internal Address SecondaryDelegatee5Address => Accounts[11].Address; + + internal Address SecondaryDelegatee6Address => Accounts[12].Address; + + + internal Address userAddress => Accounts[2].Address; internal TokenContractContainer.TokenContractStub TokenContractStubA { get; set; } internal Address UserAAddress => Accounts[3].Address; - internal Address UserCAddress => Accounts[4].Address; + internal Address UserBAddress => Accounts[4].Address; + internal Address UserCAddress => Accounts[5].Address; + - private async Task InitializeContracts() { await DeployContractsAsync(); @@ -345,15 +438,42 @@ private async Task DeployContractsAsync() GetTester(TokenContractAddress, DefaultSenderKeyPair); TokenContractStub2 = GetTester(TokenContractAddress, DelegateeKeyPair); - TokenContractStub3 = + TokenContractStub3 = GetTester(TokenContractAddress, UserKeyPair); - TokenContractStubA = + TokenContractStubA = GetTester(TokenContractAddress, UserKeyPair); + TokenContractStubDelegator = + GetTester(TokenContractAddress, UserTomSenderKeyPair); + TokenContractStubDelegate1 = + GetTester(TokenContractAddress, DelegateeKeyPair); + TokenContractStubDelegate2 = + GetTester(TokenContractAddress, Delegatee2KeyPair); + TokenContractImplStub = + GetTester(TokenContractAddress, DefaultSenderKeyPair); + TokenContractStubDelegate3 = + GetTester(TokenContractAddress, Delegatee3KeyPair); + TokenContractStubSecondaryDelegate1 = + GetTester(TokenContractAddress, SecondaryDelegatee1KeyPair); + TokenContractStubSecondaryDelegate2 = + GetTester(TokenContractAddress, SecondaryDelegatee2KeyPair); + TokenContractStubSecondaryDelegate3 = + GetTester(TokenContractAddress, SecondaryDelegatee3KeyPair); + TokenContractStubSecondaryDelegate4 = + GetTester(TokenContractAddress, SecondaryDelegatee4KeyPair); + TokenContractStubSecondaryDelegate5 = + GetTester(TokenContractAddress, SecondaryDelegatee5KeyPair); + TokenContractStubSecondaryDelegate6 = + GetTester(TokenContractAddress, SecondaryDelegatee6KeyPair); + + TokenContractImplStub = + GetTester(TokenContractAddress, DefaultSenderKeyPair); + TokenContractImplStub2 = + GetTester(TokenContractAddress, UserKeyPair); } // Parliament { - var code = Codes.Single(kv => kv.Key.Contains("Parliament")).Value; + var code = Codes.Single(kv => kv.Key.Contains("MockParliament")).Value; var parliamentContractAddress = await DeploySystemSmartContract(category, code, ParliamentSmartContractAddressNameProvider.Name, DefaultSenderKeyPair); ParliamentContractStub = @@ -390,7 +510,8 @@ private async Task CreateNativeTokenAsync() IsBurnable = true, TokenName = "elf token", TotalSupply = totalSupply, - Issuer = DefaultSender + Issuer = DefaultSender, + Owner = DefaultSender }); createResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); } diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeTestModule.cs b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeTestModule.cs index b05c11846f..874d7b8049 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeTestModule.cs +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeTestModule.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Threading.Tasks; -using AElf.Blockchains.MainChain; using AElf.Contracts.TestBase; using AElf.ContractTestKit; using AElf.Cryptography; @@ -11,7 +10,6 @@ using AElf.Kernel.Miner.Application; using AElf.Kernel.SmartContract.Application; using AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests.Service; -using AElf.Kernel.Token; using AElf.OS.Node.Application; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -47,8 +45,8 @@ public override void ConfigureServices(ServiceConfigurationContext context) [DependsOn( typeof(ContractTestModule), -typeof(ExecutionPluginForMethodFeeModule), -typeof(FeeCalculationModule))] + typeof(ExecutionPluginForMethodFeeModule), + typeof(FeeCalculationModule))] public class ExecutionPluginForUserContractMethodFeeTestModule : ContractTestModule { public override void ConfigureServices(ServiceConfigurationContext context) @@ -95,6 +93,7 @@ public class ExecutionPluginTransactionDirectlyForMethodFeeTestModule : Contract public override void ConfigureServices(ServiceConfigurationContext context) { Configure(o => o.ContractDeploymentAuthorityRequired = false); + context.Services.AddSingleton(); context.Services.RemoveAll(); context.Services.RemoveAll(); context.Services.RemoveAll(); diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeWithForkTest.cs b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeWithForkTest.cs index e6bebf3081..6a6ffe5a73 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeWithForkTest.cs +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeWithForkTest.cs @@ -33,7 +33,7 @@ private async Task> GetTransactionFeesMapAsync(IChainC public async Task ChargeFee_With_Fork_Test() { var amount = 100000; - + await SetMethodFeeWithProposalAsync(new MethodFees { MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), @@ -74,6 +74,7 @@ await SetMethodFeeWithProposalAsync(new MethodFees BlockHash = branchOneBlock.GetHash(), BlockHeight = branchOneBlock.Height }); transactionFeesMap.ShouldBeNull(); + await SetMethodFeeWithProposalAsync(new MethodFees { MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), @@ -102,7 +103,7 @@ await SetMethodFeeWithProposalAsync(new MethodFees BlockHash = result.Item1.GetHash(), BlockHeight = result.Item1.Height }); transactionFeesMap.First().Value.ShouldBe(fee); //300000 - targetFee.ShouldNotBe(fee); + targetFee.ShouldBe(fee); } // branch two @@ -128,18 +129,20 @@ await SetMethodFeeWithProposalAsync(new MethodFees public async Task Claim_Fee_Send_By_User_Fail_Test() { var amount = 100000; - await SetMethodFeeWithProposalAsync(new MethodFees - { - MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), - Fees = + + await Tester.ExecuteContractWithMiningAsync(TokenContractAddress, + nameof(TokenContractImplContainer.TokenContractImplStub.SetMethodFee), new MethodFees { - new MethodFee + MethodName = nameof(TokenContractContainer.TokenContractStub.Transfer), + Fees = { - Symbol = "ELF", - BasicFee = amount + new MethodFee + { + Symbol = "ELF", + BasicFee = amount + } } - } - }.ToByteString()); + }); await Tester.ExecuteContractWithMiningReturnBlockAsync(TokenContractAddress, nameof(TokenContractContainer.TokenContractStub.Transfer), new TransferInput diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForUserContractMethodFeeTest.cs b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForUserContractMethodFeeTest.cs index f10baf828c..25e8daa5f4 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForUserContractMethodFeeTest.cs +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForUserContractMethodFeeTest.cs @@ -155,7 +155,7 @@ public async Task GetPreTransactions_None_PreTransaction_Test() [InlineData(9, 0, 1, 10, 1, 2, "ELF", 9, false)] [InlineData(100000000, 2, 2, 0, 1, 2, "TSA", 1, true)] [InlineData(100000000, 2, 2, 0, 13, 2, "TSB", 2, true)] - [InlineData(100000000, 2, 2, 0, 20, 20, "TSB", 2, false)] + [InlineData(100000000, 2, 2, 0, 20, 20, "TSA", 2, false)] [InlineData(1, 0, 1, 0, 1, 2, "TSB", 1, false)] [InlineData(10, 0, 0, 0, 1, 2, "ELF", 10, false)] // Charge 10 ELFs tx size fee. public async Task ChargeFee_SetConfiguration_Tests(long balance1, long balance2, long balance3, long fee1, @@ -184,14 +184,8 @@ await TokenContractStub.Transfer.SendAsync(new TransferInput Key = ConfigurationKey, Value = transactionFee.ToByteString() }; - { - var organizationAddress = await GetParliamentDefaultOrganizationAddressAsync(); - var proposalId = - await CreateProposalAsync(organizationAddress, createProposalInput, "SetConfiguration"); - await ParliamentContractStub.Approve.SendAsync(proposalId); - var releaseRet = await ParliamentContractStub.Release.SendAsync(proposalId); - releaseRet.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); - } + + await ConfigurationStub.SetConfiguration.SendAsync(createProposalInput); var originBalance = (await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { @@ -250,14 +244,7 @@ public async Task ChargeFee_SizeFeeIsFree() Key = ConfigurationKey, Value = transactionFee.ToByteString() }; - { - var organizationAddress = await GetParliamentDefaultOrganizationAddressAsync(); - var proposalId = - await CreateProposalAsync(organizationAddress, createProposalInput, "SetConfiguration"); - await ParliamentContractStub.Approve.SendAsync(proposalId); - var releaseRet = await ParliamentContractStub.Release.SendAsync(proposalId); - releaseRet.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); - } + await ConfigurationStub.SetConfiguration.SendAsync(createProposalInput); var beforeBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { Owner = DefaultAddress, @@ -298,14 +285,7 @@ public async Task ChargeFee_SpecConfigurationFee() Key = $"{ConfigurationKey}_{_testContractAddress}_{nameof(TestContractContainer.TestContractStub.TestMethod)}", Value = transactionFee.ToByteString() }; - { - var organizationAddress = await GetParliamentDefaultOrganizationAddressAsync(); - var proposalId = - await CreateProposalAsync(organizationAddress, createProposalInput, "SetConfiguration"); - await ParliamentContractStub.Approve.SendAsync(proposalId); - var releaseRet = await ParliamentContractStub.Release.SendAsync(proposalId); - releaseRet.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); - } + await ConfigurationStub.SetConfiguration.SendAsync(createProposalInput); var beforeBalance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { Owner = DefaultAddress, @@ -334,7 +314,8 @@ await TokenContractStub.Create.SendAsync(new CreateInput IsBurnable = true, TokenName = "test token", TotalSupply = 1_000_000_00000000L, - Issuer = DefaultAddress + Issuer = DefaultAddress, + Owner = DefaultAddress, }); if (issueAmount != 0) @@ -363,10 +344,7 @@ private async Task Initialize() await DeployTestContractAsync(); { - var proposalId = await SetUserContractFeeAsync(1_00000000); - await ParliamentContractStub.Approve.SendAsync(proposalId); - var releaseRet = await ParliamentContractStub.Release.SendAsync(proposalId); - releaseRet.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + await SetUserContractFeeAsync(1_00000000); var configuration = await ConfigurationStub.GetConfiguration.CallAsync(new StringValue { Value = ConfigurationKey @@ -390,13 +368,10 @@ private async Task CheckTransactionFeesMapAsync(Dictionary transac transactionFeesMap.Value[transactionFee.Key].ShouldBe(transactionFee.Value); } - private async Task SetUserContractFeeAsync(int amount) + private async Task SetUserContractFeeAsync(int amount) { var createProposalInput = SetUserContractFee(amount); - var organizationAddress = await GetParliamentDefaultOrganizationAddressAsync(); - var proposalId = - await CreateProposalAsync(organizationAddress, createProposalInput, "SetConfiguration"); - return proposalId; + await ConfigurationStub.SetConfiguration.SendAsync(createProposalInput); } private async Task
GetParliamentDefaultOrganizationAddressAsync() diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/MethodFeeTestTokenContractInitializationProvider.cs b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/MethodFeeTestTokenContractInitializationProvider.cs index 38cc9592d0..585a902412 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/MethodFeeTestTokenContractInitializationProvider.cs +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/MethodFeeTestTokenContractInitializationProvider.cs @@ -28,7 +28,8 @@ public MethodFeeTestTokenContractInitializationProvider() : base(null) IsBurnable = true, TokenName = "elf token", TotalSupply = 1_000_000_00000000L, - Issuer = SampleAccount.Accounts[0].Address + Issuer = SampleAccount.Accounts[0].Address, + Owner = SampleAccount.Accounts[0].Address }.ToByteString() }); diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests.csproj b/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests.csproj index 360e3a5f34..6413e7f543 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests.csproj +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests.csproj @@ -9,7 +9,7 @@ runtime; build; native; contentfiles; analyzers - + all @@ -21,11 +21,11 @@ - - - - - + + + + + @@ -53,12 +53,12 @@ Contract PreserveNewest - + false Contract PreserveNewest - + false Contract PreserveNewest @@ -103,12 +103,12 @@ Protobuf\Proto\acs10.proto - - Protobuf\Proto\parliament_contract.proto - Protobuf\Proto\aedpos_contract.proto + + Protobuf\Proto\test_mock_parliament_contract.proto + diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/ExecutionPluginForResourceFeeTestBase.cs b/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/ExecutionPluginForResourceFeeTestBase.cs index 7d73b285db..7f641bdb82 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/ExecutionPluginForResourceFeeTestBase.cs +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/ExecutionPluginForResourceFeeTestBase.cs @@ -4,6 +4,7 @@ using AElf.Contracts.Consensus.AEDPoS; using AElf.Contracts.MultiToken; using AElf.Contracts.Parliament; +using AElf.Contracts.TestContract.MockParliament; using AElf.Contracts.TokenConverter; using AElf.Contracts.Treasury; using AElf.ContractTestKit; @@ -119,11 +120,12 @@ public class ExecutionPluginForResourceFeeTestBase : ContractTestBase Accounts[0].KeyPair; @@ -139,11 +141,11 @@ public class ExecutionPluginForResourceFeeTestBase : ContractTestBase kv.Key.Contains("TestContract")).Value; + var code = Codes.Single(kv => kv.Key.Contains("ExecutionPluginForResourceFee.Tests.TestContract")).Value; TestContractAddress = await DeployContractAsync(category, code, HashHelper.ComputeFrom("TestContract"), DefaultSenderKeyPair); TestContractStub = @@ -196,11 +198,11 @@ await DeploySystemSmartContract(category, code, // Parliament { - var code = Codes.Single(kv => kv.Key.Contains("Parliament")).Value; - var parliamentContractAddress = await DeploySystemSmartContract(category, code, + var code = Codes.Single(kv => kv.Key.Contains("MockParliament")).Value; + ParliamentContractAddress = await DeploySystemSmartContract(category, code, ParliamentSmartContractAddressNameProvider.Name, DefaultSenderKeyPair); ParliamentContractStub = - GetTester(parliamentContractAddress, + GetTester(ParliamentContractAddress, DefaultSenderKeyPair); } @@ -222,18 +224,17 @@ private async Task InitializeTokenAsync() const long issueAmountToConverter = 100_000_000_00000000; //init elf token { - var createResult = await TokenContractStub.Create.SendAsync(new CreateInput - { - Symbol = "ELF", - Decimals = 8, - IsBurnable = true, - TokenName = "elf token", - TotalSupply = totalSupply, - Issuer = DefaultSender, - LockWhiteList = { TreasuryContractAddress, TokenConverterAddress } - }); - - createResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + await TokenContractStub.Create.SendAsync(new CreateInput + { + Symbol = "ELF", + Decimals = 8, + IsBurnable = true, + TokenName = "elf token", + TotalSupply = totalSupply, + Issuer = DefaultSender, + Owner = DefaultSender, + LockWhiteList = { TreasuryContractAddress, TokenConverterAddress } + }); { var issueResult = await TokenContractStub.Issue.SendAsync(new IssueInput @@ -258,7 +259,7 @@ private async Task InitializeTokenAsync() //init resource token - CPU { - var createResult = await TokenContractStub.Create.SendAsync(new CreateInput + await TokenContractStub.Create.SendAsync(new CreateInput { Symbol = "READ", Decimals = 2, @@ -266,11 +267,10 @@ private async Task InitializeTokenAsync() TokenName = "read token", TotalSupply = totalSupply, Issuer = DefaultSender, + Owner = DefaultSender, LockWhiteList = { TreasuryContractAddress, TokenConverterAddress } }); - createResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); - var issueResult = await TokenContractStub.Issue.SendAsync(new IssueInput { Symbol = "READ", @@ -283,7 +283,7 @@ private async Task InitializeTokenAsync() //init resource token - STO { - var createResult = await TokenContractStub.Create.SendAsync(new CreateInput + await TokenContractStub.Create.SendAsync(new CreateInput { Symbol = "STORAGE", Decimals = 2, @@ -291,11 +291,10 @@ private async Task InitializeTokenAsync() TokenName = "sto token", TotalSupply = totalSupply, Issuer = DefaultSender, + Owner = DefaultSender, LockWhiteList = { TreasuryContractAddress, TokenConverterAddress } }); - createResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); - var issueResult = await TokenContractStub.Issue.SendAsync(new IssueInput { Symbol = "STORAGE", @@ -308,7 +307,7 @@ private async Task InitializeTokenAsync() //init resource token - NET { - var createResult = await TokenContractStub.Create.SendAsync(new CreateInput + await TokenContractStub.Create.SendAsync(new CreateInput { Symbol = "TRAFFIC", Decimals = 2, @@ -316,11 +315,10 @@ private async Task InitializeTokenAsync() TokenName = "net token", TotalSupply = totalSupply, Issuer = DefaultSender, + Owner = DefaultSender, LockWhiteList = { TreasuryContractAddress, TokenConverterAddress } }); - createResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); - var issueResult = await TokenContractStub.Issue.SendAsync(new IssueInput { Symbol = "TRAFFIC", @@ -332,7 +330,7 @@ private async Task InitializeTokenAsync() } //init resource token - WRITE { - var createResult = await TokenContractStub.Create.SendAsync(new CreateInput + await TokenContractStub.Create.SendAsync(new CreateInput { Symbol = "WRITE", Decimals = 2, @@ -340,11 +338,10 @@ private async Task InitializeTokenAsync() TokenName = "WRITE token", TotalSupply = totalSupply, Issuer = DefaultSender, + Owner = DefaultSender, LockWhiteList = { TreasuryContractAddress, TokenConverterAddress } }); - createResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); - var issueResult = await TokenContractStub.Issue.SendAsync(new IssueInput { Symbol = "WRITE", @@ -390,7 +387,11 @@ await TreasuryContractStub.InitialMiningRewardProfitItem.SendAsync( private async Task InitializeParliament() { - await ParliamentContractStub.Initialize.SendAsync(new Contracts.Parliament.InitializeInput()); + await ParliamentContractStub.Initialize.SendAsync(new AElf.Contracts.TestContract.MockParliament.InitializeInput + { + ProposerAuthorityRequired = false, + PrivilegedProposer = DefaultSender + }); } private async Task InitializeAElfConsensus() diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/ExecutionPluginForResourceFeeTestModule.cs b/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/ExecutionPluginForResourceFeeTestModule.cs index 22d634ad0b..3fb9d1c466 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/ExecutionPluginForResourceFeeTestModule.cs +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/ExecutionPluginForResourceFeeTestModule.cs @@ -1,4 +1,5 @@ using AElf.ContractTestKit; +using AElf.Kernel.Consensus.AEDPoS; using AElf.Kernel.FeeCalculation; using AElf.Kernel.FeeCalculation.Infrastructure; using AElf.Kernel.SmartContract.Application; diff --git a/test/AElf.OS.TestBase/OSTestHelper.cs b/test/AElf.OS.TestBase/OSTestHelper.cs index a2f430808f..cbef7de0eb 100644 --- a/test/AElf.OS.TestBase/OSTestHelper.cs +++ b/test/AElf.OS.TestBase/OSTestHelper.cs @@ -432,7 +432,23 @@ private async Task StartNodeAsync() TotalSupply = TokenTotalSupply, Decimals = 2, Issuer = ownAddress, - IsBurnable = true + IsBurnable = true, + Owner = ownAddress + }.ToByteString() + }); + + tokenContractDto + .AddGenesisTransactionMethodCall(new ContractInitializationMethodCall + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Create), Params = new CreateInput + { + Symbol = "TELF", + TokenName = "ELF_Token", + TotalSupply = TokenTotalSupply, + Decimals = 2, + Issuer = ownAddress, + IsBurnable = true, + Owner = ownAddress }.ToByteString() }); @@ -452,6 +468,16 @@ private async Task StartNodeAsync() Memo = "Issue" }.ToByteString() }); + tokenContractDto.AddGenesisTransactionMethodCall(new ContractInitializationMethodCall + { + MethodName = nameof(TokenContractContainer.TokenContractStub.Issue), Params = new IssueInput + { + Symbol = "TELF", + Amount = TokenTotalSupply, + To = ownAddress, + Memo = "Issue" + }.ToByteString() + }); tokenContractDto.AddGenesisTransactionMethodCall(new ContractInitializationMethodCall { diff --git a/test/AElf.OS.Tests/Account/Application/AccountServiceTests.cs b/test/AElf.OS.Tests/Account/Application/AccountServiceTests.cs index 399bdd6064..d00503a5eb 100644 --- a/test/AElf.OS.Tests/Account/Application/AccountServiceTests.cs +++ b/test/AElf.OS.Tests/Account/Application/AccountServiceTests.cs @@ -99,4 +99,15 @@ public async Task GetAccountAsync_WithOptionEmpty() account = await _accountService.GetAccountAsync(); account.ShouldNotBeNull(); } + + [Fact] + public async Task Vrf_Test() + { + var alpha = "5cf8151010716e40e5349ad02821da605df22e9ac95450c7e35f04c720fd4db5"; + var alphaBytes = Hash.LoadFromHex(alpha).ToByteArray(); + var pi = await _accountService.ECVrfProveAsync(alphaBytes); + var pubkey = await _accountService.GetPublicKeyAsync(); + var beta = CryptoHelper.ECVrfVerify(pubkey, alphaBytes, pi); + beta.ShouldNotBeEmpty(); + } } \ No newline at end of file diff --git a/test/AElf.Parallel.Tests/ParallelTestHelper.cs b/test/AElf.Parallel.Tests/ParallelTestHelper.cs index e2b523791f..94934e37d8 100644 --- a/test/AElf.Parallel.Tests/ParallelTestHelper.cs +++ b/test/AElf.Parallel.Tests/ParallelTestHelper.cs @@ -173,37 +173,22 @@ await _smartContractAddressService.GetAddressByContractNameAsync(await GetChainC return GetBalanceOutput.Parser.ParseFrom(returnValue).Balance; } - public async Task CreateAndIssueTokenAsync(string symbol, Address issueAddress) + public async Task TransferTokenAsync(string symbol, Address issueAddress) { var ownAddress = await _accountService.GetAccountAsync(); var tokenContractAddress = await _smartContractAddressService.GetAddressByContractNameAsync(await GetChainContextAsync(), TokenSmartContractAddressNameProvider.StringName); - var createTokenTransaction = GenerateTransaction(ownAddress, tokenContractAddress, - nameof(TokenContractContainer.TokenContractStub.Create), - new CreateInput - { - Symbol = symbol, - TokenName = $"{symbol}_Token", - TotalSupply = TokenTotalSupply, - Decimals = 2, - Issuer = ownAddress, - IsBurnable = true - }); - var signature = await _accountService.SignAsync(createTokenTransaction.GetHash().ToByteArray()); - createTokenTransaction.Signature = ByteString.CopyFrom(signature); - await BroadcastTransactions(new List { createTokenTransaction }); - await MinedOneBlock(); - + var issueTokenTransaction = GenerateTransaction(ownAddress, tokenContractAddress, - nameof(TokenContractContainer.TokenContractStub.Issue), new IssueInput + nameof(TokenContractContainer.TokenContractStub.Transfer), new TransferInput { Symbol = symbol, - Amount = TokenTotalSupply, + Amount = 100_000000, To = issueAddress, - Memo = "Issue" + Memo = "Transfer" }); - signature = await _accountService.SignAsync(issueTokenTransaction.GetHash().ToByteArray()); + var signature = await _accountService.SignAsync(issueTokenTransaction.GetHash().ToByteArray()); issueTokenTransaction.Signature = ByteString.CopyFrom(signature); await BroadcastTransactions(new List { issueTokenTransaction }); await MinedOneBlock(); diff --git a/test/AElf.Parallel.Tests/ParallelTests.cs b/test/AElf.Parallel.Tests/ParallelTests.cs index b0dbbd5e5a..19b8452e24 100644 --- a/test/AElf.Parallel.Tests/ParallelTests.cs +++ b/test/AElf.Parallel.Tests/ParallelTests.cs @@ -224,7 +224,7 @@ public async Task TransferTwoSymbolParallelTest() var symbol = "TELF"; var keyPair = CryptoHelper.GenerateKeyPair(); var address = Address.FromPublicKey(keyPair.PublicKey); - await _parallelTestHelper.CreateAndIssueTokenAsync(symbol, address); + await _parallelTestHelper.TransferTokenAsync(symbol, address); var transactionList = new List(); var accountAddress = await _accountService.GetAccountAsync(); diff --git a/test/AElf.WebApp.Application.Chain.Tests/AElf.WebApp.Application.Chain.Tests.csproj b/test/AElf.WebApp.Application.Chain.Tests/AElf.WebApp.Application.Chain.Tests.csproj index f4907935f6..f629badebd 100644 --- a/test/AElf.WebApp.Application.Chain.Tests/AElf.WebApp.Application.Chain.Tests.csproj +++ b/test/AElf.WebApp.Application.Chain.Tests/AElf.WebApp.Application.Chain.Tests.csproj @@ -9,7 +9,7 @@ runtime; build; native; contentfiles; analyzers - + all @@ -20,14 +20,19 @@ runtime; build; native; contentfiles; analyzers - + - + false Contract PreserveNewest + + false + Contract + PreserveNewest + diff --git a/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs b/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs index 6755b238ee..b898977dc3 100644 --- a/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs +++ b/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs @@ -66,7 +66,8 @@ public BlockChainAppServiceTest(ITestOutputHelper outputHelper) : base(outputHel _codes ?? (_codes = ContractsDeployer.GetContractCodes()); private byte[] TestContractCode => Codes.Single(kv => kv.Key.Contains("TestContract.BasicFunction")).Value; - private byte[] VoteContractCode => Codes.Single(kv => kv.Key.Contains("Vote")).Value; + private byte[] VoteContractCode => Codes.Single(kv => kv.Key.Contains("Contracts.Vote")).Value; + private byte[] TestVoteContractCode => Codes.Single(kv => kv.Key.Contains("TestContract.Vote")).Value; [Fact] public async Task Deploy_Contract_Success_Test() @@ -1720,5 +1721,105 @@ await PostResponseAsObjectAsync("/api/blockChain/CalculateT response.Error.Message.ShouldBe(Error.Message[Error.InvalidParams]); } + [Fact] + public async Task Transaction_Params_Changed_Test() + { + var accountAddress = await _accountService.GetAccountAsync(); + var chain = await _blockchainService.GetChainAsync(); + var deployTransaction = new Transaction + { + From = accountAddress, + To = _smartContractAddressService.GetZeroSmartContractAddress(), + MethodName = nameof(BasicContractZero.DeploySmartContract), + Params = ByteString.CopyFrom(new ContractDeploymentInput + { + Category = KernelConstants.CodeCoverageRunnerCategory, + Code = ByteString.CopyFrom(VoteContractCode) + }.ToByteArray()), + RefBlockNumber = chain.BestChainHeight, + RefBlockPrefix = BlockHelper.GetRefBlockPrefix(chain.BestChainHash) + }; + deployTransaction.Signature = + ByteString.CopyFrom(await _accountService.SignAsync(deployTransaction.GetHash().ToByteArray())); + + var parameters = new Dictionary + { + { "rawTransaction", deployTransaction.ToByteArray().ToHex() } + }; + + var sendTransactionResponse = + await PostResponseAsObjectAsync("/api/blockChain/sendTransaction", + parameters); + + sendTransactionResponse.TransactionId.ShouldBe(deployTransaction.GetHash().ToHex()); + await _osTestHelper.MinedOneBlock(); + var transactionResult = await _osTestHelper.GetTransactionResultsAsync(deployTransaction.GetHash()); + transactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + var address = Address.Parser.ParseFrom(transactionResult.ReturnValue); + var transaction = new Transaction + { + From = accountAddress, + To = address, + MethodName = nameof(VoteContractContainer.VoteContractStub.AddOption), + Params = ByteString.CopyFrom(new AddOptionInput() + { + VotingItemId = HashHelper.ComputeFrom("VotingItemId"), + Option = "Option" + }.ToByteArray()), + RefBlockNumber = chain.BestChainHeight, + RefBlockPrefix = BlockHelper.GetRefBlockPrefix(chain.BestChainHash) + }; + transaction.Signature = + ByteString.CopyFrom(await _accountService.SignAsync(transaction.GetHash().ToByteArray())); + + parameters = new Dictionary + { + { "rawTransaction", transaction.ToByteArray().ToHex() } + }; + + sendTransactionResponse = + await PostResponseAsObjectAsync("/api/blockChain/sendTransaction", + parameters); + + sendTransactionResponse.TransactionId.ShouldBe(transaction.GetHash().ToHex()); + await _osTestHelper.MinedOneBlock(); + var response = await GetResponseAsObjectAsync( + $"/api/blockChain/transactionResult?transactionId={transaction.GetHash().ToHex()}"); + response.Transaction.Params.ShouldBe(AddOptionInput.Parser.ParseFrom(transaction.Params).ToString()); + + var updateTransaction = new Transaction + { + From = accountAddress, + To = _smartContractAddressService.GetZeroSmartContractAddress(), + MethodName = nameof(BasicContractZero.UpdateSmartContract), + Params = ByteString.CopyFrom(new ContractUpdateInput + { + Address = address, + Code = ByteString.CopyFrom(TestVoteContractCode) + }.ToByteArray()), + RefBlockNumber = chain.BestChainHeight, + RefBlockPrefix = BlockHelper.GetRefBlockPrefix(chain.BestChainHash) + }; + updateTransaction.Signature = + ByteString.CopyFrom(await _accountService.SignAsync(updateTransaction.GetHash().ToByteArray())); + + parameters = new Dictionary + { + { "rawTransaction", updateTransaction.ToByteArray().ToHex() } + }; + sendTransactionResponse = + await PostResponseAsObjectAsync("/api/blockChain/sendTransaction", + parameters); + + sendTransactionResponse.TransactionId.ShouldBe(updateTransaction.GetHash().ToHex()); + await _osTestHelper.MinedOneBlock(); + transactionResult = await _osTestHelper.GetTransactionResultsAsync(updateTransaction.GetHash()); + transactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + response = await GetResponseAsObjectAsync( + $"/api/blockChain/transactionResult?transactionId={transaction.GetHash().ToHex()}"); + response.Transaction.Params.ShouldBe(transaction.Params.ToBase64()); + response.Transaction.Params.ShouldNotBe( + AddOptionInput.Parser.ParseFrom(transaction.Params).ToString()); + } } \ No newline at end of file