diff --git a/contract/Forest.Contracts.SymbolRegistrar/Forest.Contracts.SymbolRegistrar.csproj b/contract/Forest.Contracts.SymbolRegistrar/Forest.Contracts.SymbolRegistrar.csproj index fe15d2b..5d7acf8 100644 --- a/contract/Forest.Contracts.SymbolRegistrar/Forest.Contracts.SymbolRegistrar.csproj +++ b/contract/Forest.Contracts.SymbolRegistrar/Forest.Contracts.SymbolRegistrar.csproj @@ -19,6 +19,12 @@ Protobuf\Proto\acs0.proto + + Protobuf\Proto\reference\acs1.proto + + + Protobuf\Proto\reference\acs2.proto + Protobuf\Proto\acs3.proto @@ -40,6 +46,13 @@ Protobuf\Proto\reference\token_contract.proto + + + Protobuf\Proto\token_contract_impl.proto + + + Protobuf\Proto\reference\transaction_fee.proto + diff --git a/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContractConstants.cs b/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContractConstants.cs index ad43c93..82a5e11 100644 --- a/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContractConstants.cs +++ b/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContractConstants.cs @@ -16,6 +16,8 @@ public class SymbolRegistrarContractConstants public const int MaxAddSpecialSeedCount = 500; public const int MaxSymbolLength = 30; public const string SeedPrefix = "SEED-"; + public const string SeedPrefixPart = "SEED"; + public const int MaxCycleCount = 30; public const string CollectionSymbolSuffix = "0"; public const string SeedOwnedSymbolExternalInfoKey = "__seed_owned_symbol"; diff --git a/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContractReferenceState.cs b/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContractReferenceState.cs index 652caae..b323b29 100644 --- a/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContractReferenceState.cs +++ b/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContractReferenceState.cs @@ -20,5 +20,7 @@ internal AElf.Contracts.ProxyAccountContract.ProxyAccountContractContainer.Proxy get; set; } + + internal TokenContractImplContainer.TokenContractImplReferenceState TokenImplContract { get; set; } } } \ No newline at end of file diff --git a/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContractState.cs b/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContractState.cs index fb8e9c4..f4ce56d 100644 --- a/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContractState.cs +++ b/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContractState.cs @@ -40,5 +40,10 @@ public partial class SymbolRegistrarContractState : ContractState public SingletonState IssueChainList { get; set; } public SingletonState
SeedCollectionOwner { get; set; } public SingletonState ProxyAccountHash { get; set; } + + public SingletonState SeedRenewHashVerifyKey { get; set; } + //seedSymbol -> renewTime + public MappedState SeedRenewTimeMap { get; set; } + } } \ No newline at end of file diff --git a/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContract_Action.cs b/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContract_Action.cs index a83450d..f92b593 100644 --- a/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContract_Action.cs +++ b/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContract_Action.cs @@ -124,6 +124,28 @@ private void DoCreateSeed(Address to, string symbol, long expireTime = 0) ImageUrl = seedInfo.ImageUrl }); } + private void DoRenewSeed(string seedSymbol, long expireTime = 0) + { + if (State.TokenImplContract.Value == null) + { + State.TokenImplContract.Value = State.TokenContract.Value; + } + + var renewInput = new ExtendSeedExpirationTimeInput + { + Symbol = seedSymbol, + ExpirationTime = expireTime + }; + + State.ProxyAccountContract.ForwardCall.Send( + new ForwardCallInput + { + ContractAddress = State.TokenContract.Value, + MethodName = nameof(State.TokenImplContract.ExtendSeedExpirationTime), + ProxyAccountHash = GetProxyAccountHash(), + Args = renewInput.ToByteString() + }); + } private bool CreateSeedToken(Address issuer, string symbol, long expireTime = 0) { @@ -213,5 +235,161 @@ private Hash GetProxyAccountHash() State.ProxyAccountHash.Value = proxyAccount.ProxyAccountHash; return proxyAccount.ProxyAccountHash; } + + public override Empty RegularSeedRenew(RegularSeedRenewInput input) + { + AssertContractInitialize(); + AssertSeedSymbolPattern(input.SeedSymbol); + CheckSeedBalanceExisted(input.SeedSymbol); + var seedTokenInfo = GetTokenInfo(input.SeedSymbol); + Assert(seedTokenInfo.Symbol.Length > 1, "Seed Symbol not exists"); + var seedOwnedSymbol = seedTokenInfo.ExternalInfo.Value[SymbolRegistrarContractConstants.SeedOwnedSymbolExternalInfoKey]; + var seedExpTime = long.Parse(seedTokenInfo.ExternalInfo.Value[SymbolRegistrarContractConstants.SeedExpireTimeExternalInfoKey]); + Assert(!string.IsNullOrWhiteSpace(seedOwnedSymbol) && seedExpTime > Context.CurrentBlockTime.Seconds, "symbol seed not existed or expired"); + + var specialSeed = State.SpecialSeedMap[seedOwnedSymbol]; + Assert(specialSeed == null, "Special seed " + input.SeedSymbol + " not support renew."); + + var price = GetDealPrice(seedOwnedSymbol); + Assert(price != null, "Symbol price not exits"); + Assert(price.Symbol == input.Price.Symbol && price.Amount == input.Price.Amount, "input symbol price not correct"); + + State.TokenContract.TransferFrom.Send(new TransferFromInput() + { + From = Context.Sender, + To = State.ReceivingAccount.Value, + Symbol = price.Symbol, + Amount = price.Amount, + }); + + var nextExpTime = seedExpTime + State.SeedExpirationConfig.Value; + DoRenewSeed(input.SeedSymbol, nextExpTime); + + Context.Fire(new SeedRenewed() + { + ChainId = Context.ChainId, + Buyer = Context.Sender, + Symbol = seedOwnedSymbol, + SeedSymbol = input.SeedSymbol, + ExpTime = nextExpTime, + OriginalExpTime = seedExpTime, + Price = new Price + { + Symbol = input.Price.Symbol, + Amount = input.Price.Amount + }, + SeedType = SeedType.Regular, + RenewType = RenewType.Self + }); + + return new Empty(); + } + + public override Empty SpecialSeedRenew(SpecialSeedRenewInput input) + { + AssertContractInitialize(); + AssertSeedSymbolPattern(input.SeedSymbol); + CheckSeedBalanceExisted(input.SeedSymbol); + Assert(Context.Sender == input.Buyer, "param owner not sender"); + var seedTokenInfo = GetTokenInfo(input.SeedSymbol); + Assert(seedTokenInfo.Symbol.Length > 1, "Seed Symbol not exists"); + var seedOwnedSymbol = seedTokenInfo.ExternalInfo.Value[SymbolRegistrarContractConstants.SeedOwnedSymbolExternalInfoKey]; + var seedExpTime = long.Parse(seedTokenInfo.ExternalInfo.Value[SymbolRegistrarContractConstants.SeedExpireTimeExternalInfoKey]); + Assert(!string.IsNullOrWhiteSpace(seedOwnedSymbol) && seedExpTime > Context.CurrentBlockTime.Seconds, "symbol seed not existed or expired"); + + var specialSeed = State.SpecialSeedMap[seedOwnedSymbol]; + Assert(specialSeed != null, "Not Special seed " + input.SeedSymbol + " not support renew."); + var requestStr = string.Concat(input.Buyer.ToBase58(), input.SeedSymbol,input.Price.Symbol, input.Price.Amount); + requestStr = string.Concat(requestStr,input.OpTime); + CheckSeedRenewRequestHash(requestStr, input.RequestHash); + var lastAddTime = State.SeedRenewTimeMap[input.SeedSymbol]; + Assert(input.OpTime > lastAddTime, "Invalid param OpTime"); + State.SeedRenewTimeMap[input.SeedSymbol] = input.OpTime; + + + var price = input.Price; + Assert(price != null, "Symbol price not exits"); + var priceTokenInfo = GetTokenInfo(price.Symbol); + Assert(priceTokenInfo != null, "input price symbol not correct"); + Assert(price.Amount > 0, "input price amount not correct"); + + State.TokenContract.TransferFrom.Send(new TransferFromInput() + { + From = Context.Sender, + To = State.ReceivingAccount.Value, + Symbol = price.Symbol, + Amount = price.Amount, + }); + + var nextExpTime = seedExpTime + State.SeedExpirationConfig.Value; + DoRenewSeed(input.SeedSymbol, nextExpTime); + + Context.Fire(new SeedRenewed() + { + ChainId = Context.ChainId, + Buyer = Context.Sender, + Symbol = seedOwnedSymbol, + SeedSymbol = input.SeedSymbol, + ExpTime = nextExpTime, + OriginalExpTime = seedExpTime, + Price = new Price + { + Symbol = input.Price.Symbol, + Amount = input.Price.Amount + }, + SeedType = SeedType.Unique, + RenewType = RenewType.Self + }); + + return new Empty(); + } + + + public override Empty BidFinishSeedRenew(BidFinishSeedRenewInput input) + { + AssertContractInitialize(); + AssertSeedSymbolPattern(input.SeedSymbol); + var seedTokenInfo = GetTokenInfo(input.SeedSymbol); + Assert(seedTokenInfo.Symbol.Length > 1, "Seed Symbol not exists"); + var seedOwnedSymbol = seedTokenInfo.ExternalInfo.Value[SymbolRegistrarContractConstants.SeedOwnedSymbolExternalInfoKey]; + var seedExpTime = long.Parse(seedTokenInfo.ExternalInfo.Value[SymbolRegistrarContractConstants.SeedExpireTimeExternalInfoKey]); + Assert(!string.IsNullOrWhiteSpace(seedOwnedSymbol) && seedExpTime > Context.CurrentBlockTime.Seconds, "symbol seed not existed or expired"); + + var specialSeed = State.SpecialSeedMap[seedOwnedSymbol]; + Assert(specialSeed != null, "Not Special seed " + input.SeedSymbol + " not support renew."); + var requestStr = string.Concat(input.SeedSymbol, input.BidFinishTime, input.OpTime); + CheckSeedRenewRequestHash(requestStr, input.RequestHash); + var lastAddTime = State.SeedRenewTimeMap[input.SeedSymbol]; + Assert(input.OpTime > lastAddTime, "Invalid param OpTime"); + State.SeedRenewTimeMap[input.SeedSymbol] = input.OpTime; + + var nextExpTime = input.BidFinishTime + State.SeedExpirationConfig.Value; + DoRenewSeed(input.SeedSymbol, nextExpTime); + + Context.Fire(new SeedRenewed() + { + ChainId = Context.ChainId, + Buyer = Context.Sender, + Symbol = seedOwnedSymbol, + SeedSymbol = input.SeedSymbol, + ExpTime = nextExpTime, + OriginalExpTime = seedExpTime, + SeedType = SeedType.Unique, + RenewType = RenewType.Bid + }); + + return new Empty(); + } + + private void CheckSeedRenewRequestHash(string request, string hash) + { + var key = State.SeedRenewHashVerifyKey.Value; + Assert(!string.IsNullOrEmpty(key), "Need Set SeedRenewHashVerifyKey"); + var requestHash = HashHelper.ComputeFrom(string.Concat(request, key)); + Assert(hash.Equals(requestHash.ToHex()), "Unverified requests"); + } + } + + } \ No newline at end of file diff --git a/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContract_Admin.cs b/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContract_Admin.cs index 380fe28..1b11fd0 100644 --- a/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContract_Admin.cs +++ b/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContract_Admin.cs @@ -399,5 +399,20 @@ public override Empty RemoveIssueChain(IssueChainList input) return new Empty(); } + + public override Empty SetSeedRenewHashVerifyKey(StringValue input) + { + AssertInitialized(); + Assert(input != null && !string.IsNullOrEmpty(input.Value), "Invalid param key"); + var key = State.SeedRenewHashVerifyKey.Value; + if (string.IsNullOrEmpty(key)) + { + State.SeedRenewHashVerifyKey.Value = input.Value; + return new Empty(); + } + AssertAdmin(); + State.SeedRenewHashVerifyKey.Value = input.Value; + return new Empty(); + } } } \ No newline at end of file diff --git a/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContract_Helper.cs b/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContract_Helper.cs index ca74340..67a4b46 100644 --- a/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContract_Helper.cs +++ b/contract/Forest.Contracts.SymbolRegistrar/SymbolRegistrarContract_Helper.cs @@ -55,6 +55,16 @@ private void AssertSymbolPattern(string symbol) } } + private void AssertSeedSymbolPattern(string symbol) + { + Assert(symbol.Length > 0 && symbol.Length <= SymbolRegistrarContractConstants.MaxSymbolLength, + "Invalid symbol length."); + + var symbolPartition = symbol.Split(SymbolRegistrarContractConstants.NFTSymbolSeparator); + Assert(symbolPartition.Length == 2, "Invalid symbol."); + Assert(symbolPartition[0] == SymbolRegistrarContractConstants.SeedPrefixPart, "Invalid seed symbol prefix."); + } + private void AssertPriceListLength(PriceList priceList) { Assert(priceList?.Value?.Count == SymbolRegistrarContractConstants.MaxSymbolLength, @@ -155,5 +165,15 @@ private void CheckSymbolExisted(string symbol) var collectionSeed = State.SymbolSeedMap[collectionSymbol]; Assert(string.IsNullOrWhiteSpace(collectionSeed) || State.SeedInfoMap[collectionSeed].ExpireTime < Context.CurrentBlockTime.Seconds, "symbol seed existed"); } + + private void CheckSeedBalanceExisted(string symbol) + { + var balance = State.TokenContract.GetBalance.Call(new GetBalanceInput + { + Symbol = symbol, + Owner = Context.Sender + }); + Assert(balance.Balance == 1, $"you don't have enough {symbol}"); + } } } \ No newline at end of file diff --git a/protobuf/symbol_registrar_contract.proto b/protobuf/symbol_registrar_contract.proto index 2b711d6..25aee1f 100644 --- a/protobuf/symbol_registrar_contract.proto +++ b/protobuf/symbol_registrar_contract.proto @@ -32,6 +32,15 @@ service SymbolRegistrarContract { rpc Buy(BuyInput) returns (google.protobuf.Empty){ } + + rpc RegularSeedRenew(RegularSeedRenewInput) returns (google.protobuf.Empty){ + } + + rpc SpecialSeedRenew(SpecialSeedRenewInput) returns (google.protobuf.Empty){ + } + + rpc BidFinishSeedRenew(BidFinishSeedRenewInput) returns (google.protobuf.Empty){ + } // for Parliament contract // when proposal has been approved special-seeds will be add @@ -88,7 +97,8 @@ service SymbolRegistrarContract { rpc RemoveIssueChain(IssueChainList) returns (google.protobuf.Empty) { } - + rpc SetSeedRenewHashVerifyKey(google.protobuf.StringValue)returns (google.protobuf.Empty){ + } // View @@ -250,6 +260,11 @@ enum SeedType { REGULAR = 4; } +enum RenewType { + SELF = 0; + BID = 1; +} + message SpecialSeed { // special seed SeedType seed_type = 1; @@ -269,13 +284,33 @@ message SpecialSeed { map external_info = 8; } - message BuyInput{ // the symbol to buy string symbol = 1; // which address to issue aelf.Address issue_to = 2; } +//REGULAR +message RegularSeedRenewInput{ + //seedSymbol + string seed_symbol = 1; + Price price = 2; +} + +message SpecialSeedRenewInput{ + aelf.Address buyer = 1; + string seed_symbol = 2; + Price price = 3; + int64 op_time = 4; + string request_hash = 5; +} + +message BidFinishSeedRenewInput{ + string seed_symbol = 1; + int64 bid_finish_time = 2; + int64 op_time = 3; + string request_hash = 4; +} message ControllerList{ repeated aelf.Address controllers = 1; @@ -354,6 +389,19 @@ message Bought { Price price = 4; } +message SeedRenewed { + option (aelf.is_event) = true; + aelf.Address buyer = 1; + string symbol = 2; + string seed_symbol = 3; + int64 exp_time = 4; + Price price = 5; + int64 original_exp_time =6; + SeedType seed_type=7; + RenewType renew_type=8; + int32 chain_id = 9; +} + message SaleControllerAdded { option (aelf.is_event) = true; ControllerList addresses = 1; diff --git a/protobuf/token_contract_impl.proto b/protobuf/token_contract_impl.proto index 5885914..fbccc19 100644 --- a/protobuf/token_contract_impl.proto +++ b/protobuf/token_contract_impl.proto @@ -24,11 +24,11 @@ service TokenContractImpl { option (aelf.base) = "acs1.proto"; option (aelf.base) = "acs2.proto"; option (aelf.base) = "token_contract.proto"; - + // Transfer resource tokens to designated contract address. rpc AdvanceResourceToken (AdvanceResourceTokenInput) returns (google.protobuf.Empty) { } - + // Take token from contract address. rpc TakeResourceTokenBack (TakeResourceTokenBackInput) returns (google.protobuf.Empty) { } @@ -36,15 +36,15 @@ service TokenContractImpl { // Register the token contract address for cross chain. rpc RegisterCrossChainTokenContractAddress (RegisterCrossChainTokenContractAddressInput) returns (google.protobuf.Empty) { } - + // Set the receiver address of the side chain transaction fee. rpc SetFeeReceiver (aelf.Address) returns (google.protobuf.Empty) { } - + // Validates if the token exist. rpc ValidateTokenInfoExists(ValidateTokenInfoExistsInput) returns (google.protobuf.Empty){ } - + // Update the rental unit price of the side chain. rpc UpdateRental (UpdateRentalInput) returns (google.protobuf.Empty) { } @@ -56,23 +56,23 @@ service TokenContractImpl { // Transfer Token to the specified contract. rpc TransferToContract (TransferToContractInput) returns (google.protobuf.Empty) { } - + // Change the governance organization of side chain rental. rpc ChangeSideChainRentalController (AuthorityInfo) returns (google.protobuf.Empty) { } - + // Change the governance organization for tokens to pay transaction fees. rpc ChangeSymbolsToPayTXSizeFeeController(AuthorityInfo) returns (google.protobuf.Empty){ } - + // Change the governance organization for cross-chain token contract address registration. rpc ChangeCrossChainTokenContractRegistrationController (AuthorityInfo) returns (google.protobuf.Empty) { } - + // Change the governance organization of the coefficient of the user transaction fee calculation formula. rpc ChangeUserFeeController (AuthorityInfo) returns (google.protobuf.Empty) { } - + // Change the governance organization of the coefficient of the developer's transaction resource fee calculation formula. rpc ChangeDeveloperController (AuthorityInfo) returns (google.protobuf.Empty) { } @@ -84,18 +84,18 @@ service TokenContractImpl { } rpc SetMaxBatchApproveCount (google.protobuf.Int32Value) 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){ } @@ -104,48 +104,48 @@ service TokenContractImpl { rpc GetFeeReceiver (google.protobuf.Empty) returns (aelf.Address){ option (aelf.is_view) = true; } - + // Query the amount of resources usage currently. rpc GetResourceUsage (google.protobuf.Empty) returns (ResourceUsage) { option (aelf.is_view) = true; } - + // Query the governance organization for tokens to pay transaction fees. rpc GetSymbolsToPayTXSizeFeeController(google.protobuf.Empty) returns (AuthorityInfo){ option (aelf.is_view) = true; } - + // Query the governance organization of the rpc GetCrossChainTokenContractRegistrationController (google.protobuf.Empty) returns (AuthorityInfo) { option (aelf.is_view) = true; } - + // Query the governance organization that calculates the formula coefficient // for the transaction cost the user sends the contract. rpc GetUserFeeController(google.protobuf.Empty) returns (UserFeeController){ option (aelf.is_view) = true; } - + // Query the governing organization of the formula coefficients for calculating developer contract transaction fee. rpc GetDeveloperFeeController (google.protobuf.Empty) returns (DeveloperFeeController) { option (aelf.is_view) = true; } - + // Query the organization that governs the side chain rental fee. rpc GetSideChainRentalControllerCreateInfo (google.protobuf.Empty) returns (AuthorityInfo) { option (aelf.is_view) = true; } - + // Compute the virtual address for locking. rpc GetVirtualAddressForLocking (GetVirtualAddressForLockingInput) returns (aelf.Address) { option (aelf.is_view) = true; } - + // Query how much resource tokens should be paid currently. rpc GetOwningRental (google.protobuf.Empty) returns (OwningRental) { option (aelf.is_view) = true; } - + // Query the unit price of the side chain resource cost, resource cost = unit price * quantity, // the quantity can be queried through GetResourceUsage. rpc GetOwningRentalUnitValue (google.protobuf.Empty) returns (OwningRentalUnitValue) { @@ -159,7 +159,7 @@ service TokenContractImpl { 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; @@ -168,13 +168,13 @@ service TokenContractImpl { rpc GetTransactionFeeDelegateInfo(GetTransactionFeeDelegateInfoInput) returns (token.TransactionFeeDelegations){ option (aelf.is_view) = true; } - + rpc ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput) returns (google.protobuf.Empty) { } - + rpc SetTokenIssuerAndOwnerModificationEnabled(SetTokenIssuerAndOwnerModificationEnabledInput) returns (google.protobuf.Empty) { } - + rpc GetTokenIssuerAndOwnerModificationEnabled(google.protobuf.Empty) returns (google.protobuf.BoolValue) { option (aelf.is_view) = true; } @@ -182,6 +182,9 @@ service TokenContractImpl { rpc GetMaxBatchApproveCount (google.protobuf.Empty) returns (google.protobuf.Int32Value) { } + + rpc ExtendSeedExpirationTime (ExtendSeedExpirationTimeInput) returns (google.protobuf.Empty) { + } } message AdvanceResourceTokenInput { @@ -444,4 +447,17 @@ message ModifyTokenIssuerAndOwnerInput { message SetTokenIssuerAndOwnerModificationEnabledInput{ bool enabled = 1; +} + +message ExtendSeedExpirationTimeInput { + string symbol = 1; + int64 expiration_time = 2; +} + +message SeedExpirationTimeUpdated { + option (aelf.is_event) = true; + int32 chain_id = 1; + string symbol = 2; + int64 old_expiration_time = 3; + int64 new_expiration_time = 4; } \ No newline at end of file diff --git a/test/Forest.Contracts.SymbolRegistrar.Tests/SymbolRegistrarContractTests_Admin.cs b/test/Forest.Contracts.SymbolRegistrar.Tests/SymbolRegistrarContractTests_Admin.cs index 4215cf0..5c5419a 100644 --- a/test/Forest.Contracts.SymbolRegistrar.Tests/SymbolRegistrarContractTests_Admin.cs +++ b/test/Forest.Contracts.SymbolRegistrar.Tests/SymbolRegistrarContractTests_Admin.cs @@ -515,5 +515,18 @@ public async Task IssueChainListTests() } + + [Fact] + public async Task SetSeedRenewHashVerifyKeyTests() + { + await InitializeContract(); + var value = "1a2b3c"; + await AdminSymbolRegistrarContractStub.SetSeedRenewHashVerifyKey.SendAsync(new StringValue() + { + Value = value + }); + var result = AdminSymbolRegistrarContractStub.GetSeedRenewHashVerifyKey.CallAsync(new Empty()); + result.Result.Value.ShouldBe(value); + } } } \ No newline at end of file diff --git a/test/Forest.Contracts.SymbolRegistrar.Tests/SymbolRegistrarContractTests_Buy.cs b/test/Forest.Contracts.SymbolRegistrar.Tests/SymbolRegistrarContractTests_Buy.cs index a3e4212..d1b2983 100644 --- a/test/Forest.Contracts.SymbolRegistrar.Tests/SymbolRegistrarContractTests_Buy.cs +++ b/test/Forest.Contracts.SymbolRegistrar.Tests/SymbolRegistrarContractTests_Buy.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Threading.Tasks; using AElf.Contracts.MultiToken; +using Google.Protobuf.WellKnownTypes; using Shouldly; using Xunit; @@ -313,6 +314,35 @@ public async Task Buy_existsFT_fail() res.ShouldNotBeNull(); res.Message.ShouldContain("Symbol exists"); } - + + [Fact] + public async Task SeedRenew_Test () + { + await Buy_success(); + await User1SymbolRegistrarContractStub.RegularSeedRenew.SendAsync(new RegularSeedRenewInput() + { + SeedSymbol = "SEED-1", + Price = new Price() + { + Symbol = "ELF", + Amount = 4700000000 + } + }); + } + + [Fact] + public async Task BidSeedRenew_Test () + { + await Buy_success(); + await User1SymbolRegistrarContractStub.SetSeedRenewHashVerifyKey.SendAsync(new StringValue{Value = "1a2b3c"}); + await User1SymbolRegistrarContractStub.BidFinishSeedRenew.SendAsync(new BidFinishSeedRenewInput() + { + SeedSymbol = "SEED-1", + BidFinishTime = DateTime.UtcNow.Second, + OpTime = DateTime.UtcNow.Second, + RequestHash = "1234" + }); + + } } } \ No newline at end of file