Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add seed renew #203

Merged
merged 9 commits into from
Dec 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
<ContractReference Include="..\..\protobuf\acs0.proto">
<Link>Protobuf\Proto\acs0.proto</Link>
</ContractReference>
<ContractReference Include="..\..\protobuf\acs1.proto">
<Link>Protobuf\Proto\reference\acs1.proto</Link>
</ContractReference>
<ContractReference Include="..\..\protobuf\acs2.proto">
<Link>Protobuf\Proto\reference\acs2.proto</Link>
</ContractReference>
<ContractReference Include="..\..\protobuf\acs3.proto">
<Link>Protobuf\Proto\acs3.proto</Link>
</ContractReference>
Expand All @@ -40,6 +46,13 @@
<ContractReference Include="..\..\protobuf\token_contract.proto">
<Link>Protobuf\Proto\reference\token_contract.proto</Link>
</ContractReference>

<ContractReference Include="..\..\protobuf\token_contract_impl.proto">
<Link>Protobuf\Proto\token_contract_impl.proto</Link>
</ContractReference>
<ContractReference Include="..\..\protobuf\transaction_fee.proto">
<Link>Protobuf\Proto\reference\transaction_fee.proto</Link>
</ContractReference>
</ItemGroup>
<ItemGroup>
<ContractBase Include="..\..\protobuf\acs12.proto">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,7 @@ internal AElf.Contracts.ProxyAccountContract.ProxyAccountContractContainer.Proxy
get;
set;
}

internal TokenContractImplContainer.TokenContractImplReferenceState TokenImplContract { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,10 @@ public partial class SymbolRegistrarContractState : ContractState
public SingletonState<IssueChainList> IssueChainList { get; set; }
public SingletonState<Address> SeedCollectionOwner { get; set; }
public SingletonState<Hash> ProxyAccountHash { get; set; }

public SingletonState<string> SeedRenewHashVerifyKey { get; set; }
//seedSymbol -> renewTime
public MappedState<string, long> SeedRenewTimeMap { get; set; }

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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");
}

}


}
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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}");
}
}
}
52 changes: 50 additions & 2 deletions protobuf/symbol_registrar_contract.proto
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -88,7 +97,8 @@ service SymbolRegistrarContract {

rpc RemoveIssueChain(IssueChainList) returns (google.protobuf.Empty) {
}

rpc SetSeedRenewHashVerifyKey(google.protobuf.StringValue)returns (google.protobuf.Empty){
}

// View

Expand Down Expand Up @@ -250,6 +260,11 @@ enum SeedType {
REGULAR = 4;
}

enum RenewType {
SELF = 0;
BID = 1;
}

message SpecialSeed {
// special seed
SeedType seed_type = 1;
Expand All @@ -269,13 +284,33 @@ message SpecialSeed {
map<string, string> 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;
Expand Down Expand Up @@ -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;
Expand Down
Loading
Loading