From 8945a1751e5a4e1d74b47eb537c1fd48e9aa4929 Mon Sep 17 00:00:00 2001 From: aelf-lu Date: Fri, 9 Jun 2023 11:36:20 +0800 Subject: [PATCH 01/20] initilize commit --- .../AElf.Contracts.TimelockContract.csproj | 23 + timelock-contract/src/ContractsReferences.cs | 9 + .../src/Protobuf/base/acs1.proto | 53 ++ .../Protobuf/contract/timelock_contract.proto | 86 +++ .../src/Protobuf/message/authority_info.proto | 10 + .../Protobuf/reference/token_contract.proto | 511 ++++++++++++++++++ timelock-contract/src/TimelockContract.cs | 116 ++++ .../src/TimelockContractConstants.cs | 13 + .../src/TimelockContractState.cs | 13 + ...lf.Contracts.TimelockContract.Tests.csproj | 45 ++ .../test/Protobuf/base/acs1.proto | 53 ++ .../Protobuf/message/authority_info.proto | 10 + .../Protobuf/stub/timelock_contract.proto | 86 +++ .../test/Protobuf/stub/token_contract.proto | 511 ++++++++++++++++++ .../test/TimelockContractTests.cs | 82 +++ .../TimelockContractTestsInitialization.cs | 27 + timelock-contract/test/_Setup.cs | 22 + 17 files changed, 1670 insertions(+) create mode 100644 timelock-contract/src/AElf.Contracts.TimelockContract.csproj create mode 100644 timelock-contract/src/ContractsReferences.cs create mode 100644 timelock-contract/src/Protobuf/base/acs1.proto create mode 100644 timelock-contract/src/Protobuf/contract/timelock_contract.proto create mode 100644 timelock-contract/src/Protobuf/message/authority_info.proto create mode 100644 timelock-contract/src/Protobuf/reference/token_contract.proto create mode 100644 timelock-contract/src/TimelockContract.cs create mode 100644 timelock-contract/src/TimelockContractConstants.cs create mode 100644 timelock-contract/src/TimelockContractState.cs create mode 100644 timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj create mode 100644 timelock-contract/test/Protobuf/base/acs1.proto create mode 100644 timelock-contract/test/Protobuf/message/authority_info.proto create mode 100644 timelock-contract/test/Protobuf/stub/timelock_contract.proto create mode 100644 timelock-contract/test/Protobuf/stub/token_contract.proto create mode 100644 timelock-contract/test/TimelockContractTests.cs create mode 100644 timelock-contract/test/TimelockContractTestsInitialization.cs create mode 100644 timelock-contract/test/_Setup.cs diff --git a/timelock-contract/src/AElf.Contracts.TimelockContract.csproj b/timelock-contract/src/AElf.Contracts.TimelockContract.csproj new file mode 100644 index 0000000..b815e46 --- /dev/null +++ b/timelock-contract/src/AElf.Contracts.TimelockContract.csproj @@ -0,0 +1,23 @@ + + + net6.0 + AElf.Contracts.Timelock + true + + + true + + + true + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + \ No newline at end of file diff --git a/timelock-contract/src/ContractsReferences.cs b/timelock-contract/src/ContractsReferences.cs new file mode 100644 index 0000000..76a0c5e --- /dev/null +++ b/timelock-contract/src/ContractsReferences.cs @@ -0,0 +1,9 @@ +using AElf.Contracts.MultiToken; + +namespace AElf.Contracts.Timelock +{ + public partial class TimelockContractState + { + internal TokenContractContainer.TokenContractReferenceState TokenContract { get; set; } + } +} \ No newline at end of file diff --git a/timelock-contract/src/Protobuf/base/acs1.proto b/timelock-contract/src/Protobuf/base/acs1.proto new file mode 100644 index 0000000..74b3a98 --- /dev/null +++ b/timelock-contract/src/Protobuf/base/acs1.proto @@ -0,0 +1,53 @@ +/** + * AElf Standards ACS1(Transaction Fee Standard) + * + * Used to manage the transaction fee. + */ +syntax = "proto3"; + +package acs1; + +import public "aelf/options.proto"; +import public "google/protobuf/empty.proto"; +import public "google/protobuf/wrappers.proto"; +import "aelf/core.proto"; +import "Protobuf/message/authority_info.proto"; + +option (aelf.identity) = "acs1"; +option csharp_namespace = "AElf.Standards.ACS1"; + +service MethodFeeProviderContract { + + // Set the method fees for the specified method. Note that this will override all fees of the method. + rpc SetMethodFee (MethodFees) returns (google.protobuf.Empty) { + } + + // Change the method fee controller, the default is parliament and default organization. + rpc ChangeMethodFeeController (AuthorityInfo) returns (google.protobuf.Empty) { + } + + // Query method fee information by method name. + rpc GetMethodFee (google.protobuf.StringValue) returns (MethodFees) { + option (aelf.is_view) = true; + } + + // Query the method fee controller. + rpc GetMethodFeeController (google.protobuf.Empty) returns (AuthorityInfo) { + option (aelf.is_view) = true; + } +} + +message MethodFees { + // The name of the method to be charged. + string method_name = 1; + // List of fees to be charged. + repeated MethodFee fees = 2; + bool is_size_fee_free = 3;// Optional based on the implementation of SetMethodFee method. +} + +message MethodFee { + // The token symbol of the method fee. + string symbol = 1; + // The amount of fees to be charged. + int64 basic_fee = 2; +} \ No newline at end of file diff --git a/timelock-contract/src/Protobuf/contract/timelock_contract.proto b/timelock-contract/src/Protobuf/contract/timelock_contract.proto new file mode 100644 index 0000000..1c89147 --- /dev/null +++ b/timelock-contract/src/Protobuf/contract/timelock_contract.proto @@ -0,0 +1,86 @@ +syntax = "proto3"; + +import "aelf/core.proto"; +import "aelf/options.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/wrappers.proto"; +import "google/protobuf/timestamp.proto"; + +option csharp_namespace = "AElf.Contracts.Timelock"; + +service TimelockContract { + option (aelf.csharp_state) = "AElf.Contracts.Timelock.TimelockContractState"; + + // Actions + rpc SetDelay (DelayInput) returns (google.protobuf.Empty) {} + rpc AcceptAdmin (google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc SetPendingAdmin (SetPendingAdminInput) returns (google.protobuf.Empty) {} + rpc QueueTransaction (TransactionInput) returns (google.protobuf.Empty) {} + rpc ExecuteTransaction (TransactionInput) returns (google.protobuf.Empty) {} + rpc CancelTransaction (TransactionInput) returns (google.protobuf.Empty) {} + +} + +// Params +message DelayInput { + uint64 delay = 1; +} + +message SetPendingAdminInput { + aelf.Address pending_admin = 1; +} + +message TransactionInput { + aelf.Hash txn_hash = 1; + aelf.Address target = 2; + uint64 amount = 3; + string signature = 4; + string data = 5; + google.protobuf.Timestamp eta = 6; +} + +// Events +message NewAdmin { + option (aelf.is_event) = true; + aelf.Address new_admin = 1; +} + +message NewPendingAdmin { + option (aelf.is_event) = true; + aelf.Address new_pending_admin = 1; +} + +message NewDelay { + option (aelf.is_event) = true; + uint64 new_delay = 1; +} + +message QueueTransaction { + option (aelf.is_event) = true; + aelf.Hash txn_hash = 1; + aelf.Address target = 2; + uint64 amount = 3; + string signature = 4; + string data = 5; + google.protobuf.Timestamp eta = 6; +} + +message ExecuteTransaction { + option (aelf.is_event) = true; + aelf.Hash txn_hash = 1; + aelf.Address target = 2; + uint64 amount = 3; + string signature = 4; + string data = 5; + google.protobuf.Timestamp eta = 6; +} + +message CancelTransaction { + option (aelf.is_event) = true; + aelf.Hash txn_hash = 1; + aelf.Address target = 2; + uint64 amount = 3; + string signature = 4; + string data = 5; + google.protobuf.Timestamp eta = 6; +} \ No newline at end of file diff --git a/timelock-contract/src/Protobuf/message/authority_info.proto b/timelock-contract/src/Protobuf/message/authority_info.proto new file mode 100644 index 0000000..1b7bc25 --- /dev/null +++ b/timelock-contract/src/Protobuf/message/authority_info.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +import "aelf/core.proto"; + +option csharp_namespace = "AElf.Contracts.Timelock"; + +message AuthorityInfo { + aelf.Address contract_address = 1; + aelf.Address owner_address = 2; +} \ No newline at end of file diff --git a/timelock-contract/src/Protobuf/reference/token_contract.proto b/timelock-contract/src/Protobuf/reference/token_contract.proto new file mode 100644 index 0000000..b418d31 --- /dev/null +++ b/timelock-contract/src/Protobuf/reference/token_contract.proto @@ -0,0 +1,511 @@ +syntax = "proto3"; + +package token; + +import "aelf/core.proto"; +import "aelf/options.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/wrappers.proto"; + +option csharp_namespace = "AElf.Contracts.MultiToken"; + +service TokenContract { + // Actions + + // Multiple Token + rpc Create (CreateInput) returns (google.protobuf.Empty) { + } + rpc Issue (IssueInput) returns (google.protobuf.Empty) { + } + rpc Transfer (TransferInput) returns (google.protobuf.Empty) { + } + rpc TransferFrom (TransferFromInput) returns (google.protobuf.Empty) { + } + rpc Approve (ApproveInput) returns (google.protobuf.Empty) { + } + rpc UnApprove (UnApproveInput) returns (google.protobuf.Empty) { + } + rpc Lock (LockInput) returns (google.protobuf.Empty) { + } + rpc Unlock (UnlockInput) returns (google.protobuf.Empty) { + } + rpc Burn (BurnInput) returns (google.protobuf.Empty) { + } + rpc ChangeTokenIssuer (ChangeTokenIssuerInput) returns (google.protobuf.Empty) { + } + + // Cross Chain Exchange + rpc SetPrimaryTokenSymbol (SetPrimaryTokenSymbolInput) returns (google.protobuf.Empty) { + } + rpc CrossChainTransfer (CrossChainTransferInput) returns (google.protobuf.Empty) { + } + rpc CrossChainReceiveToken (CrossChainReceiveTokenInput) returns (google.protobuf.Empty) { + } + rpc CrossChainCreateToken(CrossChainCreateTokenInput) returns (google.protobuf.Empty) { + } + rpc InitializeFromParentChain (InitializeFromParentChainInput) returns (google.protobuf.Empty) { + } + + // ACS1 - Charge Transaction Fees + rpc ClaimTransactionFees (TotalTransactionFeesMap) returns (google.protobuf.Empty) { + } + rpc ChargeTransactionFees (ChargeTransactionFeesInput) returns (ChargeTransactionFeesOutput) { + } + + // ACS5 - Contract Method Call Threshold + rpc CheckThreshold (CheckThresholdInput) returns (google.protobuf.Empty) { + } + + // ACS8 - Charge Resource Tokens + rpc InitialCoefficients (google.protobuf.Empty) returns (google.protobuf.Empty){ + } + rpc DonateResourceToken (TotalResourceTokensMaps) returns (google.protobuf.Empty) { + } + rpc ChargeResourceToken (ChargeResourceTokenInput) returns (google.protobuf.Empty) { + } + rpc CheckResourceToken (google.protobuf.Empty) returns (google.protobuf.Empty) { + } + rpc SetSymbolsToPayTxSizeFee (SymbolListToPayTxSizeFee) returns (google.protobuf.Empty){ + } + rpc UpdateCoefficientsForSender (UpdateCoefficientsInput) returns (google.protobuf.Empty) { + } + rpc UpdateCoefficientsForContract (UpdateCoefficientsInput) returns (google.protobuf.Empty) { + } + rpc InitializeAuthorizedController(google.protobuf.Empty) returns (google.protobuf.Empty){ + } + + // Views + rpc GetTokenInfo (GetTokenInfoInput) returns (TokenInfo) { + option (aelf.is_view) = true; + } + rpc GetNativeTokenInfo (google.protobuf.Empty) returns (TokenInfo) { + option (aelf.is_view) = true; + } + rpc GetResourceTokenInfo (google.protobuf.Empty) returns (TokenInfoList) { + option (aelf.is_view) = true; + } + rpc GetBalance (GetBalanceInput) returns (GetBalanceOutput) { + option (aelf.is_view) = true; + } + rpc GetAllowance (GetAllowanceInput) returns (GetAllowanceOutput) { + option (aelf.is_view) = true; + } + rpc IsInWhiteList (IsInWhiteListInput) returns (google.protobuf.BoolValue) { + option (aelf.is_view) = true; + } + rpc GetLockedAmount (GetLockedAmountInput) returns (GetLockedAmountOutput) { + option (aelf.is_view) = true; + } + rpc GetCrossChainTransferTokenContractAddress (GetCrossChainTransferTokenContractAddressInput) returns (aelf.Address) { + option (aelf.is_view) = true; + } + rpc GetPrimaryTokenSymbol (google.protobuf.Empty) returns (google.protobuf.StringValue) { + option (aelf.is_view) = true; + } + rpc GetCalculateFeeCoefficientsForContract (google.protobuf.Int32Value) returns (CalculateFeeCoefficients) { + option (aelf.is_view) = true; + } + rpc GetCalculateFeeCoefficientsForSender (google.protobuf.Empty) returns (CalculateFeeCoefficients) { + option (aelf.is_view) = true; + } + // referenced by src + rpc GetSymbolsToPayTxSizeFee (google.protobuf.Empty) returns (SymbolListToPayTxSizeFee){ + option (aelf.is_view) = true; + } + rpc GetLatestTotalTransactionFeesMapHash (google.protobuf.Empty) returns (aelf.Hash){ + option (aelf.is_view) = true; + } + rpc GetLatestTotalResourceTokensMapsHash (google.protobuf.Empty) returns (aelf.Hash){ + option (aelf.is_view) = true; + } + rpc IsTokenAvailableForMethodFee (google.protobuf.StringValue) returns (google.protobuf.BoolValue) { + option (aelf.is_view) = true; + } +} + +message TokenInfo { + string symbol = 1; + string token_name = 2; + int64 supply = 3; + int64 total_supply = 4; + int32 decimals = 5; + aelf.Address issuer = 6; + bool is_burnable = 7; + int32 issue_chain_id = 9; + int64 issued = 10; +} + +message CreateInput { + string symbol = 1; + string token_name = 2; + int64 total_supply = 3; + int32 decimals = 4; + aelf.Address issuer = 5; + bool is_burnable = 6; + repeated aelf.Address lock_white_list = 7; + int32 issue_chain_id = 8; +} + +message RegisterNativeTokenInfoInput { + string symbol = 1; + string token_name = 2; + int64 total_supply = 3; + int32 decimals = 4; + aelf.Address issuer = 5; + bool is_burnable = 6; + int32 issue_chain_id = 7; +} + +message SetPrimaryTokenSymbolInput { + string symbol = 1; +} + +message IssueInput { + string symbol = 1; + int64 amount = 2; + string memo = 3; + aelf.Address to = 4; +} + +message TransferInput { + aelf.Address to = 1; + string symbol = 2; + int64 amount = 3; + string memo = 4; +} + +message LockInput { + aelf.Address address = 1; // The one want to lock his token. + aelf.Hash lock_id = 2; + string symbol = 3; + string usage = 4; + int64 amount = 5; +} + +message UnlockInput { + aelf.Address address = 1; // The one want to lock his token. + aelf.Hash lock_id = 2; + string symbol = 3; + string usage = 4; + int64 amount = 5; +} + +message TransferFromInput { + aelf.Address from = 1; + aelf.Address to = 2; + string symbol = 3; + int64 amount = 4; + string memo = 5; +} + +message ApproveInput { + aelf.Address spender = 1; + string symbol = 2; + int64 amount = 3; +} + +message UnApproveInput { + aelf.Address spender = 1; + string symbol = 2; + int64 amount = 3; +} + +message BurnInput { + string symbol = 1; + int64 amount = 2; +} + +message ChargeResourceTokenInput { + map cost_dic = 1; + aelf.Address caller = 2; +} + +message TransactionFeeBill { + map fees_map = 1; +} + +message CheckThresholdInput { + aelf.Address sender = 1; + map symbol_to_threshold = 2; + bool is_check_allowance = 3; +} + +message GetTokenInfoInput { + string symbol = 1; +} + +message GetBalanceInput { + string symbol = 1; + aelf.Address owner = 2; +} + +message GetBalanceOutput { + string symbol = 1; + aelf.Address owner = 2; + int64 balance = 3; +} + +message GetAllowanceInput { + string symbol = 1; + aelf.Address owner = 2; + aelf.Address spender = 3; +} + +message GetAllowanceOutput { + string symbol = 1; + aelf.Address owner = 2; + aelf.Address spender = 3; + int64 allowance = 4; +} + +message CrossChainTransferInput { + aelf.Address to = 1; + string symbol = 2; + int64 amount = 3; + string memo = 4; + int32 to_chain_id = 5; + int32 issue_chain_id = 6; +} + +message CrossChainReceiveTokenInput { + int32 from_chain_id = 1; + int64 parent_chain_height = 2; + bytes transfer_transaction_bytes = 3; + aelf.MerklePath merkle_path = 4; +} + +message IsInWhiteListInput { + string symbol = 1; + aelf.Address address = 2; +} + +message SymbolToPayTxSizeFee{ + string token_symbol = 1; + int32 base_token_weight = 2; + int32 added_token_weight = 3; +} + +message SymbolListToPayTxSizeFee{ + repeated SymbolToPayTxSizeFee symbols_to_pay_tx_size_fee = 1; +} + +message ChargeTransactionFeesInput { + string method_name = 1; + aelf.Address contract_address = 2; + int64 transaction_size_fee = 3; + repeated SymbolToPayTxSizeFee symbols_to_pay_tx_size_fee = 4; +} + +message ChargeTransactionFeesOutput { + bool success = 1; + string charging_information = 2; +} + +message ExtraTokenListModified { + option (aelf.is_event) = true; + SymbolListToPayTxSizeFee symbol_list_to_pay_tx_size_fee = 1; +} + +message ReturnTaxInput { + int64 balance_before_selling = 1; + aelf.Address return_tax_receiver_address = 2; +} + +message GetLockedAmountInput { + aelf.Address address = 1; + string symbol = 2; + aelf.Hash lock_id = 3; +} + +message GetLockedAmountOutput { + aelf.Address address = 1; + string symbol = 2; + aelf.Hash lock_id = 3; + int64 amount = 4; +} + +message TokenInfoList { + repeated TokenInfo value = 1; +} + +message GetCrossChainTransferTokenContractAddressInput { + int32 chainId = 1; +} + +message CrossChainCreateTokenInput { + int32 from_chain_id = 1; + int64 parent_chain_height = 2; + bytes transaction_bytes = 3; + aelf.MerklePath merkle_path = 4; +} + +message InitializeFromParentChainInput { + map resource_amount = 1; + map registered_other_token_contract_addresses = 2; + aelf.Address creator = 3; +} + +message UpdateCoefficientsInput { + repeated int32 piece_numbers = 1;// To specify pieces gonna update. + CalculateFeeCoefficients coefficients = 2; +} + +enum FeeTypeEnum { + READ = 0; + STORAGE = 1; + WRITE = 2; + TRAFFIC = 3; + TX = 4; +} + +// Coefficients of one single piece. +// The first char is its type: liner / power. +// The second char is its piece upper bound. +message CalculateFeePieceCoefficients { + repeated int32 value = 1; +} + +// Coefficients of one single type ot token, like READ, WRITE, etc. +message CalculateFeeCoefficients { + int32 fee_token_type = 1; + repeated CalculateFeePieceCoefficients piece_coefficients_list = 2; +} + +// To include coefficients of all tokens. +message AllCalculateFeeCoefficients { + repeated CalculateFeeCoefficients value = 1; +} + +message TotalTransactionFeesMap +{ + map value = 1; + aelf.Hash block_hash = 2; + int64 block_height = 3; +} + +message TotalResourceTokensMaps { + repeated ContractTotalResourceTokens value = 1; + aelf.Hash block_hash = 2; + int64 block_height = 3; +} + +message ContractTotalResourceTokens { + aelf.Address contract_address = 1; + TotalResourceTokensMap tokens_map = 2; +} + +message TotalResourceTokensMap +{ + map value = 1; +} + +message ChangeTokenIssuerInput +{ + string symbol = 1; + aelf.Address new_token_Issuer = 2; +} + +// Events + +message Transferred { + option (aelf.is_event) = true; + aelf.Address from = 1 [(aelf.is_indexed) = true]; + aelf.Address to = 2 [(aelf.is_indexed) = true]; + string symbol = 3 [(aelf.is_indexed) = true]; + int64 amount = 4; + string memo = 5; +} + +message Approved { + option (aelf.is_event) = true; + aelf.Address owner = 1 [(aelf.is_indexed) = true]; + aelf.Address spender = 2 [(aelf.is_indexed) = true]; + string symbol = 3 [(aelf.is_indexed) = true]; + int64 amount = 4; +} + +message UnApproved { + option (aelf.is_event) = true; + aelf.Address owner = 1 [(aelf.is_indexed) = true]; + aelf.Address spender = 2 [(aelf.is_indexed) = true]; + string symbol = 3 [(aelf.is_indexed) = true]; + int64 amount = 4; +} + +message Burned +{ + option (aelf.is_event) = true; + aelf.Address burner = 1 [(aelf.is_indexed) = true]; + string symbol = 2 [(aelf.is_indexed) = true]; + int64 amount = 3; +} + +message ChainPrimaryTokenSymbolSet { + option (aelf.is_event) = true; + string token_symbol = 1; +} + +message TransactionSizeFeeUnitPriceUpdated { + option (aelf.is_event) = true; + int64 unit_price = 1; +} + +// The modification of each fee type will fire one event. +message CalculateFeeAlgorithmUpdated { + option (aelf.is_event) = true; + AllCalculateFeeCoefficients all_type_fee_coefficients = 1; +} + +message RentalCharged { + option (aelf.is_event) = true; + string symbol = 1; + int64 amount = 2; +} + +message RentalAccountBalanceInsufficient { + option (aelf.is_event) = true; + string symbol = 1; + int64 amount = 2; +} + +message TokenCreated { + option (aelf.is_event) = true; + string symbol = 1; + string token_name = 2; + int64 total_supply = 3; + int32 decimals = 4; + aelf.Address issuer = 5; + bool is_burnable = 6; + int32 issue_chain_id = 7; +} + +message Issued { + option (aelf.is_event) = true; + string symbol = 1; + int64 amount = 2; + string memo = 3; + aelf.Address to = 4; +} + +message CrossChainTransferred { + option (aelf.is_event) = true; + aelf.Address from = 1; + aelf.Address to = 2; + string symbol = 3; + int64 amount = 4; + string memo = 5; + int32 to_chain_id = 6; + int32 issue_chain_id = 7; +} + +message CrossChainReceived { + option (aelf.is_event) = true; + aelf.Address from = 1; + aelf.Address to = 2; + string symbol = 3; + int64 amount = 4; + string memo = 5; + int32 from_chain_id = 6; + int32 issue_chain_id = 7; + int64 parent_chain_height = 8; +} \ No newline at end of file diff --git a/timelock-contract/src/TimelockContract.cs b/timelock-contract/src/TimelockContract.cs new file mode 100644 index 0000000..99c1c04 --- /dev/null +++ b/timelock-contract/src/TimelockContract.cs @@ -0,0 +1,116 @@ +using AElf.Contracts.MultiToken; +using AElf.CSharp.Core.Extension; +using AElf.Sdk.CSharp; +using AElf.Types; +using Google.Protobuf.WellKnownTypes; + +namespace AElf.Contracts.Timelock +{ + public partial class TimelockContract : TimelockContractContainer.TimelockContractBase + { + public override Empty SetDelay(DelayInput input) + { + Assert(State.Admin.Value == Context.Sender, "No permission"); + Assert(input != null, "Delay must not be null"); + Assert(input.Delay >= TimelockContractConstants.MIN_DELAY, "Delay must exceed minimum delay"); + Assert(input.Delay <= TimelockContractConstants.MAX_DELAY, "Delay must exceed maximum delay"); + State.Delay.Value = (long) input.Delay; + + Context.Fire(new NewDelay + { + NewDelay_ = input.Delay + }); + return new Empty(); + } + + public override Empty AcceptAdmin(Empty input) + { + Assert(Context.Sender == State.PendingAdmin.Value, "No permission"); + State.Admin.Value = Context.Sender; + State.PendingAdmin.Value = null; + Context.Fire(new NewAdmin + { + NewAdmin_ = Context.Sender + }); + return new Empty(); + } + + public override Empty SetPendingAdmin(SetPendingAdminInput input) + { + Assert(Context.Sender == Context.Self, "No permission"); + State.PendingAdmin.Value = input.PendingAdmin; + Context.Fire(new NewPendingAdmin + { + NewPendingAdmin_ = input.PendingAdmin + }); + return new Empty(); + } + + public override Empty QueueTransaction(TransactionInput input) + { + Assert(Context.Sender == State.Admin.Value, "No permission"); + Assert(input.Eta >= Context.CurrentBlockTime.AddDays(State.Delay.Value), "Estimated execution block must satisfy delay"); + Hash txnHash = HashHelper.ComputeFrom(input); + State.TransactionQueue[txnHash] = Context.Sender; + Context.Fire(new QueueTransaction + { + TxnHash = txnHash, + Target = input.Target, + Amount = input.Amount, + Signature = input.Signature, + Data = input.Data, + Eta = input.Eta + }); + return new Empty(); + } + + public override Empty CancelTransaction(TransactionInput input) + { + Assert(Context.Sender == State.Admin.Value, "No permission"); + Hash txnHash = HashHelper.ComputeFrom(input); + State.TransactionQueue.Remove(txnHash); + Context.Fire(new CancelTransaction + { + TxnHash = txnHash, + Target = input.Target, + Amount = input.Amount, + Signature = input.Signature, + Data = input.Data, + Eta = input.Eta + }); + return new Empty(); + } + + public override Empty ExecuteTransaction(TransactionInput input) + { + Assert(Context.Sender == State.Admin.Value, "No permission"); + Hash txnHash = HashHelper.ComputeFrom(input); + Assert(State.TransactionQueue[txnHash] != null, "executeTransaction: Transaction hasn't been queued"); + Assert(Context.CurrentBlockTime >= input.Eta, "executeTransaction: Transaction hasn't surpassed time lock"); + Assert(Context.CurrentBlockTime <= input.Eta.AddDays(TimelockContractConstants.GRACE_PERIOD), "executeTransaction: Transaction is stale"); + + Address from = State.TransactionQueue[txnHash]; + State.TokenContract.Value = Context.GetContractAddressByName(SmartContractConstants.TokenContractSystemName); + State.TokenContract.TransferFrom.Send(new TransferFromInput + { + From = from, + To = input.Target, + Amount = (long) input.Amount, + Symbol = TimelockContractConstants.SYMBOL, + Memo = "Execution" + }); + Context.Fire(new ExecuteTransaction + { + TxnHash = txnHash, + Target = input.Target, + Amount = input.Amount, + Signature = input.Signature, + Data = input.Data, + Eta = input.Eta + }); + State.TransactionQueue.Remove(txnHash); + return new Empty(); + } + } + +} \ No newline at end of file diff --git a/timelock-contract/src/TimelockContractConstants.cs b/timelock-contract/src/TimelockContractConstants.cs new file mode 100644 index 0000000..81eea70 --- /dev/null +++ b/timelock-contract/src/TimelockContractConstants.cs @@ -0,0 +1,13 @@ +namespace AElf.Contracts.Timelock +{ + public static class TimelockContractConstants + { + public const string SYMBOL = "ELF"; + // 1 min + public const long MIN_DELAY = 60; + // 1 hour + public const long MAX_DELAY = 3600; + // 30 min + public const long GRACE_PERIOD = 1800; + } +} \ No newline at end of file diff --git a/timelock-contract/src/TimelockContractState.cs b/timelock-contract/src/TimelockContractState.cs new file mode 100644 index 0000000..e3a167f --- /dev/null +++ b/timelock-contract/src/TimelockContractState.cs @@ -0,0 +1,13 @@ +using AElf.Sdk.CSharp.State; +using AElf.Types; + +namespace AElf.Contracts.Timelock +{ + public partial class TimelockContractState : ContractState + { + public SingletonState
Admin { get; set; } + public SingletonState
PendingAdmin { get; set; } + public SingletonState Delay { get; set; } + public MappedState TransactionQueue { get; set; } + } +} \ No newline at end of file diff --git a/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj b/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj new file mode 100644 index 0000000..f231c53 --- /dev/null +++ b/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj @@ -0,0 +1,45 @@ + + + + net6.0 + AElf.Contracts.Timelock + + + + 0436 + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + nocontract + + + nocontract + + + stub + + + + + + + + \ No newline at end of file diff --git a/timelock-contract/test/Protobuf/base/acs1.proto b/timelock-contract/test/Protobuf/base/acs1.proto new file mode 100644 index 0000000..74b3a98 --- /dev/null +++ b/timelock-contract/test/Protobuf/base/acs1.proto @@ -0,0 +1,53 @@ +/** + * AElf Standards ACS1(Transaction Fee Standard) + * + * Used to manage the transaction fee. + */ +syntax = "proto3"; + +package acs1; + +import public "aelf/options.proto"; +import public "google/protobuf/empty.proto"; +import public "google/protobuf/wrappers.proto"; +import "aelf/core.proto"; +import "Protobuf/message/authority_info.proto"; + +option (aelf.identity) = "acs1"; +option csharp_namespace = "AElf.Standards.ACS1"; + +service MethodFeeProviderContract { + + // Set the method fees for the specified method. Note that this will override all fees of the method. + rpc SetMethodFee (MethodFees) returns (google.protobuf.Empty) { + } + + // Change the method fee controller, the default is parliament and default organization. + rpc ChangeMethodFeeController (AuthorityInfo) returns (google.protobuf.Empty) { + } + + // Query method fee information by method name. + rpc GetMethodFee (google.protobuf.StringValue) returns (MethodFees) { + option (aelf.is_view) = true; + } + + // Query the method fee controller. + rpc GetMethodFeeController (google.protobuf.Empty) returns (AuthorityInfo) { + option (aelf.is_view) = true; + } +} + +message MethodFees { + // The name of the method to be charged. + string method_name = 1; + // List of fees to be charged. + repeated MethodFee fees = 2; + bool is_size_fee_free = 3;// Optional based on the implementation of SetMethodFee method. +} + +message MethodFee { + // The token symbol of the method fee. + string symbol = 1; + // The amount of fees to be charged. + int64 basic_fee = 2; +} \ No newline at end of file diff --git a/timelock-contract/test/Protobuf/message/authority_info.proto b/timelock-contract/test/Protobuf/message/authority_info.proto new file mode 100644 index 0000000..1b7bc25 --- /dev/null +++ b/timelock-contract/test/Protobuf/message/authority_info.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +import "aelf/core.proto"; + +option csharp_namespace = "AElf.Contracts.Timelock"; + +message AuthorityInfo { + aelf.Address contract_address = 1; + aelf.Address owner_address = 2; +} \ No newline at end of file diff --git a/timelock-contract/test/Protobuf/stub/timelock_contract.proto b/timelock-contract/test/Protobuf/stub/timelock_contract.proto new file mode 100644 index 0000000..1c89147 --- /dev/null +++ b/timelock-contract/test/Protobuf/stub/timelock_contract.proto @@ -0,0 +1,86 @@ +syntax = "proto3"; + +import "aelf/core.proto"; +import "aelf/options.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/wrappers.proto"; +import "google/protobuf/timestamp.proto"; + +option csharp_namespace = "AElf.Contracts.Timelock"; + +service TimelockContract { + option (aelf.csharp_state) = "AElf.Contracts.Timelock.TimelockContractState"; + + // Actions + rpc SetDelay (DelayInput) returns (google.protobuf.Empty) {} + rpc AcceptAdmin (google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc SetPendingAdmin (SetPendingAdminInput) returns (google.protobuf.Empty) {} + rpc QueueTransaction (TransactionInput) returns (google.protobuf.Empty) {} + rpc ExecuteTransaction (TransactionInput) returns (google.protobuf.Empty) {} + rpc CancelTransaction (TransactionInput) returns (google.protobuf.Empty) {} + +} + +// Params +message DelayInput { + uint64 delay = 1; +} + +message SetPendingAdminInput { + aelf.Address pending_admin = 1; +} + +message TransactionInput { + aelf.Hash txn_hash = 1; + aelf.Address target = 2; + uint64 amount = 3; + string signature = 4; + string data = 5; + google.protobuf.Timestamp eta = 6; +} + +// Events +message NewAdmin { + option (aelf.is_event) = true; + aelf.Address new_admin = 1; +} + +message NewPendingAdmin { + option (aelf.is_event) = true; + aelf.Address new_pending_admin = 1; +} + +message NewDelay { + option (aelf.is_event) = true; + uint64 new_delay = 1; +} + +message QueueTransaction { + option (aelf.is_event) = true; + aelf.Hash txn_hash = 1; + aelf.Address target = 2; + uint64 amount = 3; + string signature = 4; + string data = 5; + google.protobuf.Timestamp eta = 6; +} + +message ExecuteTransaction { + option (aelf.is_event) = true; + aelf.Hash txn_hash = 1; + aelf.Address target = 2; + uint64 amount = 3; + string signature = 4; + string data = 5; + google.protobuf.Timestamp eta = 6; +} + +message CancelTransaction { + option (aelf.is_event) = true; + aelf.Hash txn_hash = 1; + aelf.Address target = 2; + uint64 amount = 3; + string signature = 4; + string data = 5; + google.protobuf.Timestamp eta = 6; +} \ No newline at end of file diff --git a/timelock-contract/test/Protobuf/stub/token_contract.proto b/timelock-contract/test/Protobuf/stub/token_contract.proto new file mode 100644 index 0000000..31313c8 --- /dev/null +++ b/timelock-contract/test/Protobuf/stub/token_contract.proto @@ -0,0 +1,511 @@ +syntax = "proto3"; + +package token; + +import "aelf/core.proto"; +import "aelf/options.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/wrappers.proto"; + +option csharp_namespace = "AElf.Contracts.MultiToken"; + +service TokenContract { + // Actions + + // Multiple Token + rpc Create (CreateInput) returns (google.protobuf.Empty) { + } + rpc Issue (IssueInput) returns (google.protobuf.Empty) { + } + rpc Transfer (TransferInput) returns (google.protobuf.Empty) { + } + rpc TransferFrom (TransferFromInput) returns (google.protobuf.Empty) { + } + rpc Approve (ApproveInput) returns (google.protobuf.Empty) { + } + rpc UnApprove (UnApproveInput) returns (google.protobuf.Empty) { + } + rpc Lock (LockInput) returns (google.protobuf.Empty) { + } + rpc Unlock (UnlockInput) returns (google.protobuf.Empty) { + } + rpc Burn (BurnInput) returns (google.protobuf.Empty) { + } + rpc ChangeTokenIssuer (ChangeTokenIssuerInput) returns (google.protobuf.Empty) { + } + + // Cross Chain Exchange + rpc SetPrimaryTokenSymbol (SetPrimaryTokenSymbolInput) returns (google.protobuf.Empty) { + } + rpc CrossChainTransfer (CrossChainTransferInput) returns (google.protobuf.Empty) { + } + rpc CrossChainReceiveToken (CrossChainReceiveTokenInput) returns (google.protobuf.Empty) { + } + rpc CrossChainCreateToken(CrossChainCreateTokenInput) returns (google.protobuf.Empty) { + } + rpc InitializeFromParentChain (InitializeFromParentChainInput) returns (google.protobuf.Empty) { + } + + // ACS1 - Charge Transaction Fees + rpc ClaimTransactionFees (TotalTransactionFeesMap) returns (google.protobuf.Empty) { + } + rpc ChargeTransactionFees (ChargeTransactionFeesInput) returns (ChargeTransactionFeesOutput) { + } + + // ACS5 - Contract Method Call Threshold + rpc CheckThreshold (CheckThresholdInput) returns (google.protobuf.Empty) { + } + + // ACS8 - Charge Resource Tokens + rpc InitialCoefficients (google.protobuf.Empty) returns (google.protobuf.Empty){ + } + rpc DonateResourceToken (TotalResourceTokensMaps) returns (google.protobuf.Empty) { + } + rpc ChargeResourceToken (ChargeResourceTokenInput) returns (google.protobuf.Empty) { + } + rpc CheckResourceToken (google.protobuf.Empty) returns (google.protobuf.Empty) { + } + rpc SetSymbolsToPayTxSizeFee (SymbolListToPayTxSizeFee) returns (google.protobuf.Empty){ + } + rpc UpdateCoefficientsForSender (UpdateCoefficientsInput) returns (google.protobuf.Empty) { + } + rpc UpdateCoefficientsForContract (UpdateCoefficientsInput) returns (google.protobuf.Empty) { + } + rpc InitializeAuthorizedController(google.protobuf.Empty) returns (google.protobuf.Empty){ + } + + // Views + rpc GetTokenInfo (GetTokenInfoInput) returns (TokenInfo) { + option (aelf.is_view) = true; + } + rpc GetNativeTokenInfo (google.protobuf.Empty) returns (TokenInfo) { + option (aelf.is_view) = true; + } + rpc GetResourceTokenInfo (google.protobuf.Empty) returns (TokenInfoList) { + option (aelf.is_view) = true; + } + rpc GetBalance (GetBalanceInput) returns (GetBalanceOutput) { + option (aelf.is_view) = true; + } + rpc GetAllowance (GetAllowanceInput) returns (GetAllowanceOutput) { + option (aelf.is_view) = true; + } + rpc IsInWhiteList (IsInWhiteListInput) returns (google.protobuf.BoolValue) { + option (aelf.is_view) = true; + } + rpc GetLockedAmount (GetLockedAmountInput) returns (GetLockedAmountOutput) { + option (aelf.is_view) = true; + } + rpc GetCrossChainTransferTokenContractAddress (GetCrossChainTransferTokenContractAddressInput) returns (aelf.Address) { + option (aelf.is_view) = true; + } + rpc GetPrimaryTokenSymbol (google.protobuf.Empty) returns (google.protobuf.StringValue) { + option (aelf.is_view) = true; + } + rpc GetCalculateFeeCoefficientsForContract (google.protobuf.Int32Value) returns (CalculateFeeCoefficients) { + option (aelf.is_view) = true; + } + rpc GetCalculateFeeCoefficientsForSender (google.protobuf.Empty) returns (CalculateFeeCoefficients) { + option (aelf.is_view) = true; + } + // referenced by src + rpc GetSymbolsToPayTxSizeFee (google.protobuf.Empty) returns (SymbolListToPayTxSizeFee){ + option (aelf.is_view) = true; + } + rpc GetLatestTotalTransactionFeesMapHash (google.protobuf.Empty) returns (aelf.Hash){ + option (aelf.is_view) = true; + } + rpc GetLatestTotalResourceTokensMapsHash (google.protobuf.Empty) returns (aelf.Hash){ + option (aelf.is_view) = true; + } + rpc IsTokenAvailableForMethodFee (google.protobuf.StringValue) returns (google.protobuf.BoolValue) { + option (aelf.is_view) = true; + } +} + +message TokenInfo { + string symbol = 1; + string token_name = 2; + int64 supply = 3; + int64 total_supply = 4; + int32 decimals = 5; + aelf.Address issuer = 6; + bool is_burnable = 7; + int32 issue_chain_id = 9; + int64 issued = 10; +} + +message CreateInput { + string symbol = 1; + string token_name = 2; + int64 total_supply = 3; + int32 decimals = 4; + aelf.Address issuer = 5; + bool is_burnable = 6; + repeated aelf.Address lock_white_list = 7; + int32 issue_chain_id = 8; +} + +message RegisterNativeTokenInfoInput { + string symbol = 1; + string token_name = 2; + int64 total_supply = 3; + int32 decimals = 4; + aelf.Address issuer = 5; + bool is_burnable = 6; + int32 issue_chain_id = 7; +} + +message SetPrimaryTokenSymbolInput { + string symbol = 1; +} + +message IssueInput { + string symbol = 1; + int64 amount = 2; + string memo = 3; + aelf.Address to = 4; +} + +message TransferInput { + aelf.Address to = 1; + string symbol = 2; + int64 amount = 3; + string memo = 4; +} + +message LockInput { + aelf.Address address = 1; // The one want to lock his token. + aelf.Hash lock_id = 2; + string symbol = 3; + string usage = 4; + int64 amount = 5; +} + +message UnlockInput { + aelf.Address address = 1; // The one want to lock his token. + aelf.Hash lock_id = 2; + string symbol = 3; + string usage = 4; + int64 amount = 5; +} + +message TransferFromInput { + aelf.Address from = 1; + aelf.Address to = 2; + string symbol = 3; + int64 amount = 4; + string memo = 5; +} + +message ApproveInput { + aelf.Address spender = 1; + string symbol = 2; + int64 amount = 3; +} + +message UnApproveInput { + aelf.Address spender = 1; + string symbol = 2; + int64 amount = 3; +} + +message BurnInput { + string symbol = 1; + int64 amount = 2; +} + +message ChargeResourceTokenInput { + map cost_dic = 1; + aelf.Address caller = 2; +} + +message TransactionFeeBill { + map fees_map = 1; +} + +message CheckThresholdInput { + aelf.Address sender = 1; + map symbol_to_threshold = 2; + bool is_check_allowance = 3; +} + +message GetTokenInfoInput { + string symbol = 1; +} + +message GetBalanceInput { + string symbol = 1; + aelf.Address owner = 2; +} + +message GetBalanceOutput { + string symbol = 1; + aelf.Address owner = 2; + int64 balance = 3; +} + +message GetAllowanceInput { + string symbol = 1; + aelf.Address owner = 2; + aelf.Address spender = 3; +} + +message GetAllowanceOutput { + string symbol = 1; + aelf.Address owner = 2; + aelf.Address spender = 3; + int64 allowance = 4; +} + +message CrossChainTransferInput { + aelf.Address to = 1; + string symbol = 2; + int64 amount = 3; + string memo = 4; + int32 to_chain_id = 5; + int32 issue_chain_id = 6; +} + +message CrossChainReceiveTokenInput { + int32 from_chain_id = 1; + int64 parent_chain_height = 2; + bytes transfer_transaction_bytes = 3; + aelf.MerklePath merkle_path = 4; +} + +message IsInWhiteListInput { + string symbol = 1; + aelf.Address address = 2; +} + +message SymbolToPayTxSizeFee{ + string token_symbol = 1; + int32 base_token_weight = 2; + int32 added_token_weight = 3; +} + +message SymbolListToPayTxSizeFee{ + repeated SymbolToPayTxSizeFee symbols_to_pay_tx_size_fee = 1; +} + +message ChargeTransactionFeesInput { + string method_name = 1; + aelf.Address contract_address = 2; + int64 transaction_size_fee = 3; + repeated SymbolToPayTxSizeFee symbols_to_pay_tx_size_fee = 4; +} + +message ChargeTransactionFeesOutput { + bool success = 1; + string charging_information = 2; +} + +message ExtraTokenListModified { + option (aelf.is_event) = true; + SymbolListToPayTxSizeFee symbol_list_to_pay_tx_size_fee = 1; +} + +message ReturnTaxInput { + int64 balance_before_selling = 1; + aelf.Address return_tax_receiver_address = 2; +} + +message GetLockedAmountInput { + aelf.Address address = 1; + string symbol = 2; + aelf.Hash lock_id = 3; +} + +message GetLockedAmountOutput { + aelf.Address address = 1; + string symbol = 2; + aelf.Hash lock_id = 3; + int64 amount = 4; +} + +message TokenInfoList { + repeated TokenInfo value = 1; +} + +message GetCrossChainTransferTokenContractAddressInput { + int32 chainId = 1; +} + +message CrossChainCreateTokenInput { + int32 from_chain_id = 1; + int64 parent_chain_height = 2; + bytes transaction_bytes = 3; + aelf.MerklePath merkle_path = 4; +} + +message InitializeFromParentChainInput { + map resource_amount = 1; + map registered_other_token_contract_addresses = 2; + aelf.Address creator = 3; +} + +message UpdateCoefficientsInput { + repeated int32 piece_numbers = 1;// To specify pieces gonna update. + CalculateFeeCoefficients coefficients = 2; +} + +enum FeeTypeEnum { + READ = 0; + STORAGE = 1; + WRITE = 2; + TRAFFIC = 3; + TX = 4; +} + +// Coefficients of one single piece. +// The first char is its type: liner / power. +// The second char is its piece upper bound. +message CalculateFeePieceCoefficients { + repeated int32 value = 1; +} + +// Coefficients of one single type ot token, like READ, WRITE, etc. +message CalculateFeeCoefficients { + int32 fee_token_type = 1; + repeated CalculateFeePieceCoefficients piece_coefficients_list = 2; +} + +// To include coefficients of all tokens. +message AllCalculateFeeCoefficients { + repeated CalculateFeeCoefficients value = 1; +} + +message TotalTransactionFeesMap +{ + map value = 1; + aelf.Hash block_hash = 2; + int64 block_height = 3; +} + +message TotalResourceTokensMaps { + repeated ContractTotalResourceTokens value = 1; + aelf.Hash block_hash = 2; + int64 block_height = 3; +} + +message ContractTotalResourceTokens { + aelf.Address contract_address = 1; + TotalResourceTokensMap tokens_map = 2; +} + +message TotalResourceTokensMap +{ + map value = 1; +} + +message ChangeTokenIssuerInput +{ + string symbol = 1; + aelf.Address new_token_Issuer = 2; +} + +// Events + +message Transferred { + option (aelf.is_event) = true; + aelf.Address from = 1 [(aelf.is_indexed) = true]; + aelf.Address to = 2 [(aelf.is_indexed) = true]; + string symbol = 3 [(aelf.is_indexed) = true]; + int64 amount = 4; + string memo = 5; +} + +message Approved { + option (aelf.is_event) = true; + aelf.Address owner = 1 [(aelf.is_indexed) = true]; + aelf.Address spender = 2 [(aelf.is_indexed) = true]; + string symbol = 3 [(aelf.is_indexed) = true]; + int64 amount = 4; +} + +message UnApproved { + option (aelf.is_event) = true; + aelf.Address owner = 1 [(aelf.is_indexed) = true]; + aelf.Address spender = 2 [(aelf.is_indexed) = true]; + string symbol = 3 [(aelf.is_indexed) = true]; + int64 amount = 4; +} + +message Burned +{ + option (aelf.is_event) = true; + aelf.Address burner = 1 [(aelf.is_indexed) = true]; + string symbol = 2 [(aelf.is_indexed) = true]; + int64 amount = 3; +} + +message ChainPrimaryTokenSymbolSet { + option (aelf.is_event) = true; + string token_symbol = 1; +} + +message TransactionSizeFeeUnitPriceUpdated { + option (aelf.is_event) = true; + int64 unit_price = 1; +} + +// The modification of each fee type will fire one event. +message CalculateFeeAlgorithmUpdated { + option (aelf.is_event) = true; + AllCalculateFeeCoefficients all_type_fee_coefficients = 1; +} + +message RentalCharged { + option (aelf.is_event) = true; + string symbol = 1; + int64 amount = 2; +} + +message RentalAccountBalanceInsufficient { + option (aelf.is_event) = true; + string symbol = 1; + int64 amount = 2; +} + +message TokenCreated { + option (aelf.is_event) = true; + string symbol = 1; + string token_name = 2; + int64 total_supply = 3; + int32 decimals = 4; + aelf.Address issuer = 5; + bool is_burnable = 6; + int32 issue_chain_id = 7; +} + +message Issued { + option (aelf.is_event) = true; + string symbol = 1; + int64 amount = 2; + string memo = 3; + aelf.Address to = 4; +} + +message CrossChainTransferred { + option (aelf.is_event) = true; + aelf.Address from = 1; + aelf.Address to = 2; + string symbol = 3; + int64 amount = 4; + string memo = 5; + int32 to_chain_id = 6; + int32 issue_chain_id = 7; +} + +message CrossChainReceived { + option (aelf.is_event) = true; + aelf.Address from = 1; + aelf.Address to = 2; + string symbol = 3; + int64 amount = 4; + string memo = 5; + int32 from_chain_id = 6; + int32 issue_chain_id = 7; + int64 parent_chain_height = 8; +} \ No newline at end of file diff --git a/timelock-contract/test/TimelockContractTests.cs b/timelock-contract/test/TimelockContractTests.cs new file mode 100644 index 0000000..768df37 --- /dev/null +++ b/timelock-contract/test/TimelockContractTests.cs @@ -0,0 +1,82 @@ +using System.Linq; +using System.Threading.Tasks; +using AElf.Contracts.MultiToken; +using AElf.Types; +using Google.Protobuf.WellKnownTypes; +using Shouldly; +using Xunit; + +namespace AElf.Contracts.Timelock +{ + public partial class TimelockContractTests : TestBase + { + [Fact] + public async Task PlayTests() + { + // Prepare awards. + await TokenContractStub.Transfer.SendAsync(new TransferInput + { + To = DAppContractAddress, + Symbol = "ELF", + Amount = 100_00000000 + }); + + await TokenContractStub.Create.SendAsync(new CreateInput + { + Symbol = "CARD", + TokenName = "Bingo Card", + Decimals = 0, + Issuer = DAppContractAddress, + IsBurnable = true, + TotalSupply = long.MaxValue + }); + + await TimelockContractStub.Register.SendAsync(new Empty()); + + await TokenContractStub.Approve.SendAsync(new ApproveInput + { + Spender = DAppContractAddress, + Symbol = "CARD", + Amount = long.MaxValue + }); + + // Now I have player information. + var address = Address.FromPublicKey(DefaultKeyPair.PublicKey); + { + var playerInformation = await TimelockContractStub.GetPlayerInformation.CallAsync(address); + playerInformation.Seed.Value.ShouldNotBeEmpty(); + playerInformation.RegisterTime.ShouldNotBeNull(); + } + + // Play. + var txResult = (await TokenContractStub.Approve.SendAsync(new ApproveInput + { + Spender = DAppContractAddress, + Symbol = "ELF", + Amount = 10000 + })).TransactionResult; + txResult.Status.ShouldBe(TransactionResultStatus.Mined); + + await TimelockContractStub.Play.SendAsync(new Int64Value {Value = 10000}); + + Hash playId; + { + var playerInformation = await TimelockContractStub.GetPlayerInformation.CallAsync(address); + playerInformation.Bouts.ShouldNotBeEmpty(); + playId = playerInformation.Bouts.First().PlayId; + } + + // Mine 7 more blocks. + for (var i = 0; i < 7; i++) + { + await TimelockContractStub.Bingo.SendWithExceptionAsync(playId); + } + + await TimelockContractStub.Bingo.SendAsync(playId); + + var award = await TimelockContractStub.GetAward.CallAsync(playId); + award.Value.ShouldNotBe(0); + } + } + +} \ No newline at end of file diff --git a/timelock-contract/test/TimelockContractTestsInitialization.cs b/timelock-contract/test/TimelockContractTestsInitialization.cs new file mode 100644 index 0000000..88c9dad --- /dev/null +++ b/timelock-contract/test/TimelockContractTestsInitialization.cs @@ -0,0 +1,27 @@ +using AElf.Contracts.MultiToken; +using AElf.Cryptography.ECDSA; +using AElf.Kernel.Token; +using AElf.Types; + +namespace AElf.Contracts.Timelock +{ + public partial class TimelockContractTests + { + // private readonly ECKeyPair KeyPair; + private readonly TimelockContractContainer.TimelockContractStub TimelockContractStub; + private readonly TokenContractContainer.TokenContractStub TokenContractStub; + protected ECKeyPair DefaultKeyPair => Accounts[0].KeyPair; + protected Address DefaultAddress => Accounts[0].Address; + protected ECKeyPair UserKeyPair => Accounts[1].KeyPair; + protected Address UserAddress => Accounts[1].Address; + + public TimelockContractTests() + { + // KeyPair = SampleAccount.Accounts.First().KeyPair; + TimelockContractStub = GetContractStub(DefaultKeyPair); + TokenContractStub = GetTester( + GetAddress(TokenSmartContractAddressNameProvider.StringName), DefaultKeyPair); + } + } + +} \ No newline at end of file diff --git a/timelock-contract/test/_Setup.cs b/timelock-contract/test/_Setup.cs new file mode 100644 index 0000000..003fc28 --- /dev/null +++ b/timelock-contract/test/_Setup.cs @@ -0,0 +1,22 @@ +using AElf.Boilerplate.TestBase.Contract; +using AElf.Boilerplate.TestBase.DAppContract; +using AElf.Cryptography.ECDSA; +using AElf.CSharp.Core; + +namespace AElf.Contracts.Timelock +{ + public class Module : ContractTestModule + { + + } + public class TestBase : DAppContractTestBase + { + // You can get address of any contract via GetAddress method, for example: + // internal Address DAppContractAddress => GetAddress(DAppSmartContractAddressNameProvider.StringName); + + protected TStub GetContractStub(ECKeyPair senderKeyPair) where TStub:ContractStubBase, new() + { + return GetTester(DAppContractAddress, senderKeyPair); + } + } +} \ No newline at end of file From 72f0c667152b466b7a37a680ccc9d805fd3e048a Mon Sep 17 00:00:00 2001 From: aelf-lu Date: Mon, 12 Jun 2023 17:59:25 +0800 Subject: [PATCH 02/20] add unit test --- .gitignore | 226 ++++++++++++++++++ .../AElf.Contracts.TimelockContract.csproj | 4 + .../Protobuf/contract/timelock_contract.proto | 35 ++- timelock-contract/src/TimelockContract.cs | 25 +- .../src/TimelockContractConstants.cs | 9 +- .../src/TimelockContractHelper.cs | 36 +++ .../src/TimelockContractState.cs | 1 + ...lf.Contracts.TimelockContract.Tests.csproj | 7 +- .../Protobuf/stub/timelock_contract.proto | 35 ++- .../test/TimelockContractTests.cs | 93 +++---- .../TimelockContractTestsInitialization.cs | 4 +- 11 files changed, 383 insertions(+), 92 deletions(-) create mode 100644 .gitignore create mode 100644 timelock-contract/src/TimelockContractHelper.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..71ec0d1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,226 @@ +# The following command works for downloading when using Git for Windows: +# curl -LOf http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore +# +# Download this file using PowerShell v3 under Windows with the following comand: +# Invoke-WebRequest https://gist.githubusercontent.com/kmorcinek/2710267/raw/ -OutFile .gitignore +# +# or wget: +# wget --no-check-certificate http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +x64/ +[Bb]in/ +[Oo]bj/ +# build folder is nowadays used for build scripts and should not be ignored +#build/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings +modulesbin/ +tempbin/ + +# EPiServer Site file (VPP) +AppData/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# vim +*.txt~ +*.swp +*.swo + +# Temp files when opening LibreOffice on ubuntu +.~lock.* + +# svn +.svn + +# CVS - Source Control +**/CVS/ + +# Remainings from resolving conflicts in Source Control +*.orig + +# SQL Server files +**/App_Data/*.mdf +**/App_Data/*.ldf +**/App_Data/*.sdf + + +#LightSwitch generated files +GeneratedArtifacts/ +_Pvt_Extensions/ +ModelManifest.xml + +# ========================= +# Windows detritus +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# OS generated files # +Icon? + +# Mac desktop service store files +.DS_Store + +# SASS Compiler cache +.sass-cache + +# Visual Studio 2014 CTP +**/*.sln.ide + +# Visual Studio temp something +.vs/ + +# dotnet stuff +project.lock.json + +# VS 2015+ +*.vc.vc.opendb +*.vc.db + +# Rider +.idea/ + +# Visual Studio Code +.vscode/ + +# Output folder used by Webpack or other FE stuff +**/node_modules/* +**/wwwroot/* + +# SpecFlow specific +*.feature.cs +*.feature.xlsx.* +*.Specs_*.html + +# UWP Projects +AppPackages/ + +##### +# End of core ignore list, below put you custom 'per project' settings (patterns or path) +##### \ No newline at end of file diff --git a/timelock-contract/src/AElf.Contracts.TimelockContract.csproj b/timelock-contract/src/AElf.Contracts.TimelockContract.csproj index b815e46..3769f32 100644 --- a/timelock-contract/src/AElf.Contracts.TimelockContract.csproj +++ b/timelock-contract/src/AElf.Contracts.TimelockContract.csproj @@ -18,6 +18,10 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + + + + \ No newline at end of file diff --git a/timelock-contract/src/Protobuf/contract/timelock_contract.proto b/timelock-contract/src/Protobuf/contract/timelock_contract.proto index 1c89147..cd13870 100644 --- a/timelock-contract/src/Protobuf/contract/timelock_contract.proto +++ b/timelock-contract/src/Protobuf/contract/timelock_contract.proto @@ -12,13 +12,34 @@ service TimelockContract { option (aelf.csharp_state) = "AElf.Contracts.Timelock.TimelockContractState"; // Actions - rpc SetDelay (DelayInput) returns (google.protobuf.Empty) {} - rpc AcceptAdmin (google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc SetPendingAdmin (SetPendingAdminInput) returns (google.protobuf.Empty) {} - rpc QueueTransaction (TransactionInput) returns (google.protobuf.Empty) {} - rpc ExecuteTransaction (TransactionInput) returns (google.protobuf.Empty) {} - rpc CancelTransaction (TransactionInput) returns (google.protobuf.Empty) {} - + rpc Initialize (google.protobuf.Empty) returns (google.protobuf.Empty) { + } + rpc SetDelay (DelayInput) returns (google.protobuf.Empty) { + } + rpc AcceptAdmin (google.protobuf.Empty) returns (google.protobuf.Empty) { + } + rpc SetPendingAdmin (SetPendingAdminInput) returns (google.protobuf.Empty) { + } + rpc QueueTransaction (TransactionInput) returns (google.protobuf.Empty) { + } + rpc ExecuteTransaction (TransactionInput) returns (google.protobuf.Empty) { + } + rpc CancelTransaction (TransactionInput) returns (google.protobuf.Empty) { + } + + // View + rpc GetDelay (google.protobuf.Empty) returns (google.protobuf.Int64Value) { + option (aelf.is_view) = true; + } + rpc GetPendingAdmin (google.protobuf.Empty) returns (aelf.Address) { + option (aelf.is_view) = true; + } + rpc GetAdmin (google.protobuf.Empty) returns (aelf.Address) { + option (aelf.is_view) = true; + } + rpc GetTransaction(aelf.Hash) returns (aelf.Address) { + option (aelf.is_view) = true; + } } // Params diff --git a/timelock-contract/src/TimelockContract.cs b/timelock-contract/src/TimelockContract.cs index 99c1c04..7f899de 100644 --- a/timelock-contract/src/TimelockContract.cs +++ b/timelock-contract/src/TimelockContract.cs @@ -8,6 +8,19 @@ namespace AElf.Contracts.Timelock { public partial class TimelockContract : TimelockContractContainer.TimelockContractBase { + public override Empty Initialize(Empty input) + { + if (State.Initialized.Value) + { + return new Empty(); + } + State.TokenContract.Value = + Context.GetContractAddressByName(SmartContractConstants.TokenContractSystemName); + State.Admin.Value = Context.Sender; + State.Initialized.Value = true; + return new Empty(); + } + public override Empty SetDelay(DelayInput input) { Assert(State.Admin.Value == Context.Sender, "No permission"); @@ -23,21 +36,25 @@ public override Empty SetDelay(DelayInput input) return new Empty(); } + // todo [confirm] admin to accept or pendingAdmin accept? public override Empty AcceptAdmin(Empty input) { - Assert(Context.Sender == State.PendingAdmin.Value, "No permission"); - State.Admin.Value = Context.Sender; + // Assert(Context.Sender == State.PendingAdmin.Value, "No permission"); + Assert(State.Admin.Value == Context.Sender, "No permission"); + Assert(State.PendingAdmin.Value != null, "PendingAdmin must not be null"); + State.Admin.Value = State.PendingAdmin.Value; State.PendingAdmin.Value = null; Context.Fire(new NewAdmin { - NewAdmin_ = Context.Sender + NewAdmin_ = State.PendingAdmin.Value }); return new Empty(); } public override Empty SetPendingAdmin(SetPendingAdminInput input) { - Assert(Context.Sender == Context.Self, "No permission"); + // Assert(Context.Sender == Context.Self, "No permission"); + Assert(State.Admin.Value == Context.Sender, "No permission"); State.PendingAdmin.Value = input.PendingAdmin; Context.Fire(new NewPendingAdmin { diff --git a/timelock-contract/src/TimelockContractConstants.cs b/timelock-contract/src/TimelockContractConstants.cs index 81eea70..645c11a 100644 --- a/timelock-contract/src/TimelockContractConstants.cs +++ b/timelock-contract/src/TimelockContractConstants.cs @@ -3,11 +3,8 @@ namespace AElf.Contracts.Timelock public static class TimelockContractConstants { public const string SYMBOL = "ELF"; - // 1 min - public const long MIN_DELAY = 60; - // 1 hour - public const long MAX_DELAY = 3600; - // 30 min - public const long GRACE_PERIOD = 1800; + public const long MIN_DELAY = 1L; + public const long MAX_DELAY = 3L; + public const long GRACE_PERIOD = 2L; } } \ No newline at end of file diff --git a/timelock-contract/src/TimelockContractHelper.cs b/timelock-contract/src/TimelockContractHelper.cs new file mode 100644 index 0000000..9478f57 --- /dev/null +++ b/timelock-contract/src/TimelockContractHelper.cs @@ -0,0 +1,36 @@ +using AElf.Contracts.MultiToken; +using AElf.CSharp.Core.Extension; +using AElf.Sdk.CSharp; +using AElf.Types; +using Google.Protobuf.WellKnownTypes; + +namespace AElf.Contracts.Timelock +{ + public partial class TimelockContract + { + public override Int64Value GetDelay(Empty input) + { + var delay = new Int64Value + { + Value = State.Delay.Value + }; + return delay; + } + + public override Address GetPendingAdmin(Empty input) + { + return State.PendingAdmin.Value; + } + + public override Address GetAdmin(Empty input) + { + return State.Admin.Value; + } + + public override Address GetTransaction(Hash input) + { + return State.TransactionQueue[input]; + } + } + +} \ No newline at end of file diff --git a/timelock-contract/src/TimelockContractState.cs b/timelock-contract/src/TimelockContractState.cs index e3a167f..92b335e 100644 --- a/timelock-contract/src/TimelockContractState.cs +++ b/timelock-contract/src/TimelockContractState.cs @@ -5,6 +5,7 @@ namespace AElf.Contracts.Timelock { public partial class TimelockContractState : ContractState { + public BoolState Initialized { get; set; } public SingletonState
Admin { get; set; } public SingletonState
PendingAdmin { get; set; } public SingletonState Delay { get; set; } diff --git a/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj b/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj index f231c53..abeea1f 100644 --- a/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj +++ b/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj @@ -3,6 +3,7 @@ net6.0 AElf.Contracts.Timelock + http://10.0.1.239:8081/repository/nuget-group/index.json @@ -10,7 +11,7 @@ - + @@ -20,7 +21,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -39,7 +40,7 @@ - + \ No newline at end of file diff --git a/timelock-contract/test/Protobuf/stub/timelock_contract.proto b/timelock-contract/test/Protobuf/stub/timelock_contract.proto index 1c89147..cd13870 100644 --- a/timelock-contract/test/Protobuf/stub/timelock_contract.proto +++ b/timelock-contract/test/Protobuf/stub/timelock_contract.proto @@ -12,13 +12,34 @@ service TimelockContract { option (aelf.csharp_state) = "AElf.Contracts.Timelock.TimelockContractState"; // Actions - rpc SetDelay (DelayInput) returns (google.protobuf.Empty) {} - rpc AcceptAdmin (google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc SetPendingAdmin (SetPendingAdminInput) returns (google.protobuf.Empty) {} - rpc QueueTransaction (TransactionInput) returns (google.protobuf.Empty) {} - rpc ExecuteTransaction (TransactionInput) returns (google.protobuf.Empty) {} - rpc CancelTransaction (TransactionInput) returns (google.protobuf.Empty) {} - + rpc Initialize (google.protobuf.Empty) returns (google.protobuf.Empty) { + } + rpc SetDelay (DelayInput) returns (google.protobuf.Empty) { + } + rpc AcceptAdmin (google.protobuf.Empty) returns (google.protobuf.Empty) { + } + rpc SetPendingAdmin (SetPendingAdminInput) returns (google.protobuf.Empty) { + } + rpc QueueTransaction (TransactionInput) returns (google.protobuf.Empty) { + } + rpc ExecuteTransaction (TransactionInput) returns (google.protobuf.Empty) { + } + rpc CancelTransaction (TransactionInput) returns (google.protobuf.Empty) { + } + + // View + rpc GetDelay (google.protobuf.Empty) returns (google.protobuf.Int64Value) { + option (aelf.is_view) = true; + } + rpc GetPendingAdmin (google.protobuf.Empty) returns (aelf.Address) { + option (aelf.is_view) = true; + } + rpc GetAdmin (google.protobuf.Empty) returns (aelf.Address) { + option (aelf.is_view) = true; + } + rpc GetTransaction(aelf.Hash) returns (aelf.Address) { + option (aelf.is_view) = true; + } } // Params diff --git a/timelock-contract/test/TimelockContractTests.cs b/timelock-contract/test/TimelockContractTests.cs index 768df37..98bb37a 100644 --- a/timelock-contract/test/TimelockContractTests.cs +++ b/timelock-contract/test/TimelockContractTests.cs @@ -1,7 +1,10 @@ +using System; using System.Linq; using System.Threading.Tasks; +using System.Timers; using AElf.Contracts.MultiToken; using AElf.Types; +using Google.Protobuf; using Google.Protobuf.WellKnownTypes; using Shouldly; using Xunit; @@ -11,72 +14,36 @@ namespace AElf.Contracts.Timelock public partial class TimelockContractTests : TestBase { [Fact] - public async Task PlayTests() + public async Task InitializeTests() { - // Prepare awards. - await TokenContractStub.Transfer.SendAsync(new TransferInput - { - To = DAppContractAddress, - Symbol = "ELF", - Amount = 100_00000000 - }); - - await TokenContractStub.Create.SendAsync(new CreateInput - { - Symbol = "CARD", - TokenName = "Bingo Card", - Decimals = 0, - Issuer = DAppContractAddress, - IsBurnable = true, - TotalSupply = long.MaxValue - }); - - await TimelockContractStub.Register.SendAsync(new Empty()); - - await TokenContractStub.Approve.SendAsync(new ApproveInput - { - Spender = DAppContractAddress, - Symbol = "CARD", - Amount = long.MaxValue - }); - - // Now I have player information. - var address = Address.FromPublicKey(DefaultKeyPair.PublicKey); - { - var playerInformation = await TimelockContractStub.GetPlayerInformation.CallAsync(address); - playerInformation.Seed.Value.ShouldNotBeEmpty(); - playerInformation.RegisterTime.ShouldNotBeNull(); - } - - // Play. - var txResult = (await TokenContractStub.Approve.SendAsync(new ApproveInput - { - Spender = DAppContractAddress, - Symbol = "ELF", - Amount = 10000 - })).TransactionResult; - txResult.Status.ShouldBe(TransactionResultStatus.Mined); - - await TimelockContractStub.Play.SendAsync(new Int64Value {Value = 10000}); - - Hash playId; - { - var playerInformation = await TimelockContractStub.GetPlayerInformation.CallAsync(address); - playerInformation.Bouts.ShouldNotBeEmpty(); - playId = playerInformation.Bouts.First().PlayId; - } - - // Mine 7 more blocks. - for (var i = 0; i < 7; i++) - { - await TimelockContractStub.Bingo.SendWithExceptionAsync(playId); - } - - await TimelockContractStub.Bingo.SendAsync(playId); + await TimelockContractStub.Initialize.SendAsync(new Empty()); + } + + [Fact] + public async Task SetDelayTests() + { + await InitializeTests(); + DelayInput input = new DelayInput(); + input.Delay = 3L; + await TimelockContractStub.SetDelay.SendAsync(input); + var result = await TimelockContractStub.GetDelay.CallAsync(new Empty()); + result.Value.ShouldBe(3L); + } - var award = await TimelockContractStub.GetAward.CallAsync(playId); - award.Value.ShouldNotBe(0); + [Fact] + public async Task AcceptAdminTests() + { + await InitializeTests(); + SetPendingAdminInput input = new SetPendingAdminInput(); + input.PendingAdmin = UserAddress; + await TimelockContractStub.SetPendingAdmin.SendAsync(input); + await Task.Delay(2000); + await TimelockContractStub.AcceptAdmin.SendAsync(new Empty()); + await Task.Delay(2000); + var result = await TimelockContractStub.GetAdmin.CallAsync(new Empty()); + result.ShouldBe(UserAddress); } + } } \ No newline at end of file diff --git a/timelock-contract/test/TimelockContractTestsInitialization.cs b/timelock-contract/test/TimelockContractTestsInitialization.cs index 88c9dad..4917579 100644 --- a/timelock-contract/test/TimelockContractTestsInitialization.cs +++ b/timelock-contract/test/TimelockContractTestsInitialization.cs @@ -8,8 +8,8 @@ namespace AElf.Contracts.Timelock public partial class TimelockContractTests { // private readonly ECKeyPair KeyPair; - private readonly TimelockContractContainer.TimelockContractStub TimelockContractStub; - private readonly TokenContractContainer.TokenContractStub TokenContractStub; + internal TimelockContractContainer.TimelockContractStub TimelockContractStub; + internal TokenContractContainer.TokenContractStub TokenContractStub; protected ECKeyPair DefaultKeyPair => Accounts[0].KeyPair; protected Address DefaultAddress => Accounts[0].Address; protected ECKeyPair UserKeyPair => Accounts[1].KeyPair; From f69ab9ef3f2481a43816a5dceaf26557fa836fd8 Mon Sep 17 00:00:00 2001 From: aelf-lu Date: Tue, 13 Jun 2023 11:33:39 +0800 Subject: [PATCH 03/20] add unit test --- timelock-contract/src/TimelockContract.cs | 8 +- ...lf.Contracts.TimelockContract.Tests.csproj | 37 +++++++++ .../test/TimelockContractTests.cs | 81 ++++++++++++++++++- .../TimelockContractTestsInitialization.cs | 6 +- 4 files changed, 123 insertions(+), 9 deletions(-) diff --git a/timelock-contract/src/TimelockContract.cs b/timelock-contract/src/TimelockContract.cs index 7f899de..810ab42 100644 --- a/timelock-contract/src/TimelockContract.cs +++ b/timelock-contract/src/TimelockContract.cs @@ -36,17 +36,15 @@ public override Empty SetDelay(DelayInput input) return new Empty(); } - // todo [confirm] admin to accept or pendingAdmin accept? - public override Empty AcceptAdmin(Empty input) + public override Empty AcceptAdmin(SetPendingAdminInput input) { // Assert(Context.Sender == State.PendingAdmin.Value, "No permission"); Assert(State.Admin.Value == Context.Sender, "No permission"); Assert(State.PendingAdmin.Value != null, "PendingAdmin must not be null"); - State.Admin.Value = State.PendingAdmin.Value; - State.PendingAdmin.Value = null; + State.Admin.Value = input.PendingAdmin; Context.Fire(new NewAdmin { - NewAdmin_ = State.PendingAdmin.Value + NewAdmin_ = State.Admin.Value }); return new Empty(); } diff --git a/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj b/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj index abeea1f..a28757f 100644 --- a/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj +++ b/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj @@ -43,4 +43,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/timelock-contract/test/TimelockContractTests.cs b/timelock-contract/test/TimelockContractTests.cs index 98bb37a..d612363 100644 --- a/timelock-contract/test/TimelockContractTests.cs +++ b/timelock-contract/test/TimelockContractTests.cs @@ -37,13 +37,88 @@ public async Task AcceptAdminTests() SetPendingAdminInput input = new SetPendingAdminInput(); input.PendingAdmin = UserAddress; await TimelockContractStub.SetPendingAdmin.SendAsync(input); - await Task.Delay(2000); - await TimelockContractStub.AcceptAdmin.SendAsync(new Empty()); - await Task.Delay(2000); + await TimelockContractStub.AcceptAdmin.SendAsync(input); var result = await TimelockContractStub.GetAdmin.CallAsync(new Empty()); result.ShouldBe(UserAddress); } + + [Fact] + public async Task QueueTransactionTests() + { + await InitializeTests(); + TransactionInput input = new TransactionInput + { + Target = UserAddress, + Amount = 3L, + Data = "", + Eta = new Timestamp(), + Signature = "sign" + }; + Hash txnHash = HashHelper.ComputeFrom(input); + await TimelockContractStub.QueueTransaction.SendAsync(input); + var result = await TimelockContractStub.GetTransaction.CallAsync(txnHash); + result.ShouldNotBeNull(); + } + [Fact] + public async Task CancelTransactionTests() + { + await InitializeTests(); + TransactionInput input = new TransactionInput + { + Target = UserAddress, + Amount = 3L, + Data = "", + Eta = new Timestamp(), + Signature = "sign" + }; + Hash txnHash = HashHelper.ComputeFrom(input); + await TimelockContractStub.QueueTransaction.SendAsync(input); + await TimelockContractStub.CancelTransaction.SendAsync(input); + var result = await TimelockContractStub.GetTransaction.CallAsync(txnHash); + result.ShouldBe(new Address()); + } + + private async Task InitializeAsync() + { + await TokenContractStub.Transfer.SendAsync(new TransferInput + { + To = DAppContractAddress, + Symbol = "ELF", + Amount = 1000_00000000 + }); + await TokenContractStub.Approve.SendAsync(new ApproveInput + { + Spender = DAppContractAddress, + Symbol = "ELF", + Amount = 1000_00000000 + }); + } + + [Fact] + public async Task ExecuteTransactionTests() + { + await InitializeTests(); + await InitializeAsync(); + TransactionInput input = new TransactionInput + { + Target = UserAddress, + Amount = 5L, + Data = "", + Eta = new Timestamp(), + Signature = "sign" + }; + await TimelockContractStub.QueueTransaction.SendAsync(input); + await TimelockContractStub.ExecuteTransaction.SendAsync(input); + + var balance2 = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = UserAddress, + Symbol = "ELF" + }); + balance2.Balance.ShouldBe(5L); + } + } } \ No newline at end of file diff --git a/timelock-contract/test/TimelockContractTestsInitialization.cs b/timelock-contract/test/TimelockContractTestsInitialization.cs index 4917579..d73c8b3 100644 --- a/timelock-contract/test/TimelockContractTestsInitialization.cs +++ b/timelock-contract/test/TimelockContractTestsInitialization.cs @@ -2,11 +2,14 @@ using AElf.Cryptography.ECDSA; using AElf.Kernel.Token; using AElf.Types; +using Xunit.Abstractions; namespace AElf.Contracts.Timelock { public partial class TimelockContractTests { + private readonly ITestOutputHelper _testOutputHelper; + // private readonly ECKeyPair KeyPair; internal TimelockContractContainer.TimelockContractStub TimelockContractStub; internal TokenContractContainer.TokenContractStub TokenContractStub; @@ -15,8 +18,9 @@ public partial class TimelockContractTests protected ECKeyPair UserKeyPair => Accounts[1].KeyPair; protected Address UserAddress => Accounts[1].Address; - public TimelockContractTests() + public TimelockContractTests(ITestOutputHelper testOutputHelper) { + _testOutputHelper = testOutputHelper; // KeyPair = SampleAccount.Accounts.First().KeyPair; TimelockContractStub = GetContractStub(DefaultKeyPair); TokenContractStub = GetTester( From cb128d21c7366e8e8f05f5510be33422e00c2686 Mon Sep 17 00:00:00 2001 From: aelf-lu Date: Tue, 13 Jun 2023 15:02:11 +0800 Subject: [PATCH 04/20] fix --- timelock-contract/src/TimelockContract.cs | 4 ++-- timelock-contract/src/TimelockContractConstants.cs | 2 +- timelock-contract/test/TimelockContractTests.cs | 14 +++++--------- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/timelock-contract/src/TimelockContract.cs b/timelock-contract/src/TimelockContract.cs index 810ab42..22423bb 100644 --- a/timelock-contract/src/TimelockContract.cs +++ b/timelock-contract/src/TimelockContract.cs @@ -36,12 +36,12 @@ public override Empty SetDelay(DelayInput input) return new Empty(); } - public override Empty AcceptAdmin(SetPendingAdminInput input) + public override Empty AcceptAdmin(Empty input) { // Assert(Context.Sender == State.PendingAdmin.Value, "No permission"); Assert(State.Admin.Value == Context.Sender, "No permission"); Assert(State.PendingAdmin.Value != null, "PendingAdmin must not be null"); - State.Admin.Value = input.PendingAdmin; + State.Admin.Value = State.PendingAdmin.Value; Context.Fire(new NewAdmin { NewAdmin_ = State.Admin.Value diff --git a/timelock-contract/src/TimelockContractConstants.cs b/timelock-contract/src/TimelockContractConstants.cs index 645c11a..e1107fe 100644 --- a/timelock-contract/src/TimelockContractConstants.cs +++ b/timelock-contract/src/TimelockContractConstants.cs @@ -5,6 +5,6 @@ public static class TimelockContractConstants public const string SYMBOL = "ELF"; public const long MIN_DELAY = 1L; public const long MAX_DELAY = 3L; - public const long GRACE_PERIOD = 2L; + public const long GRACE_PERIOD = 1L; } } \ No newline at end of file diff --git a/timelock-contract/test/TimelockContractTests.cs b/timelock-contract/test/TimelockContractTests.cs index d612363..9d52c55 100644 --- a/timelock-contract/test/TimelockContractTests.cs +++ b/timelock-contract/test/TimelockContractTests.cs @@ -1,12 +1,8 @@ -using System; -using System.Linq; using System.Threading.Tasks; -using System.Timers; using AElf.Contracts.MultiToken; +using AElf.Kernel; using AElf.Types; -using Google.Protobuf; using Google.Protobuf.WellKnownTypes; -using Shouldly; using Xunit; namespace AElf.Contracts.Timelock @@ -37,7 +33,7 @@ public async Task AcceptAdminTests() SetPendingAdminInput input = new SetPendingAdminInput(); input.PendingAdmin = UserAddress; await TimelockContractStub.SetPendingAdmin.SendAsync(input); - await TimelockContractStub.AcceptAdmin.SendAsync(input); + await TimelockContractStub.AcceptAdmin.SendAsync(new Empty()); var result = await TimelockContractStub.GetAdmin.CallAsync(new Empty()); result.ShouldBe(UserAddress); } @@ -51,7 +47,7 @@ public async Task QueueTransactionTests() Target = UserAddress, Amount = 3L, Data = "", - Eta = new Timestamp(), + Eta = TimestampHelper.GetUtcNow(), Signature = "sign" }; Hash txnHash = HashHelper.ComputeFrom(input); @@ -69,7 +65,7 @@ public async Task CancelTransactionTests() Target = UserAddress, Amount = 3L, Data = "", - Eta = new Timestamp(), + Eta = TimestampHelper.GetUtcNow(), Signature = "sign" }; Hash txnHash = HashHelper.ComputeFrom(input); @@ -105,7 +101,7 @@ public async Task ExecuteTransactionTests() Target = UserAddress, Amount = 5L, Data = "", - Eta = new Timestamp(), + Eta = TimestampHelper.GetUtcNow(), Signature = "sign" }; await TimelockContractStub.QueueTransaction.SendAsync(input); From 1de8632bb5c1510ee326223c7503af3d564ce15f Mon Sep 17 00:00:00 2001 From: aelf-lu Date: Thu, 15 Jun 2023 14:51:03 +0800 Subject: [PATCH 05/20] move code --- .../src/AElf.Contracts.TimelockContract.csproj | 0 .../timelock-contract}/src/ContractsReferences.cs | 0 .../timelock-contract}/src/Protobuf/base/acs1.proto | 0 .../src/Protobuf/contract/timelock_contract.proto | 0 .../timelock-contract}/src/Protobuf/message/authority_info.proto | 0 .../src/Protobuf/reference/token_contract.proto | 0 .../timelock-contract}/src/TimelockContract.cs | 0 .../timelock-contract}/src/TimelockContractConstants.cs | 0 .../timelock-contract}/src/TimelockContractHelper.cs | 0 .../timelock-contract}/src/TimelockContractState.cs | 0 .../test/AElf.Contracts.TimelockContract.Tests.csproj | 0 .../timelock-contract}/test/Protobuf/base/acs1.proto | 0 .../test/Protobuf/message/authority_info.proto | 0 .../test/Protobuf/stub/timelock_contract.proto | 0 .../timelock-contract}/test/Protobuf/stub/token_contract.proto | 0 .../timelock-contract}/test/TimelockContractTests.cs | 1 + .../test/TimelockContractTestsInitialization.cs | 0 {timelock-contract => basics/timelock-contract}/test/_Setup.cs | 0 18 files changed, 1 insertion(+) rename {timelock-contract => basics/timelock-contract}/src/AElf.Contracts.TimelockContract.csproj (100%) rename {timelock-contract => basics/timelock-contract}/src/ContractsReferences.cs (100%) rename {timelock-contract => basics/timelock-contract}/src/Protobuf/base/acs1.proto (100%) rename {timelock-contract => basics/timelock-contract}/src/Protobuf/contract/timelock_contract.proto (100%) rename {timelock-contract => basics/timelock-contract}/src/Protobuf/message/authority_info.proto (100%) rename {timelock-contract => basics/timelock-contract}/src/Protobuf/reference/token_contract.proto (100%) rename {timelock-contract => basics/timelock-contract}/src/TimelockContract.cs (100%) rename {timelock-contract => basics/timelock-contract}/src/TimelockContractConstants.cs (100%) rename {timelock-contract => basics/timelock-contract}/src/TimelockContractHelper.cs (100%) rename {timelock-contract => basics/timelock-contract}/src/TimelockContractState.cs (100%) rename {timelock-contract => basics/timelock-contract}/test/AElf.Contracts.TimelockContract.Tests.csproj (100%) rename {timelock-contract => basics/timelock-contract}/test/Protobuf/base/acs1.proto (100%) rename {timelock-contract => basics/timelock-contract}/test/Protobuf/message/authority_info.proto (100%) rename {timelock-contract => basics/timelock-contract}/test/Protobuf/stub/timelock_contract.proto (100%) rename {timelock-contract => basics/timelock-contract}/test/Protobuf/stub/token_contract.proto (100%) rename {timelock-contract => basics/timelock-contract}/test/TimelockContractTests.cs (99%) rename {timelock-contract => basics/timelock-contract}/test/TimelockContractTestsInitialization.cs (100%) rename {timelock-contract => basics/timelock-contract}/test/_Setup.cs (100%) diff --git a/timelock-contract/src/AElf.Contracts.TimelockContract.csproj b/basics/timelock-contract/src/AElf.Contracts.TimelockContract.csproj similarity index 100% rename from timelock-contract/src/AElf.Contracts.TimelockContract.csproj rename to basics/timelock-contract/src/AElf.Contracts.TimelockContract.csproj diff --git a/timelock-contract/src/ContractsReferences.cs b/basics/timelock-contract/src/ContractsReferences.cs similarity index 100% rename from timelock-contract/src/ContractsReferences.cs rename to basics/timelock-contract/src/ContractsReferences.cs diff --git a/timelock-contract/src/Protobuf/base/acs1.proto b/basics/timelock-contract/src/Protobuf/base/acs1.proto similarity index 100% rename from timelock-contract/src/Protobuf/base/acs1.proto rename to basics/timelock-contract/src/Protobuf/base/acs1.proto diff --git a/timelock-contract/src/Protobuf/contract/timelock_contract.proto b/basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto similarity index 100% rename from timelock-contract/src/Protobuf/contract/timelock_contract.proto rename to basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto diff --git a/timelock-contract/src/Protobuf/message/authority_info.proto b/basics/timelock-contract/src/Protobuf/message/authority_info.proto similarity index 100% rename from timelock-contract/src/Protobuf/message/authority_info.proto rename to basics/timelock-contract/src/Protobuf/message/authority_info.proto diff --git a/timelock-contract/src/Protobuf/reference/token_contract.proto b/basics/timelock-contract/src/Protobuf/reference/token_contract.proto similarity index 100% rename from timelock-contract/src/Protobuf/reference/token_contract.proto rename to basics/timelock-contract/src/Protobuf/reference/token_contract.proto diff --git a/timelock-contract/src/TimelockContract.cs b/basics/timelock-contract/src/TimelockContract.cs similarity index 100% rename from timelock-contract/src/TimelockContract.cs rename to basics/timelock-contract/src/TimelockContract.cs diff --git a/timelock-contract/src/TimelockContractConstants.cs b/basics/timelock-contract/src/TimelockContractConstants.cs similarity index 100% rename from timelock-contract/src/TimelockContractConstants.cs rename to basics/timelock-contract/src/TimelockContractConstants.cs diff --git a/timelock-contract/src/TimelockContractHelper.cs b/basics/timelock-contract/src/TimelockContractHelper.cs similarity index 100% rename from timelock-contract/src/TimelockContractHelper.cs rename to basics/timelock-contract/src/TimelockContractHelper.cs diff --git a/timelock-contract/src/TimelockContractState.cs b/basics/timelock-contract/src/TimelockContractState.cs similarity index 100% rename from timelock-contract/src/TimelockContractState.cs rename to basics/timelock-contract/src/TimelockContractState.cs diff --git a/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj b/basics/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj similarity index 100% rename from timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj rename to basics/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj diff --git a/timelock-contract/test/Protobuf/base/acs1.proto b/basics/timelock-contract/test/Protobuf/base/acs1.proto similarity index 100% rename from timelock-contract/test/Protobuf/base/acs1.proto rename to basics/timelock-contract/test/Protobuf/base/acs1.proto diff --git a/timelock-contract/test/Protobuf/message/authority_info.proto b/basics/timelock-contract/test/Protobuf/message/authority_info.proto similarity index 100% rename from timelock-contract/test/Protobuf/message/authority_info.proto rename to basics/timelock-contract/test/Protobuf/message/authority_info.proto diff --git a/timelock-contract/test/Protobuf/stub/timelock_contract.proto b/basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto similarity index 100% rename from timelock-contract/test/Protobuf/stub/timelock_contract.proto rename to basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto diff --git a/timelock-contract/test/Protobuf/stub/token_contract.proto b/basics/timelock-contract/test/Protobuf/stub/token_contract.proto similarity index 100% rename from timelock-contract/test/Protobuf/stub/token_contract.proto rename to basics/timelock-contract/test/Protobuf/stub/token_contract.proto diff --git a/timelock-contract/test/TimelockContractTests.cs b/basics/timelock-contract/test/TimelockContractTests.cs similarity index 99% rename from timelock-contract/test/TimelockContractTests.cs rename to basics/timelock-contract/test/TimelockContractTests.cs index 9d52c55..b86c942 100644 --- a/timelock-contract/test/TimelockContractTests.cs +++ b/basics/timelock-contract/test/TimelockContractTests.cs @@ -3,6 +3,7 @@ using AElf.Kernel; using AElf.Types; using Google.Protobuf.WellKnownTypes; +using Shouldly; using Xunit; namespace AElf.Contracts.Timelock diff --git a/timelock-contract/test/TimelockContractTestsInitialization.cs b/basics/timelock-contract/test/TimelockContractTestsInitialization.cs similarity index 100% rename from timelock-contract/test/TimelockContractTestsInitialization.cs rename to basics/timelock-contract/test/TimelockContractTestsInitialization.cs diff --git a/timelock-contract/test/_Setup.cs b/basics/timelock-contract/test/_Setup.cs similarity index 100% rename from timelock-contract/test/_Setup.cs rename to basics/timelock-contract/test/_Setup.cs From 296c285f02e54a468f766b4e474acfbf1f1036d7 Mon Sep 17 00:00:00 2001 From: aelf-lu Date: Thu, 22 Jun 2023 18:09:19 +0800 Subject: [PATCH 06/20] modify method implementation --- .../AElf.Contracts.TimelockContract.csproj | 13 +-- .../Protobuf/contract/timelock_contract.proto | 44 ++++------- .../timelock-contract/src/TimelockContract.cs | 41 ++-------- .../src/TimelockContractHelper.cs | 8 -- ...lf.Contracts.TimelockContract.Tests.csproj | 49 +++--------- .../Protobuf/stub/timelock_contract.proto | 44 ++++------- .../test/TimelockContractTests.cs | 79 ++++++++++++------- .../TimelockContractTestsInitialization.cs | 6 +- basics/timelock-contract/test/_Setup.cs | 10 +-- nuget.config | 6 ++ 10 files changed, 120 insertions(+), 180 deletions(-) create mode 100644 nuget.config diff --git a/basics/timelock-contract/src/AElf.Contracts.TimelockContract.csproj b/basics/timelock-contract/src/AElf.Contracts.TimelockContract.csproj index 3769f32..ce2fc55 100644 --- a/basics/timelock-contract/src/AElf.Contracts.TimelockContract.csproj +++ b/basics/timelock-contract/src/AElf.Contracts.TimelockContract.csproj @@ -10,18 +10,21 @@ true + + $(OutputPath)obj/$(Configuration)/$(TargetFramework)/ + + + + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - \ No newline at end of file diff --git a/basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto b/basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto index cd13870..480d9b9 100644 --- a/basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto +++ b/basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto @@ -16,9 +16,7 @@ service TimelockContract { } rpc SetDelay (DelayInput) returns (google.protobuf.Empty) { } - rpc AcceptAdmin (google.protobuf.Empty) returns (google.protobuf.Empty) { - } - rpc SetPendingAdmin (SetPendingAdminInput) returns (google.protobuf.Empty) { + rpc ChangeAdmin (ChangeAdminInput) returns (google.protobuf.Empty) { } rpc QueueTransaction (TransactionInput) returns (google.protobuf.Empty) { } @@ -31,9 +29,6 @@ service TimelockContract { rpc GetDelay (google.protobuf.Empty) returns (google.protobuf.Int64Value) { option (aelf.is_view) = true; } - rpc GetPendingAdmin (google.protobuf.Empty) returns (aelf.Address) { - option (aelf.is_view) = true; - } rpc GetAdmin (google.protobuf.Empty) returns (aelf.Address) { option (aelf.is_view) = true; } @@ -47,17 +42,16 @@ message DelayInput { uint64 delay = 1; } -message SetPendingAdminInput { - aelf.Address pending_admin = 1; +message ChangeAdminInput { + aelf.Address new_admin = 1; } message TransactionInput { aelf.Hash txn_hash = 1; aelf.Address target = 2; - uint64 amount = 3; - string signature = 4; - string data = 5; - google.protobuf.Timestamp eta = 6; + string method = 3; + bytes data = 4; + google.protobuf.Timestamp eta = 5; } // Events @@ -66,11 +60,6 @@ message NewAdmin { aelf.Address new_admin = 1; } -message NewPendingAdmin { - option (aelf.is_event) = true; - aelf.Address new_pending_admin = 1; -} - message NewDelay { option (aelf.is_event) = true; uint64 new_delay = 1; @@ -80,28 +69,25 @@ message QueueTransaction { option (aelf.is_event) = true; aelf.Hash txn_hash = 1; aelf.Address target = 2; - uint64 amount = 3; - string signature = 4; - string data = 5; - google.protobuf.Timestamp eta = 6; + string method = 3; + bytes data = 4; // data equivalent to ConvertToByteString(IMessage) + google.protobuf.Timestamp eta = 5; } message ExecuteTransaction { option (aelf.is_event) = true; aelf.Hash txn_hash = 1; aelf.Address target = 2; - uint64 amount = 3; - string signature = 4; - string data = 5; - google.protobuf.Timestamp eta = 6; + string method = 3; + bytes data = 4; + google.protobuf.Timestamp eta = 5; } message CancelTransaction { option (aelf.is_event) = true; aelf.Hash txn_hash = 1; aelf.Address target = 2; - uint64 amount = 3; - string signature = 4; - string data = 5; - google.protobuf.Timestamp eta = 6; + string method = 3; + bytes data = 4; + google.protobuf.Timestamp eta = 5; } \ No newline at end of file diff --git a/basics/timelock-contract/src/TimelockContract.cs b/basics/timelock-contract/src/TimelockContract.cs index 22423bb..e359be8 100644 --- a/basics/timelock-contract/src/TimelockContract.cs +++ b/basics/timelock-contract/src/TimelockContract.cs @@ -1,4 +1,3 @@ -using AElf.Contracts.MultiToken; using AElf.CSharp.Core.Extension; using AElf.Sdk.CSharp; using AElf.Types; @@ -36,12 +35,11 @@ public override Empty SetDelay(DelayInput input) return new Empty(); } - public override Empty AcceptAdmin(Empty input) + public override Empty ChangeAdmin(ChangeAdminInput input) { - // Assert(Context.Sender == State.PendingAdmin.Value, "No permission"); - Assert(State.Admin.Value == Context.Sender, "No permission"); - Assert(State.PendingAdmin.Value != null, "PendingAdmin must not be null"); - State.Admin.Value = State.PendingAdmin.Value; + Assert(Context.Sender == Context.Self, "No permission"); + Assert(input.NewAdmin != null, "NewAdmin must not be null"); + State.Admin.Value = input.NewAdmin; Context.Fire(new NewAdmin { NewAdmin_ = State.Admin.Value @@ -49,18 +47,6 @@ public override Empty AcceptAdmin(Empty input) return new Empty(); } - public override Empty SetPendingAdmin(SetPendingAdminInput input) - { - // Assert(Context.Sender == Context.Self, "No permission"); - Assert(State.Admin.Value == Context.Sender, "No permission"); - State.PendingAdmin.Value = input.PendingAdmin; - Context.Fire(new NewPendingAdmin - { - NewPendingAdmin_ = input.PendingAdmin - }); - return new Empty(); - } - public override Empty QueueTransaction(TransactionInput input) { Assert(Context.Sender == State.Admin.Value, "No permission"); @@ -71,8 +57,7 @@ public override Empty QueueTransaction(TransactionInput input) { TxnHash = txnHash, Target = input.Target, - Amount = input.Amount, - Signature = input.Signature, + Method = input.Method, Data = input.Data, Eta = input.Eta }); @@ -88,8 +73,7 @@ public override Empty CancelTransaction(TransactionInput input) { TxnHash = txnHash, Target = input.Target, - Amount = input.Amount, - Signature = input.Signature, + Method = input.Method, Data = input.Data, Eta = input.Eta }); @@ -105,21 +89,12 @@ public override Empty ExecuteTransaction(TransactionInput input) Assert(Context.CurrentBlockTime <= input.Eta.AddDays(TimelockContractConstants.GRACE_PERIOD), "executeTransaction: Transaction is stale"); Address from = State.TransactionQueue[txnHash]; - State.TokenContract.Value = Context.GetContractAddressByName(SmartContractConstants.TokenContractSystemName); - State.TokenContract.TransferFrom.Send(new TransferFromInput - { - From = from, - To = input.Target, - Amount = (long) input.Amount, - Symbol = TimelockContractConstants.SYMBOL, - Memo = "Execution" - }); + Context.SendInline(input.Target, input.Method, input.Data); Context.Fire(new ExecuteTransaction { TxnHash = txnHash, Target = input.Target, - Amount = input.Amount, - Signature = input.Signature, + Method = input.Method, Data = input.Data, Eta = input.Eta }); diff --git a/basics/timelock-contract/src/TimelockContractHelper.cs b/basics/timelock-contract/src/TimelockContractHelper.cs index 9478f57..b5830c5 100644 --- a/basics/timelock-contract/src/TimelockContractHelper.cs +++ b/basics/timelock-contract/src/TimelockContractHelper.cs @@ -1,6 +1,3 @@ -using AElf.Contracts.MultiToken; -using AElf.CSharp.Core.Extension; -using AElf.Sdk.CSharp; using AElf.Types; using Google.Protobuf.WellKnownTypes; @@ -17,11 +14,6 @@ public override Int64Value GetDelay(Empty input) return delay; } - public override Address GetPendingAdmin(Empty input) - { - return State.PendingAdmin.Value; - } - public override Address GetAdmin(Empty input) { return State.Admin.Value; diff --git a/basics/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj b/basics/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj index a28757f..125ae03 100644 --- a/basics/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj +++ b/basics/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj @@ -3,15 +3,17 @@ net6.0 AElf.Contracts.Timelock - http://10.0.1.239:8081/repository/nuget-group/index.json 0436 + + $(OutputPath)obj/$(Configuration)/$(TargetFramework)/ + - + @@ -21,7 +23,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -40,44 +42,11 @@ - + - + - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto b/basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto index cd13870..480d9b9 100644 --- a/basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto +++ b/basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto @@ -16,9 +16,7 @@ service TimelockContract { } rpc SetDelay (DelayInput) returns (google.protobuf.Empty) { } - rpc AcceptAdmin (google.protobuf.Empty) returns (google.protobuf.Empty) { - } - rpc SetPendingAdmin (SetPendingAdminInput) returns (google.protobuf.Empty) { + rpc ChangeAdmin (ChangeAdminInput) returns (google.protobuf.Empty) { } rpc QueueTransaction (TransactionInput) returns (google.protobuf.Empty) { } @@ -31,9 +29,6 @@ service TimelockContract { rpc GetDelay (google.protobuf.Empty) returns (google.protobuf.Int64Value) { option (aelf.is_view) = true; } - rpc GetPendingAdmin (google.protobuf.Empty) returns (aelf.Address) { - option (aelf.is_view) = true; - } rpc GetAdmin (google.protobuf.Empty) returns (aelf.Address) { option (aelf.is_view) = true; } @@ -47,17 +42,16 @@ message DelayInput { uint64 delay = 1; } -message SetPendingAdminInput { - aelf.Address pending_admin = 1; +message ChangeAdminInput { + aelf.Address new_admin = 1; } message TransactionInput { aelf.Hash txn_hash = 1; aelf.Address target = 2; - uint64 amount = 3; - string signature = 4; - string data = 5; - google.protobuf.Timestamp eta = 6; + string method = 3; + bytes data = 4; + google.protobuf.Timestamp eta = 5; } // Events @@ -66,11 +60,6 @@ message NewAdmin { aelf.Address new_admin = 1; } -message NewPendingAdmin { - option (aelf.is_event) = true; - aelf.Address new_pending_admin = 1; -} - message NewDelay { option (aelf.is_event) = true; uint64 new_delay = 1; @@ -80,28 +69,25 @@ message QueueTransaction { option (aelf.is_event) = true; aelf.Hash txn_hash = 1; aelf.Address target = 2; - uint64 amount = 3; - string signature = 4; - string data = 5; - google.protobuf.Timestamp eta = 6; + string method = 3; + bytes data = 4; // data equivalent to ConvertToByteString(IMessage) + google.protobuf.Timestamp eta = 5; } message ExecuteTransaction { option (aelf.is_event) = true; aelf.Hash txn_hash = 1; aelf.Address target = 2; - uint64 amount = 3; - string signature = 4; - string data = 5; - google.protobuf.Timestamp eta = 6; + string method = 3; + bytes data = 4; + google.protobuf.Timestamp eta = 5; } message CancelTransaction { option (aelf.is_event) = true; aelf.Hash txn_hash = 1; aelf.Address target = 2; - uint64 amount = 3; - string signature = 4; - string data = 5; - google.protobuf.Timestamp eta = 6; + string method = 3; + bytes data = 4; + google.protobuf.Timestamp eta = 5; } \ No newline at end of file diff --git a/basics/timelock-contract/test/TimelockContractTests.cs b/basics/timelock-contract/test/TimelockContractTests.cs index b86c942..eb51729 100644 --- a/basics/timelock-contract/test/TimelockContractTests.cs +++ b/basics/timelock-contract/test/TimelockContractTests.cs @@ -2,6 +2,7 @@ using AElf.Contracts.MultiToken; using AElf.Kernel; using AElf.Types; +using Google.Protobuf; using Google.Protobuf.WellKnownTypes; using Shouldly; using Xunit; @@ -28,13 +29,14 @@ public async Task SetDelayTests() } [Fact] - public async Task AcceptAdminTests() + public async Task NewAdminTests() { await InitializeTests(); - SetPendingAdminInput input = new SetPendingAdminInput(); - input.PendingAdmin = UserAddress; - await TimelockContractStub.SetPendingAdmin.SendAsync(input); - await TimelockContractStub.AcceptAdmin.SendAsync(new Empty()); + ChangeAdminInput changeAdminInput = new ChangeAdminInput + { + NewAdmin = UserAddress + }; + await TimelockContractStub.ChangeAdmin.SendAsync(changeAdminInput); var result = await TimelockContractStub.GetAdmin.CallAsync(new Empty()); result.ShouldBe(UserAddress); } @@ -43,16 +45,21 @@ public async Task AcceptAdminTests() public async Task QueueTransactionTests() { await InitializeTests(); - TransactionInput input = new TransactionInput + TransferInput transferInput = new TransferInput + { + To = UserAddress, + Symbol = "ELF", + Amount = 100 + }; + TransactionInput transactionInput = new TransactionInput { Target = UserAddress, - Amount = 3L, - Data = "", + Method = "Transfer", + Data = transferInput.ToByteString(), Eta = TimestampHelper.GetUtcNow(), - Signature = "sign" }; - Hash txnHash = HashHelper.ComputeFrom(input); - await TimelockContractStub.QueueTransaction.SendAsync(input); + Hash txnHash = HashHelper.ComputeFrom(transactionInput); + await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); var result = await TimelockContractStub.GetTransaction.CallAsync(txnHash); result.ShouldNotBeNull(); } @@ -61,17 +68,22 @@ public async Task QueueTransactionTests() public async Task CancelTransactionTests() { await InitializeTests(); - TransactionInput input = new TransactionInput + TransferInput transferInput = new TransferInput + { + To = UserAddress, + Symbol = "ELF", + Amount = 100 + }; + TransactionInput transactionInput = new TransactionInput { Target = UserAddress, - Amount = 3L, - Data = "", + Method = "Transfer", + Data = transferInput.ToByteString(), Eta = TimestampHelper.GetUtcNow(), - Signature = "sign" }; - Hash txnHash = HashHelper.ComputeFrom(input); - await TimelockContractStub.QueueTransaction.SendAsync(input); - await TimelockContractStub.CancelTransaction.SendAsync(input); + Hash txnHash = HashHelper.ComputeFrom(transactionInput); + await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); + await TimelockContractStub.CancelTransaction.SendAsync(transactionInput); var result = await TimelockContractStub.GetTransaction.CallAsync(txnHash); result.ShouldBe(new Address()); } @@ -80,13 +92,13 @@ private async Task InitializeAsync() { await TokenContractStub.Transfer.SendAsync(new TransferInput { - To = DAppContractAddress, + To = ContractAddress, Symbol = "ELF", Amount = 1000_00000000 }); await TokenContractStub.Approve.SendAsync(new ApproveInput { - Spender = DAppContractAddress, + Spender = ContractAddress, Symbol = "ELF", Amount = 1000_00000000 }); @@ -97,23 +109,34 @@ public async Task ExecuteTransactionTests() { await InitializeTests(); await InitializeAsync(); - TransactionInput input = new TransactionInput + var balance1 = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { - Target = UserAddress, - Amount = 5L, - Data = "", + Owner = UserAddress, + Symbol = "ELF" + }); + _testOutputHelper.WriteLine(balance1.Balance.ToString()); + TransferInput transferInput = new TransferInput + { + To = UserAddress, + Symbol = "ELF", + Amount = 500 + }; + TransactionInput transactionInput = new TransactionInput + { + Target = TokenContractAddress, + Method = "Transfer", + Data = transferInput.ToByteString(), Eta = TimestampHelper.GetUtcNow(), - Signature = "sign" }; - await TimelockContractStub.QueueTransaction.SendAsync(input); - await TimelockContractStub.ExecuteTransaction.SendAsync(input); + await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); + await TimelockContractStub.ExecuteTransaction.SendAsync(transactionInput); var balance2 = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { Owner = UserAddress, Symbol = "ELF" }); - balance2.Balance.ShouldBe(5L); + balance2.Balance.ShouldBe(500L); } } diff --git a/basics/timelock-contract/test/TimelockContractTestsInitialization.cs b/basics/timelock-contract/test/TimelockContractTestsInitialization.cs index d73c8b3..0af1bc9 100644 --- a/basics/timelock-contract/test/TimelockContractTestsInitialization.cs +++ b/basics/timelock-contract/test/TimelockContractTestsInitialization.cs @@ -13,6 +13,8 @@ public partial class TimelockContractTests // private readonly ECKeyPair KeyPair; internal TimelockContractContainer.TimelockContractStub TimelockContractStub; internal TokenContractContainer.TokenContractStub TokenContractStub; + + protected Address TokenContractAddress; protected ECKeyPair DefaultKeyPair => Accounts[0].KeyPair; protected Address DefaultAddress => Accounts[0].Address; protected ECKeyPair UserKeyPair => Accounts[1].KeyPair; @@ -23,8 +25,8 @@ public TimelockContractTests(ITestOutputHelper testOutputHelper) _testOutputHelper = testOutputHelper; // KeyPair = SampleAccount.Accounts.First().KeyPair; TimelockContractStub = GetContractStub(DefaultKeyPair); - TokenContractStub = GetTester( - GetAddress(TokenSmartContractAddressNameProvider.StringName), DefaultKeyPair); + TokenContractAddress = GetAddress(TokenSmartContractAddressNameProvider.StringName); + TokenContractStub = GetTester(TokenContractAddress, DefaultKeyPair); } } diff --git a/basics/timelock-contract/test/_Setup.cs b/basics/timelock-contract/test/_Setup.cs index 003fc28..dabe617 100644 --- a/basics/timelock-contract/test/_Setup.cs +++ b/basics/timelock-contract/test/_Setup.cs @@ -1,7 +1,6 @@ -using AElf.Boilerplate.TestBase.Contract; -using AElf.Boilerplate.TestBase.DAppContract; -using AElf.Cryptography.ECDSA; +using AElf.Cryptography.ECDSA; using AElf.CSharp.Core; +using AElf.Testing.TestBase; namespace AElf.Contracts.Timelock { @@ -9,14 +8,13 @@ public class Module : ContractTestModule { } - public class TestBase : DAppContractTestBase + public class TestBase : ContractTestBase { // You can get address of any contract via GetAddress method, for example: // internal Address DAppContractAddress => GetAddress(DAppSmartContractAddressNameProvider.StringName); - protected TStub GetContractStub(ECKeyPair senderKeyPair) where TStub:ContractStubBase, new() { - return GetTester(DAppContractAddress, senderKeyPair); + return GetTester(ContractAddress, senderKeyPair); } } } \ No newline at end of file diff --git a/nuget.config b/nuget.config new file mode 100644 index 0000000..cb448ea --- /dev/null +++ b/nuget.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From 640ef67de4282da35dd2e07e228031419e9ca2f6 Mon Sep 17 00:00:00 2001 From: aelf-lu Date: Thu, 22 Jun 2023 18:32:35 +0800 Subject: [PATCH 07/20] modify unit test --- .../test/TimelockContractTests.cs | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/basics/timelock-contract/test/TimelockContractTests.cs b/basics/timelock-contract/test/TimelockContractTests.cs index eb51729..682b67f 100644 --- a/basics/timelock-contract/test/TimelockContractTests.cs +++ b/basics/timelock-contract/test/TimelockContractTests.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; using AElf.Contracts.MultiToken; +using AElf.CSharp.Core; using AElf.Kernel; using AElf.Types; using Google.Protobuf; @@ -111,7 +112,7 @@ public async Task ExecuteTransactionTests() await InitializeAsync(); var balance1 = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { - Owner = UserAddress, + Owner = DefaultAddress, Symbol = "ELF" }); _testOutputHelper.WriteLine(balance1.Balance.ToString()); @@ -119,13 +120,21 @@ public async Task ExecuteTransactionTests() { To = UserAddress, Symbol = "ELF", - Amount = 500 + Amount = 502 + }; + TransferFromInput transferFromInput = new TransferFromInput + { + From = DefaultAddress, + To = UserAddress, + Amount = 502, + Symbol = "ELF", + Memo = "TEST" }; TransactionInput transactionInput = new TransactionInput { Target = TokenContractAddress, - Method = "Transfer", - Data = transferInput.ToByteString(), + Method = "TransferFrom", + Data = transferFromInput.ToByteString(), Eta = TimestampHelper.GetUtcNow(), }; await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); @@ -136,7 +145,13 @@ public async Task ExecuteTransactionTests() Owner = UserAddress, Symbol = "ELF" }); - balance2.Balance.ShouldBe(500L); + var balance3 = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = DefaultAddress, + Symbol = "ELF" + }); + balance2.Balance.ShouldBe(502); + balance3.Balance.ShouldBe(balance1.Balance.Sub(502)); } } From fb011b6599a5f9568a4cb95b1be74efdb208108e Mon Sep 17 00:00:00 2001 From: aelf-lu Date: Mon, 26 Jun 2023 16:37:19 +0800 Subject: [PATCH 08/20] fix bug --- .../Protobuf/contract/timelock_contract.proto | 8 ++--- .../timelock-contract/src/TimelockContract.cs | 20 ++++++------- .../src/TimelockContractConstants.cs | 7 +++-- .../src/TimelockContractHelper.cs | 9 ++++-- .../src/TimelockContractState.cs | 4 +-- .../Protobuf/stub/timelock_contract.proto | 8 ++--- .../test/TimelockContractTests.cs | 30 +++++++++++++++---- 7 files changed, 54 insertions(+), 32 deletions(-) diff --git a/basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto b/basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto index 480d9b9..dff4733 100644 --- a/basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto +++ b/basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto @@ -18,7 +18,7 @@ service TimelockContract { } rpc ChangeAdmin (ChangeAdminInput) returns (google.protobuf.Empty) { } - rpc QueueTransaction (TransactionInput) returns (google.protobuf.Empty) { + rpc QueueTransaction (TransactionInput) returns (aelf.Hash) { } rpc ExecuteTransaction (TransactionInput) returns (google.protobuf.Empty) { } @@ -32,7 +32,7 @@ service TimelockContract { rpc GetAdmin (google.protobuf.Empty) returns (aelf.Address) { option (aelf.is_view) = true; } - rpc GetTransaction(aelf.Hash) returns (aelf.Address) { + rpc GetTransaction(aelf.Hash) returns (google.protobuf.BoolValue) { option (aelf.is_view) = true; } } @@ -43,7 +43,7 @@ message DelayInput { } message ChangeAdminInput { - aelf.Address new_admin = 1; + aelf.Address admin = 1; } message TransactionInput { @@ -57,7 +57,7 @@ message TransactionInput { // Events message NewAdmin { option (aelf.is_event) = true; - aelf.Address new_admin = 1; + aelf.Address admin = 1; } message NewDelay { diff --git a/basics/timelock-contract/src/TimelockContract.cs b/basics/timelock-contract/src/TimelockContract.cs index e359be8..927a068 100644 --- a/basics/timelock-contract/src/TimelockContract.cs +++ b/basics/timelock-contract/src/TimelockContract.cs @@ -38,21 +38,21 @@ public override Empty SetDelay(DelayInput input) public override Empty ChangeAdmin(ChangeAdminInput input) { Assert(Context.Sender == Context.Self, "No permission"); - Assert(input.NewAdmin != null, "NewAdmin must not be null"); - State.Admin.Value = input.NewAdmin; + Assert(input.Admin != null, "NewAdmin must not be null"); + State.Admin.Value = input.Admin; Context.Fire(new NewAdmin { - NewAdmin_ = State.Admin.Value + Admin = State.Admin.Value }); return new Empty(); } - public override Empty QueueTransaction(TransactionInput input) + public override Hash QueueTransaction(TransactionInput input) { Assert(Context.Sender == State.Admin.Value, "No permission"); - Assert(input.Eta >= Context.CurrentBlockTime.AddDays(State.Delay.Value), "Estimated execution block must satisfy delay"); + Assert(input.Eta >= Context.CurrentBlockTime.AddSeconds(State.Delay.Value), "Estimated execution block must satisfy delay"); Hash txnHash = HashHelper.ComputeFrom(input); - State.TransactionQueue[txnHash] = Context.Sender; + State.TransactionQueue[txnHash] = true; Context.Fire(new QueueTransaction { TxnHash = txnHash, @@ -61,7 +61,7 @@ public override Empty QueueTransaction(TransactionInput input) Data = input.Data, Eta = input.Eta }); - return new Empty(); + return txnHash; } public override Empty CancelTransaction(TransactionInput input) @@ -84,11 +84,11 @@ public override Empty ExecuteTransaction(TransactionInput input) { Assert(Context.Sender == State.Admin.Value, "No permission"); Hash txnHash = HashHelper.ComputeFrom(input); - Assert(State.TransactionQueue[txnHash] != null, "executeTransaction: Transaction hasn't been queued"); + Assert(State.TransactionQueue[txnHash], "executeTransaction: Transaction hasn't been queued"); Assert(Context.CurrentBlockTime >= input.Eta, "executeTransaction: Transaction hasn't surpassed time lock"); - Assert(Context.CurrentBlockTime <= input.Eta.AddDays(TimelockContractConstants.GRACE_PERIOD), "executeTransaction: Transaction is stale"); + Assert(Context.CurrentBlockTime <= input.Eta.AddSeconds(TimelockContractConstants.GRACE_PERIOD), "executeTransaction: Transaction is stale"); - Address from = State.TransactionQueue[txnHash]; + State.TransactionQueue[txnHash] = false; Context.SendInline(input.Target, input.Method, input.Data); Context.Fire(new ExecuteTransaction { diff --git a/basics/timelock-contract/src/TimelockContractConstants.cs b/basics/timelock-contract/src/TimelockContractConstants.cs index e1107fe..2736621 100644 --- a/basics/timelock-contract/src/TimelockContractConstants.cs +++ b/basics/timelock-contract/src/TimelockContractConstants.cs @@ -3,8 +3,9 @@ namespace AElf.Contracts.Timelock public static class TimelockContractConstants { public const string SYMBOL = "ELF"; - public const long MIN_DELAY = 1L; - public const long MAX_DELAY = 3L; - public const long GRACE_PERIOD = 1L; + // unit is second + public const long MIN_DELAY = 1 * 24 * 60 * 60; + public const long MAX_DELAY = 3 * 24 * 60 * 60; + public const long GRACE_PERIOD = 2 * 24 * 60 * 60; } } \ No newline at end of file diff --git a/basics/timelock-contract/src/TimelockContractHelper.cs b/basics/timelock-contract/src/TimelockContractHelper.cs index b5830c5..1b03e1b 100644 --- a/basics/timelock-contract/src/TimelockContractHelper.cs +++ b/basics/timelock-contract/src/TimelockContractHelper.cs @@ -18,10 +18,13 @@ public override Address GetAdmin(Empty input) { return State.Admin.Value; } - - public override Address GetTransaction(Hash input) + + public override BoolValue GetTransaction(Hash input) { - return State.TransactionQueue[input]; + return new BoolValue + { + Value = State.TransactionQueue[input] + }; } } diff --git a/basics/timelock-contract/src/TimelockContractState.cs b/basics/timelock-contract/src/TimelockContractState.cs index 92b335e..c19dbd4 100644 --- a/basics/timelock-contract/src/TimelockContractState.cs +++ b/basics/timelock-contract/src/TimelockContractState.cs @@ -1,3 +1,4 @@ +using System; using AElf.Sdk.CSharp.State; using AElf.Types; @@ -7,8 +8,7 @@ public partial class TimelockContractState : ContractState { public BoolState Initialized { get; set; } public SingletonState
Admin { get; set; } - public SingletonState
PendingAdmin { get; set; } public SingletonState Delay { get; set; } - public MappedState TransactionQueue { get; set; } + public MappedState TransactionQueue { get; set; } } } \ No newline at end of file diff --git a/basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto b/basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto index 480d9b9..dff4733 100644 --- a/basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto +++ b/basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto @@ -18,7 +18,7 @@ service TimelockContract { } rpc ChangeAdmin (ChangeAdminInput) returns (google.protobuf.Empty) { } - rpc QueueTransaction (TransactionInput) returns (google.protobuf.Empty) { + rpc QueueTransaction (TransactionInput) returns (aelf.Hash) { } rpc ExecuteTransaction (TransactionInput) returns (google.protobuf.Empty) { } @@ -32,7 +32,7 @@ service TimelockContract { rpc GetAdmin (google.protobuf.Empty) returns (aelf.Address) { option (aelf.is_view) = true; } - rpc GetTransaction(aelf.Hash) returns (aelf.Address) { + rpc GetTransaction(aelf.Hash) returns (google.protobuf.BoolValue) { option (aelf.is_view) = true; } } @@ -43,7 +43,7 @@ message DelayInput { } message ChangeAdminInput { - aelf.Address new_admin = 1; + aelf.Address admin = 1; } message TransactionInput { @@ -57,7 +57,7 @@ message TransactionInput { // Events message NewAdmin { option (aelf.is_event) = true; - aelf.Address new_admin = 1; + aelf.Address admin = 1; } message NewDelay { diff --git a/basics/timelock-contract/test/TimelockContractTests.cs b/basics/timelock-contract/test/TimelockContractTests.cs index 682b67f..d17039b 100644 --- a/basics/timelock-contract/test/TimelockContractTests.cs +++ b/basics/timelock-contract/test/TimelockContractTests.cs @@ -23,10 +23,10 @@ public async Task SetDelayTests() { await InitializeTests(); DelayInput input = new DelayInput(); - input.Delay = 3L; + input.Delay = 2 * 24 * 60 * 60; await TimelockContractStub.SetDelay.SendAsync(input); var result = await TimelockContractStub.GetDelay.CallAsync(new Empty()); - result.Value.ShouldBe(3L); + result.Value.ShouldBe(2 * 24 * 60 * 60); } [Fact] @@ -35,7 +35,7 @@ public async Task NewAdminTests() await InitializeTests(); ChangeAdminInput changeAdminInput = new ChangeAdminInput { - NewAdmin = UserAddress + Admin = UserAddress }; await TimelockContractStub.ChangeAdmin.SendAsync(changeAdminInput); var result = await TimelockContractStub.GetAdmin.CallAsync(new Empty()); @@ -62,7 +62,12 @@ public async Task QueueTransactionTests() Hash txnHash = HashHelper.ComputeFrom(transactionInput); await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); var result = await TimelockContractStub.GetTransaction.CallAsync(txnHash); - result.ShouldNotBeNull(); + var expectResult = new BoolValue + { + Value = true + }; + result.ShouldBe(expectResult); + _testOutputHelper.WriteLine(txnHash.ToString()); } [Fact] @@ -86,7 +91,11 @@ public async Task CancelTransactionTests() await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); await TimelockContractStub.CancelTransaction.SendAsync(transactionInput); var result = await TimelockContractStub.GetTransaction.CallAsync(txnHash); - result.ShouldBe(new Address()); + var expectResult = new BoolValue + { + Value = false + }; + result.ShouldBe(expectResult); } private async Task InitializeAsync() @@ -105,6 +114,9 @@ await TokenContractStub.Approve.SendAsync(new ApproveInput }); } + /** + * Because of time lock limit, need to comment out the assert of the ExecuteTransaction method. + */ [Fact] public async Task ExecuteTransactionTests() { @@ -137,7 +149,7 @@ public async Task ExecuteTransactionTests() Data = transferFromInput.ToByteString(), Eta = TimestampHelper.GetUtcNow(), }; - await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); + var txnHash = await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); await TimelockContractStub.ExecuteTransaction.SendAsync(transactionInput); var balance2 = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput @@ -152,6 +164,12 @@ public async Task ExecuteTransactionTests() }); balance2.Balance.ShouldBe(502); balance3.Balance.ShouldBe(balance1.Balance.Sub(502)); + var result = await TimelockContractStub.GetTransaction.CallAsync(txnHash.Output); + var expectResult = new BoolValue + { + Value = false + }; + result.ShouldBe(expectResult); } } From 3b9b6e970c3e4d0be4d0ccb474eafa160b9aadbc Mon Sep 17 00:00:00 2001 From: aelf-lu Date: Wed, 16 Aug 2023 21:25:02 +0800 Subject: [PATCH 09/20] fix: fix unit test --- .../Protobuf/contract/timelock_contract.proto | 16 +++---- .../timelock-contract/src/TimelockContract.cs | 36 ++++---------- .../src/TimelockContractHelper.cs | 4 +- .../src/TimelockContractState.cs | 3 +- .../Protobuf/stub/timelock_contract.proto | 16 +++---- .../test/TimelockContractTests.cs | 30 +++++------- .../TimelockContractTestsInitialization.cs | 33 ------------- basics/timelock-contract/test/_Setup.cs | 47 +++++++++++++++---- 8 files changed, 78 insertions(+), 107 deletions(-) delete mode 100644 basics/timelock-contract/test/TimelockContractTestsInitialization.cs diff --git a/basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto b/basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto index dff4733..cfcc59d 100644 --- a/basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto +++ b/basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto @@ -12,9 +12,7 @@ service TimelockContract { option (aelf.csharp_state) = "AElf.Contracts.Timelock.TimelockContractState"; // Actions - rpc Initialize (google.protobuf.Empty) returns (google.protobuf.Empty) { - } - rpc SetDelay (DelayInput) returns (google.protobuf.Empty) { + rpc Initialize (InitializeInput) returns (google.protobuf.Empty) { } rpc ChangeAdmin (ChangeAdminInput) returns (google.protobuf.Empty) { } @@ -26,7 +24,7 @@ service TimelockContract { } // View - rpc GetDelay (google.protobuf.Empty) returns (google.protobuf.Int64Value) { + rpc GetDelay (google.protobuf.Empty) returns (google.protobuf.UInt64Value) { option (aelf.is_view) = true; } rpc GetAdmin (google.protobuf.Empty) returns (aelf.Address) { @@ -38,7 +36,7 @@ service TimelockContract { } // Params -message DelayInput { +message InitializeInput { uint64 delay = 1; } @@ -51,7 +49,7 @@ message TransactionInput { aelf.Address target = 2; string method = 3; bytes data = 4; - google.protobuf.Timestamp eta = 5; + google.protobuf.Timestamp execute_time = 5; } // Events @@ -71,7 +69,7 @@ message QueueTransaction { aelf.Address target = 2; string method = 3; bytes data = 4; // data equivalent to ConvertToByteString(IMessage) - google.protobuf.Timestamp eta = 5; + google.protobuf.Timestamp execute_time = 5; } message ExecuteTransaction { @@ -80,7 +78,7 @@ message ExecuteTransaction { aelf.Address target = 2; string method = 3; bytes data = 4; - google.protobuf.Timestamp eta = 5; + google.protobuf.Timestamp execute_time = 5; } message CancelTransaction { @@ -89,5 +87,5 @@ message CancelTransaction { aelf.Address target = 2; string method = 3; bytes data = 4; - google.protobuf.Timestamp eta = 5; + google.protobuf.Timestamp execute_time = 5; } \ No newline at end of file diff --git a/basics/timelock-contract/src/TimelockContract.cs b/basics/timelock-contract/src/TimelockContract.cs index 927a068..71980d9 100644 --- a/basics/timelock-contract/src/TimelockContract.cs +++ b/basics/timelock-contract/src/TimelockContract.cs @@ -7,37 +7,21 @@ namespace AElf.Contracts.Timelock { public partial class TimelockContract : TimelockContractContainer.TimelockContractBase { - public override Empty Initialize(Empty input) + public override Empty Initialize(InitializeInput input) { if (State.Initialized.Value) { return new Empty(); } - State.TokenContract.Value = - Context.GetContractAddressByName(SmartContractConstants.TokenContractSystemName); State.Admin.Value = Context.Sender; + State.Delay.Value = input.Delay; State.Initialized.Value = true; return new Empty(); } - - public override Empty SetDelay(DelayInput input) - { - Assert(State.Admin.Value == Context.Sender, "No permission"); - Assert(input != null, "Delay must not be null"); - Assert(input.Delay >= TimelockContractConstants.MIN_DELAY, "Delay must exceed minimum delay"); - Assert(input.Delay <= TimelockContractConstants.MAX_DELAY, "Delay must exceed maximum delay"); - State.Delay.Value = (long) input.Delay; - - Context.Fire(new NewDelay - { - NewDelay_ = input.Delay - }); - return new Empty(); - } public override Empty ChangeAdmin(ChangeAdminInput input) { - Assert(Context.Sender == Context.Self, "No permission"); + Assert(Context.Sender == State.Admin.Value, "No permission"); Assert(input.Admin != null, "NewAdmin must not be null"); State.Admin.Value = input.Admin; Context.Fire(new NewAdmin @@ -50,7 +34,7 @@ public override Empty ChangeAdmin(ChangeAdminInput input) public override Hash QueueTransaction(TransactionInput input) { Assert(Context.Sender == State.Admin.Value, "No permission"); - Assert(input.Eta >= Context.CurrentBlockTime.AddSeconds(State.Delay.Value), "Estimated execution block must satisfy delay"); + Assert(input.ExecuteTime >= Context.CurrentBlockTime.AddSeconds((long) State.Delay.Value), "Estimated execution block must satisfy delay"); Hash txnHash = HashHelper.ComputeFrom(input); State.TransactionQueue[txnHash] = true; Context.Fire(new QueueTransaction @@ -59,7 +43,7 @@ public override Hash QueueTransaction(TransactionInput input) Target = input.Target, Method = input.Method, Data = input.Data, - Eta = input.Eta + ExecuteTime = input.ExecuteTime }); return txnHash; } @@ -75,7 +59,7 @@ public override Empty CancelTransaction(TransactionInput input) Target = input.Target, Method = input.Method, Data = input.Data, - Eta = input.Eta + ExecuteTime = input.ExecuteTime }); return new Empty(); } @@ -85,9 +69,9 @@ public override Empty ExecuteTransaction(TransactionInput input) Assert(Context.Sender == State.Admin.Value, "No permission"); Hash txnHash = HashHelper.ComputeFrom(input); Assert(State.TransactionQueue[txnHash], "executeTransaction: Transaction hasn't been queued"); - Assert(Context.CurrentBlockTime >= input.Eta, "executeTransaction: Transaction hasn't surpassed time lock"); - Assert(Context.CurrentBlockTime <= input.Eta.AddSeconds(TimelockContractConstants.GRACE_PERIOD), "executeTransaction: Transaction is stale"); - + Assert(Context.CurrentBlockTime >= input.ExecuteTime, "executeTransaction: Transaction hasn't surpassed time lock"); + Assert(Context.CurrentBlockTime <= input.ExecuteTime.AddSeconds(TimelockContractConstants.GRACE_PERIOD), "executeTransaction: Transaction is stale"); + State.TransactionQueue[txnHash] = false; Context.SendInline(input.Target, input.Method, input.Data); Context.Fire(new ExecuteTransaction @@ -96,7 +80,7 @@ public override Empty ExecuteTransaction(TransactionInput input) Target = input.Target, Method = input.Method, Data = input.Data, - Eta = input.Eta + ExecuteTime = input.ExecuteTime }); State.TransactionQueue.Remove(txnHash); return new Empty(); diff --git a/basics/timelock-contract/src/TimelockContractHelper.cs b/basics/timelock-contract/src/TimelockContractHelper.cs index 1b03e1b..2dd5d4c 100644 --- a/basics/timelock-contract/src/TimelockContractHelper.cs +++ b/basics/timelock-contract/src/TimelockContractHelper.cs @@ -5,9 +5,9 @@ namespace AElf.Contracts.Timelock { public partial class TimelockContract { - public override Int64Value GetDelay(Empty input) + public override UInt64Value GetDelay(Empty input) { - var delay = new Int64Value + var delay = new UInt64Value { Value = State.Delay.Value }; diff --git a/basics/timelock-contract/src/TimelockContractState.cs b/basics/timelock-contract/src/TimelockContractState.cs index c19dbd4..8891354 100644 --- a/basics/timelock-contract/src/TimelockContractState.cs +++ b/basics/timelock-contract/src/TimelockContractState.cs @@ -1,4 +1,3 @@ -using System; using AElf.Sdk.CSharp.State; using AElf.Types; @@ -8,7 +7,7 @@ public partial class TimelockContractState : ContractState { public BoolState Initialized { get; set; } public SingletonState
Admin { get; set; } - public SingletonState Delay { get; set; } + public SingletonState Delay { get; set; } public MappedState TransactionQueue { get; set; } } } \ No newline at end of file diff --git a/basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto b/basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto index dff4733..cfcc59d 100644 --- a/basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto +++ b/basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto @@ -12,9 +12,7 @@ service TimelockContract { option (aelf.csharp_state) = "AElf.Contracts.Timelock.TimelockContractState"; // Actions - rpc Initialize (google.protobuf.Empty) returns (google.protobuf.Empty) { - } - rpc SetDelay (DelayInput) returns (google.protobuf.Empty) { + rpc Initialize (InitializeInput) returns (google.protobuf.Empty) { } rpc ChangeAdmin (ChangeAdminInput) returns (google.protobuf.Empty) { } @@ -26,7 +24,7 @@ service TimelockContract { } // View - rpc GetDelay (google.protobuf.Empty) returns (google.protobuf.Int64Value) { + rpc GetDelay (google.protobuf.Empty) returns (google.protobuf.UInt64Value) { option (aelf.is_view) = true; } rpc GetAdmin (google.protobuf.Empty) returns (aelf.Address) { @@ -38,7 +36,7 @@ service TimelockContract { } // Params -message DelayInput { +message InitializeInput { uint64 delay = 1; } @@ -51,7 +49,7 @@ message TransactionInput { aelf.Address target = 2; string method = 3; bytes data = 4; - google.protobuf.Timestamp eta = 5; + google.protobuf.Timestamp execute_time = 5; } // Events @@ -71,7 +69,7 @@ message QueueTransaction { aelf.Address target = 2; string method = 3; bytes data = 4; // data equivalent to ConvertToByteString(IMessage) - google.protobuf.Timestamp eta = 5; + google.protobuf.Timestamp execute_time = 5; } message ExecuteTransaction { @@ -80,7 +78,7 @@ message ExecuteTransaction { aelf.Address target = 2; string method = 3; bytes data = 4; - google.protobuf.Timestamp eta = 5; + google.protobuf.Timestamp execute_time = 5; } message CancelTransaction { @@ -89,5 +87,5 @@ message CancelTransaction { aelf.Address target = 2; string method = 3; bytes data = 4; - google.protobuf.Timestamp eta = 5; + google.protobuf.Timestamp execute_time = 5; } \ No newline at end of file diff --git a/basics/timelock-contract/test/TimelockContractTests.cs b/basics/timelock-contract/test/TimelockContractTests.cs index d17039b..e596087 100644 --- a/basics/timelock-contract/test/TimelockContractTests.cs +++ b/basics/timelock-contract/test/TimelockContractTests.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using AElf.Contracts.MultiToken; using AElf.CSharp.Core; +using AElf.CSharp.Core.Extension; using AElf.Kernel; using AElf.Types; using Google.Protobuf; @@ -12,23 +13,18 @@ namespace AElf.Contracts.Timelock { public partial class TimelockContractTests : TestBase { + private readonly Timestamp _currentTime = TimestampHelper.GetUtcNow(); + [Fact] public async Task InitializeTests() { - await TimelockContractStub.Initialize.SendAsync(new Empty()); + InitializeInput input = new InitializeInput + { + Delay = 5 + }; + await TimelockContractStub.Initialize.SendAsync(input); } - [Fact] - public async Task SetDelayTests() - { - await InitializeTests(); - DelayInput input = new DelayInput(); - input.Delay = 2 * 24 * 60 * 60; - await TimelockContractStub.SetDelay.SendAsync(input); - var result = await TimelockContractStub.GetDelay.CallAsync(new Empty()); - result.Value.ShouldBe(2 * 24 * 60 * 60); - } - [Fact] public async Task NewAdminTests() { @@ -57,7 +53,7 @@ public async Task QueueTransactionTests() Target = UserAddress, Method = "Transfer", Data = transferInput.ToByteString(), - Eta = TimestampHelper.GetUtcNow(), + ExecuteTime = _currentTime.AddSeconds(10) }; Hash txnHash = HashHelper.ComputeFrom(transactionInput); await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); @@ -67,7 +63,6 @@ public async Task QueueTransactionTests() Value = true }; result.ShouldBe(expectResult); - _testOutputHelper.WriteLine(txnHash.ToString()); } [Fact] @@ -85,7 +80,7 @@ public async Task CancelTransactionTests() Target = UserAddress, Method = "Transfer", Data = transferInput.ToByteString(), - Eta = TimestampHelper.GetUtcNow(), + ExecuteTime = _currentTime.AddSeconds(10) }; Hash txnHash = HashHelper.ComputeFrom(transactionInput); await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); @@ -127,7 +122,6 @@ public async Task ExecuteTransactionTests() Owner = DefaultAddress, Symbol = "ELF" }); - _testOutputHelper.WriteLine(balance1.Balance.ToString()); TransferInput transferInput = new TransferInput { To = UserAddress, @@ -147,9 +141,10 @@ public async Task ExecuteTransactionTests() Target = TokenContractAddress, Method = "TransferFrom", Data = transferFromInput.ToByteString(), - Eta = TimestampHelper.GetUtcNow(), + ExecuteTime = _currentTime.AddSeconds(10) }; var txnHash = await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); + BlockTimeProvider.SetBlockTime(_currentTime.AddSeconds(10)); await TimelockContractStub.ExecuteTransaction.SendAsync(transactionInput); var balance2 = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput @@ -171,7 +166,6 @@ public async Task ExecuteTransactionTests() }; result.ShouldBe(expectResult); } - } } \ No newline at end of file diff --git a/basics/timelock-contract/test/TimelockContractTestsInitialization.cs b/basics/timelock-contract/test/TimelockContractTestsInitialization.cs deleted file mode 100644 index 0af1bc9..0000000 --- a/basics/timelock-contract/test/TimelockContractTestsInitialization.cs +++ /dev/null @@ -1,33 +0,0 @@ -using AElf.Contracts.MultiToken; -using AElf.Cryptography.ECDSA; -using AElf.Kernel.Token; -using AElf.Types; -using Xunit.Abstractions; - -namespace AElf.Contracts.Timelock -{ - public partial class TimelockContractTests - { - private readonly ITestOutputHelper _testOutputHelper; - - // private readonly ECKeyPair KeyPair; - internal TimelockContractContainer.TimelockContractStub TimelockContractStub; - internal TokenContractContainer.TokenContractStub TokenContractStub; - - protected Address TokenContractAddress; - protected ECKeyPair DefaultKeyPair => Accounts[0].KeyPair; - protected Address DefaultAddress => Accounts[0].Address; - protected ECKeyPair UserKeyPair => Accounts[1].KeyPair; - protected Address UserAddress => Accounts[1].Address; - - public TimelockContractTests(ITestOutputHelper testOutputHelper) - { - _testOutputHelper = testOutputHelper; - // KeyPair = SampleAccount.Accounts.First().KeyPair; - TimelockContractStub = GetContractStub(DefaultKeyPair); - TokenContractAddress = GetAddress(TokenSmartContractAddressNameProvider.StringName); - TokenContractStub = GetTester(TokenContractAddress, DefaultKeyPair); - } - } - -} \ No newline at end of file diff --git a/basics/timelock-contract/test/_Setup.cs b/basics/timelock-contract/test/_Setup.cs index dabe617..ded1ae3 100644 --- a/basics/timelock-contract/test/_Setup.cs +++ b/basics/timelock-contract/test/_Setup.cs @@ -1,20 +1,51 @@ -using AElf.Cryptography.ECDSA; -using AElf.CSharp.Core; -using AElf.Testing.TestBase; +using AElf.Contracts.MultiToken; +using AElf.ContractTestBase.ContractTestKit; +using AElf.Cryptography.ECDSA; +using AElf.Types; +using Microsoft.Extensions.DependencyInjection; namespace AElf.Contracts.Timelock { - public class Module : ContractTestModule + // This class is used to load the context required for unit testing. + public class Module : Testing.TestBase.ContractTestModule { } - public class TestBase : ContractTestBase + + // The TestBase class inherit ContractTestBase class, which is used to define and get stub classes required for unit testing. + public class TestBase : Testing.TestBase.ContractTestBase { // You can get address of any contract via GetAddress method, for example: - // internal Address DAppContractAddress => GetAddress(DAppSmartContractAddressNameProvider.StringName); - protected TStub GetContractStub(ECKeyPair senderKeyPair) where TStub:ContractStubBase, new() + // internal Address ContractAddress => GetAddress(SmartContractAddressNameProvider.StringName); + // Using the address and key to get stub, Like this: + // TokenContractContainer.TokenContractStub stub = GetTester(TokenContractAddress, keyPair); + + // private readonly ECKeyPair KeyPair; + internal TimelockContractContainer.TimelockContractStub TimelockContractStub; + internal TokenContractContainer.TokenContractStub TokenContractStub; + + protected ECKeyPair DefaultKeyPair => Accounts[0].KeyPair; + protected Address DefaultAddress => Accounts[0].Address; + protected Address UserAddress => Accounts[1].Address; + protected IBlockTimeProvider BlockTimeProvider => + Application.ServiceProvider.GetRequiredService(); + + public TestBase() + { + // KeyPair = SampleAccount.Accounts.First().KeyPair; + TimelockContractStub = GetTimelockContractStub(DefaultKeyPair); + TokenContractStub = GetTokenContractStub(DefaultKeyPair); + } + + private TimelockContractContainer.TimelockContractStub GetTimelockContractStub(ECKeyPair senderKeyPair) + { + return GetTester(ContractAddress, senderKeyPair); + } + + private TokenContractContainer.TokenContractStub GetTokenContractStub(ECKeyPair senderKeyPair) { - return GetTester(ContractAddress, senderKeyPair); + return GetTester(TokenContractAddress, senderKeyPair); } } + } \ No newline at end of file From 4dc2d1c3fe94f27b99644adce70299297a5b0ab1 Mon Sep 17 00:00:00 2001 From: aelf-lu Date: Wed, 16 Aug 2023 22:24:29 +0800 Subject: [PATCH 10/20] fix: remove useless code --- .../test/TimelockContractTests.cs | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/basics/timelock-contract/test/TimelockContractTests.cs b/basics/timelock-contract/test/TimelockContractTests.cs index e596087..010ee2d 100644 --- a/basics/timelock-contract/test/TimelockContractTests.cs +++ b/basics/timelock-contract/test/TimelockContractTests.cs @@ -42,17 +42,19 @@ public async Task NewAdminTests() public async Task QueueTransactionTests() { await InitializeTests(); - TransferInput transferInput = new TransferInput + TransferFromInput transferFromInput = new TransferFromInput { + From = DefaultAddress, To = UserAddress, + Amount = 502, Symbol = "ELF", - Amount = 100 + Memo = "TEST" }; TransactionInput transactionInput = new TransactionInput { - Target = UserAddress, - Method = "Transfer", - Data = transferInput.ToByteString(), + Target = TokenContractAddress, + Method = "TransferFrom", + Data = transferFromInput.ToByteString(), ExecuteTime = _currentTime.AddSeconds(10) }; Hash txnHash = HashHelper.ComputeFrom(transactionInput); @@ -69,17 +71,19 @@ public async Task QueueTransactionTests() public async Task CancelTransactionTests() { await InitializeTests(); - TransferInput transferInput = new TransferInput + TransferFromInput transferFromInput = new TransferFromInput { + From = DefaultAddress, To = UserAddress, + Amount = 502, Symbol = "ELF", - Amount = 100 + Memo = "TEST" }; TransactionInput transactionInput = new TransactionInput { - Target = UserAddress, - Method = "Transfer", - Data = transferInput.ToByteString(), + Target = TokenContractAddress, + Method = "TransferFrom", + Data = transferFromInput.ToByteString(), ExecuteTime = _currentTime.AddSeconds(10) }; Hash txnHash = HashHelper.ComputeFrom(transactionInput); @@ -122,12 +126,6 @@ public async Task ExecuteTransactionTests() Owner = DefaultAddress, Symbol = "ELF" }); - TransferInput transferInput = new TransferInput - { - To = UserAddress, - Symbol = "ELF", - Amount = 502 - }; TransferFromInput transferFromInput = new TransferFromInput { From = DefaultAddress, From 96274a28473cdd99fd17bec446e42dea111fcc7d Mon Sep 17 00:00:00 2001 From: aelf-lu Date: Sun, 20 Aug 2023 18:10:06 +0800 Subject: [PATCH 11/20] fix: add configs --- .../CodeCoverage.runsettings | 17 ++ basics/timelock-contract/TimelockContract.sln | 22 +++ basics/timelock-contract/azure-pipelines.yml | 75 +++++++++ basics/timelock-contract/build.cake | 81 +++++++++ basics/timelock-contract/build.config | 3 + basics/timelock-contract/build.ps1 | 154 ++++++++++++++++++ basics/timelock-contract/build.sh | 75 +++++++++ basics/timelock-contract/codecov.sh | 12 ++ basics/timelock-contract/codecov.yml | 3 + 9 files changed, 442 insertions(+) create mode 100644 basics/timelock-contract/CodeCoverage.runsettings create mode 100644 basics/timelock-contract/TimelockContract.sln create mode 100644 basics/timelock-contract/azure-pipelines.yml create mode 100755 basics/timelock-contract/build.cake create mode 100644 basics/timelock-contract/build.config create mode 100644 basics/timelock-contract/build.ps1 create mode 100755 basics/timelock-contract/build.sh create mode 100644 basics/timelock-contract/codecov.sh create mode 100644 basics/timelock-contract/codecov.yml diff --git a/basics/timelock-contract/CodeCoverage.runsettings b/basics/timelock-contract/CodeCoverage.runsettings new file mode 100644 index 0000000..790dcc0 --- /dev/null +++ b/basics/timelock-contract/CodeCoverage.runsettings @@ -0,0 +1,17 @@ + + + + + + + + cobertura + [xunit.*]*,[*Tests]* + **/test/**/*.cs + Obsolete,GeneratedCodeAttribute + false + + + + + \ No newline at end of file diff --git a/basics/timelock-contract/TimelockContract.sln b/basics/timelock-contract/TimelockContract.sln new file mode 100644 index 0000000..987bf94 --- /dev/null +++ b/basics/timelock-contract/TimelockContract.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Contracts.TimelockContract", "src\AElf.Contracts.TimelockContract.csproj", "{6F60504C-6113-41EA-B230-531E00FDACF9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6F60504C-6113-41EA-B230-531E00FDACF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6F60504C-6113-41EA-B230-531E00FDACF9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6F60504C-6113-41EA-B230-531E00FDACF9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6F60504C-6113-41EA-B230-531E00FDACF9}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/basics/timelock-contract/azure-pipelines.yml b/basics/timelock-contract/azure-pipelines.yml new file mode 100644 index 0000000..b1995e6 --- /dev/null +++ b/basics/timelock-contract/azure-pipelines.yml @@ -0,0 +1,75 @@ +jobs: + +# All tasks on Windows.... +#- job: build_all_windows +# displayName: Build all tasks (Windows) +# pool: +# vmImage: windows-latest +# variables: +# CI_TEST: true +# steps: +# - task: UseDotNet@2 +# displayName: 'Install .NET Core SDK' +# inputs: +# version: 3.1.102 +# +# - task: BatchScript@1 +# displayName: 'Download AElf build tools' +# inputs: +# filename: 'scripts/download_binary.bat' +# - script: PowerShell.exe -file scripts/install.ps1 +# displayName: 'Install protobuf' +# - script: choco install unzip +# displayName: 'Install unzip' +# - powershell: Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) +# - script: PowerShell.exe -file build.ps1 -target=Run-Unit-Tests +# displayName: 'Build and Test' +# All tasks on Linux +- job: build_all_linux + displayName: Build all tasks (Linux) + timeoutInMinutes: 120 + pool: + vmImage: ubuntu-latest + steps: + - task: UseDotNet@2 + displayName: 'Install .NET Core SDK' + inputs: + version: 3.1.102 + - script: bash scripts/download_binary.sh + displayName: 'Download AElf build tools' + - script: bash scripts/install.sh + displayName: 'Install protobuf' + - script: bash build.sh --target=Test-with-Codecov + displayName: 'Build and Test' + - task: PublishTestResults@2 + condition: always() + inputs: + testRunner: VSTest + testResultsFiles: '**/*.trx' + - task: Palmmedia.reportgenerator.reportgenerator-build-release-task.reportgenerator@4 + displayName: ReportGenerator + inputs: + reports: '$(Build.SourcesDirectory)/test/*/TestResults/*/coverage.cobertura.xml' + targetdir: '$(Build.SourcesDirectory)/CodeCoverage' + reporttypes: 'Cobertura' + assemblyfilters: '-xunit*' + - script: bash build.sh --target=Upload-Coverage-Azure + displayName: 'Upload data to Codecov' +# All tasks on macOS +#- job: build_all_darwin +# displayName: Build all tasks (macOS) +# pool: +# vmImage: macos-latest +# variables: +# CI_TEST: true +# steps: +# - task: UseDotNet@2 +# displayName: 'Install .NET Core SDK' +# inputs: +# version: 3.1.102 +# - script: bash scripts/download_binary.sh +# displayName: 'Download AElf build tools' +# - script: bash scripts/install.sh +# displayName: 'Install protobuf' +# - script: bash build.sh -target=Run-Unit-Tests +# displayName: 'Build and Test' diff --git a/basics/timelock-contract/build.cake b/basics/timelock-contract/build.cake new file mode 100755 index 0000000..4d39c94 --- /dev/null +++ b/basics/timelock-contract/build.cake @@ -0,0 +1,81 @@ +#tool dotnet:?package=Codecov.Tool&version=1.13.0 +#addin nuget:?package=Cake.Codecov&version=1.0.1 + +var target = Argument("target", "Default"); +var configuration = Argument("configuration", "Debug"); +var rootPath = "./"; +var srcPath = rootPath + "src/"; +var contractPath = rootPath + "contract/"; +var testPath = rootPath + "test/"; +var solution = rootPath + "TimelockContract.sln"; + +Task("Clean") + .Description("clean up project cache") + .Does(() => +{ + CleanDirectories(srcPath + "**/bin"); + CleanDirectories(srcPath + "**/obj"); + CleanDirectories(contractPath + "**/bin"); + CleanDirectories(contractPath + "**/obj"); + CleanDirectories(testPath + "**/bin"); + CleanDirectories(testPath + "**/obj"); +}); + +Task("Restore") + .Description("restore project dependencies") + .Does(() => +{ + DotNetCoreRestore(solution, new DotNetCoreRestoreSettings + { + Verbosity = DotNetCoreVerbosity.Quiet, + Sources = new [] { "https://www.myget.org/F/aelf-project-dev/api/v3/index.json", "https://api.nuget.org/v3/index.json" } + }); +}); +Task("Build") + .Description("Compilation project") + .IsDependentOn("Clean") + .IsDependentOn("Restore") + .Does(() => +{ + var buildSetting = new DotNetCoreBuildSettings{ + NoRestore = true, + Configuration = configuration, + ArgumentCustomization = args => { + return args.Append("/clp:ErrorsOnly") + .Append("-v quiet");} + }; + + DotNetCoreBuild(solution, buildSetting); +}); + +Task("Test-with-Codecov") + .Description("operation tes") + .IsDependentOn("Build") + .Does(() => +{ + var testSetting = new DotNetCoreTestSettings{ + Configuration = configuration, + NoRestore = true, + NoBuild = true, + ArgumentCustomization = args => { + return args + .Append("--logger trx") + .Append("--settings CodeCoverage.runsettings") + .Append("--collect:\"XPlat Code Coverage\""); + } + }; + var testProjects = GetFiles("./test/*.Tests/*.csproj"); + var testProjectList = testProjects.OrderBy(p=>p.FullPath).ToList(); + foreach(var testProject in testProjectList) + { + DotNetCoreTest(testProject.FullPath, testSetting); + } +}); + +Task("Upload-Coverage-Azure") + .Does(() => +{ + Codecov("./CodeCoverage/Cobertura.xml",EnvironmentVariable("CODECOV_TOKEN")); +}); + +RunTarget(target); diff --git a/basics/timelock-contract/build.config b/basics/timelock-contract/build.config new file mode 100644 index 0000000..8e365f9 --- /dev/null +++ b/basics/timelock-contract/build.config @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +CAKE_VERSION=1.3.0 +DOTNET_VERSION=3.1.100 diff --git a/basics/timelock-contract/build.ps1 b/basics/timelock-contract/build.ps1 new file mode 100644 index 0000000..e48d09a --- /dev/null +++ b/basics/timelock-contract/build.ps1 @@ -0,0 +1,154 @@ +#!/usr/bin/env pwsh +$DotNetInstallerUri = 'https://dot.net/v1/dotnet-install.ps1'; +$DotNetUnixInstallerUri = 'https://dot.net/v1/dotnet-install.sh' +$DotNetChannel = 'LTS' +$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent + +[string] $CakeVersion = '' +[string] $DotNetVersion= '' +foreach($line in Get-Content (Join-Path $PSScriptRoot 'build.config')) +{ + if ($line -like 'CAKE_VERSION=*') { + $CakeVersion = $line.SubString(13) + } + elseif ($line -like 'DOTNET_VERSION=*') { + $DotNetVersion =$line.SubString(15) + } +} + + +if ([string]::IsNullOrEmpty($CakeVersion) -or [string]::IsNullOrEmpty($DotNetVersion)) { + 'Failed to parse Cake / .NET Core SDK Version' + exit 1 +} + +# Make sure tools folder exists +$ToolPath = Join-Path $PSScriptRoot "tools" +if (!(Test-Path $ToolPath)) { + Write-Verbose "Creating tools directory..." + New-Item -Path $ToolPath -Type Directory -Force | out-null +} + + +if ($PSVersionTable.PSEdition -ne 'Core') { + # Attempt to set highest encryption available for SecurityProtocol. + # PowerShell will not set this by default (until maybe .NET 4.6.x). This + # will typically produce a message for PowerShell v2 (just an info + # message though) + try { + # Set TLS 1.2 (3072), then TLS 1.1 (768), then TLS 1.0 (192), finally SSL 3.0 (48) + # Use integers because the enumeration values for TLS 1.2 and TLS 1.1 won't + # exist in .NET 4.0, even though they are addressable if .NET 4.5+ is + # installed (.NET 4.5 is an in-place upgrade). + [System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192 -bor 48 + } catch { + Write-Output 'Unable to set PowerShell to use TLS 1.2 and TLS 1.1 due to old .NET Framework installed. If you see underlying connection closed or trust errors, you may need to upgrade to .NET Framework 4.5+ and PowerShell v3' + } +} + +########################################################################### +# INSTALL .NET CORE CLI +########################################################################### + +$env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 +$env:DOTNET_CLI_TELEMETRY_OPTOUT=1 +$env:DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX=2 + + +Function Remove-PathVariable([string]$VariableToRemove) +{ + $SplitChar = ';' + if ($IsMacOS -or $IsLinux) { + $SplitChar = ':' + } + + $path = [Environment]::GetEnvironmentVariable("PATH", "User") + if ($path -ne $null) + { + $newItems = $path.Split($SplitChar, [StringSplitOptions]::RemoveEmptyEntries) | Where-Object { "$($_)" -inotlike $VariableToRemove } + [Environment]::SetEnvironmentVariable("PATH", [System.String]::Join($SplitChar, $newItems), "User") + } + + $path = [Environment]::GetEnvironmentVariable("PATH", "Process") + if ($path -ne $null) + { + $newItems = $path.Split($SplitChar, [StringSplitOptions]::RemoveEmptyEntries) | Where-Object { "$($_)" -inotlike $VariableToRemove } + [Environment]::SetEnvironmentVariable("PATH", [System.String]::Join($SplitChar, $newItems), "Process") + } +} + +# Get .NET Core CLI path if installed. +$FoundDotNetCliVersion = $null; +if (Get-Command dotnet -ErrorAction SilentlyContinue) { + $FoundDotNetCliVersion = dotnet --version; +} + +if($FoundDotNetCliVersion -ne $DotNetVersion) { + $InstallPath = Join-Path $PSScriptRoot ".dotnet" + if (!(Test-Path $InstallPath)) { + New-Item -Path $InstallPath -ItemType Directory -Force | Out-Null; + } + + if ($IsMacOS -or $IsLinux) { + $ScriptPath = Join-Path $InstallPath 'dotnet-install.sh' + (New-Object System.Net.WebClient).DownloadFile($DotNetUnixInstallerUri, $ScriptPath); + & bash $ScriptPath --version "$DotNetVersion" --install-dir "$InstallPath" --channel "$DotNetChannel" --no-path + + Remove-PathVariable "$InstallPath" + $env:PATH = "$($InstallPath):$env:PATH" + } + else { + $ScriptPath = Join-Path $InstallPath 'dotnet-install.ps1' + (New-Object System.Net.WebClient).DownloadFile($DotNetInstallerUri, $ScriptPath); + & $ScriptPath -Channel $DotNetChannel -Version $DotNetVersion -InstallDir $InstallPath; + + Remove-PathVariable "$InstallPath" + $env:PATH = "$InstallPath;$env:PATH" + } + $env:DOTNET_ROOT=$InstallPath +} + +########################################################################### +# INSTALL CAKE +########################################################################### + +# Make sure Cake has been installed. +[string] $CakeExePath = '' +[string] $CakeInstalledVersion = Get-Command dotnet-cake -ErrorAction SilentlyContinue | % {&$_.Source --version} + +if ($CakeInstalledVersion -eq $CakeVersion) { + # Cake found locally + $CakeExePath = (Get-Command dotnet-cake).Source +} +else { + $CakePath = [System.IO.Path]::Combine($ToolPath,'.store', 'cake.tool', $CakeVersion) # Old PowerShell versions Join-Path only supports one child path + + $CakeExePath = (Get-ChildItem -Path $ToolPath -Filter "dotnet-cake*" -File| ForEach-Object FullName | Select-Object -First 1) + + + if ((!(Test-Path -Path $CakePath -PathType Container)) -or (!(Test-Path $CakeExePath -PathType Leaf))) { + + if ((![string]::IsNullOrEmpty($CakeExePath)) -and (Test-Path $CakeExePath -PathType Leaf)) + { + & dotnet tool uninstall --tool-path $ToolPath Cake.Tool + } + + & dotnet tool install --tool-path $ToolPath --version $CakeVersion Cake.Tool + if ($LASTEXITCODE -ne 0) + { + 'Failed to install cake' + exit 1 + } + $CakeExePath = (Get-ChildItem -Path $ToolPath -Filter "dotnet-cake*" -File| ForEach-Object FullName | Select-Object -First 1) + } +} + +########################################################################### +# RUN BUILD SCRIPT +########################################################################### +& "$CakeExePath" ./build.cake --bootstrap +if ($LASTEXITCODE -eq 0) +{ + & "$CakeExePath" ./build.cake $args +} +exit $LASTEXITCODE \ No newline at end of file diff --git a/basics/timelock-contract/build.sh b/basics/timelock-contract/build.sh new file mode 100755 index 0000000..06bcee8 --- /dev/null +++ b/basics/timelock-contract/build.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +# Define varibles +SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +source $SCRIPT_DIR/build.config +TOOLS_DIR=$SCRIPT_DIR/tools +CAKE_EXE=$TOOLS_DIR/dotnet-cake +CAKE_PATH=$TOOLS_DIR/.store/cake.tool/$CAKE_VERSION + +if [ "$CAKE_VERSION" = "" ] || [ "$DOTNET_VERSION" = "" ]; then + echo "An error occured while parsing Cake / .NET Core SDK version." + exit 1 +fi + +# Make sure the tools folder exist. +if [ ! -d "$TOOLS_DIR" ]; then + mkdir "$TOOLS_DIR" +fi + +########################################################################### +# INSTALL .NET CORE CLI +########################################################################### + +export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 +export DOTNET_CLI_TELEMETRY_OPTOUT=1 +export DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER=0 +export DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX=2 + +DOTNET_INSTALLED_VERSION=$(dotnet --version 2>&1) + +if [ "$DOTNET_VERSION" != "$DOTNET_INSTALLED_VERSION" ]; then + echo "Installing .NET CLI..." + if [ ! -d "$SCRIPT_DIR/.dotnet" ]; then + mkdir "$SCRIPT_DIR/.dotnet" + fi + curl -Lsfo "$SCRIPT_DIR/.dotnet/dotnet-install.sh" https://dot.net/v1/dotnet-install.sh + bash "$SCRIPT_DIR/.dotnet/dotnet-install.sh" --version $DOTNET_VERSION --install-dir .dotnet --no-path + export PATH="$SCRIPT_DIR/.dotnet":$PATH + export DOTNET_ROOT="$SCRIPT_DIR/.dotnet" +fi + +########################################################################### +# INSTALL CAKE +########################################################################### + +CAKE_INSTALLED_VERSION=$(dotnet-cake --version 2>&1) + +if [ "$CAKE_VERSION" != "$CAKE_INSTALLED_VERSION" ]; then + if [ ! -f "$CAKE_EXE" ] || [ ! -d "$CAKE_PATH" ]; then + if [ -f "$CAKE_EXE" ]; then + dotnet tool uninstall --tool-path $TOOLS_DIR Cake.Tool + fi + + echo "Installing Cake $CAKE_VERSION..." + dotnet tool install --tool-path $TOOLS_DIR --version $CAKE_VERSION Cake.Tool + if [ $? -ne 0 ]; then + echo "An error occured while installing Cake." + exit 1 + fi + fi + + # Make sure that Cake has been installed. + if [ ! -f "$CAKE_EXE" ]; then + echo "Could not find Cake.exe at '$CAKE_EXE'." + exit 1 + fi +else + CAKE_EXE="dotnet-cake" +fi + +########################################################################### +# RUN BUILD SCRIPT +########################################################################### + +# Start Cake +(exec "$CAKE_EXE" build.cake --bootstrap) && (exec "$CAKE_EXE" build.cake "$@") \ No newline at end of file diff --git a/basics/timelock-contract/codecov.sh b/basics/timelock-contract/codecov.sh new file mode 100644 index 0000000..6b97c5b --- /dev/null +++ b/basics/timelock-contract/codecov.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +TOKEN=$1 +rm -r ./test/*.Tests/TestResults +rm -r CodeCoverage +for name in `ls ./test/*.Tests/*.csproj | awk '{print $NF}'`; +do + echo ${name} + dotnet test ${name} --logger trx --settings CodeCoverage.runsettings --collect:"XPlat Code Coverage" +done +reportgenerator /test/*/TestResults/*/coverage.cobertura.xml -reports:./test/*/TestResults/*/coverage.cobertura.xml -targetdir:./CodeCoverage -reporttypes:Cobertura -assemblyfilters:-xunit* +codecov -f ./CodeCoverage/Cobertura.xml -t ${TOKEN} \ No newline at end of file diff --git a/basics/timelock-contract/codecov.yml b/basics/timelock-contract/codecov.yml new file mode 100644 index 0000000..23be405 --- /dev/null +++ b/basics/timelock-contract/codecov.yml @@ -0,0 +1,3 @@ +codecov: + notify: + after_n_builds: 1 \ No newline at end of file From 629ed2ef3aafb8f35ded1cb0f5a3ac861747e63e Mon Sep 17 00:00:00 2001 From: aelf-lu Date: Mon, 21 Aug 2023 11:44:45 +0800 Subject: [PATCH 12/20] fix: fix CI configs --- ...ge.runsettings => CodeCoverage.runsettings | 0 ExampleContract.sln | 37 +++++++++++++++++++ ...azure-pipelines.yml => azure-pipelines.yml | 0 basics/timelock-contract/TimelockContract.sln | 22 ----------- .../build.cake => build.cake | 0 .../build.config => build.config | 0 .../timelock-contract/build.ps1 => build.ps1 | 0 basics/timelock-contract/build.sh => build.sh | 0 .../codecov.sh => codecov.sh | 0 .../codecov.yml => codecov.yml | 0 10 files changed, 37 insertions(+), 22 deletions(-) rename basics/timelock-contract/CodeCoverage.runsettings => CodeCoverage.runsettings (100%) create mode 100644 ExampleContract.sln rename basics/timelock-contract/azure-pipelines.yml => azure-pipelines.yml (100%) delete mode 100644 basics/timelock-contract/TimelockContract.sln rename basics/timelock-contract/build.cake => build.cake (100%) mode change 100755 => 100644 rename basics/timelock-contract/build.config => build.config (100%) rename basics/timelock-contract/build.ps1 => build.ps1 (100%) rename basics/timelock-contract/build.sh => build.sh (100%) mode change 100755 => 100644 rename basics/timelock-contract/codecov.sh => codecov.sh (100%) rename basics/timelock-contract/codecov.yml => codecov.yml (100%) diff --git a/basics/timelock-contract/CodeCoverage.runsettings b/CodeCoverage.runsettings similarity index 100% rename from basics/timelock-contract/CodeCoverage.runsettings rename to CodeCoverage.runsettings diff --git a/ExampleContract.sln b/ExampleContract.sln new file mode 100644 index 0000000..c7f0dab --- /dev/null +++ b/ExampleContract.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "basics", "basics", "{0B1211B7-ADC5-4934-9573-D19E429C7178}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "timelock-contract", "timelock-contract", "{2F26B697-A6FE-4A9F-A7DB-756E364B7BD8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Contracts.TimelockContract", "basics\timelock-contract\src\AElf.Contracts.TimelockContract.csproj", "{ECC2BE86-A75D-4526-B10B-A12915EEEEFC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Contracts.TimelockContract.Tests", "basics\timelock-contract\test\AElf.Contracts.TimelockContract.Tests.csproj", "{FFFB1BAF-52A1-4773-83BE-2BA1ED34D745}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {ECC2BE86-A75D-4526-B10B-A12915EEEEFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ECC2BE86-A75D-4526-B10B-A12915EEEEFC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ECC2BE86-A75D-4526-B10B-A12915EEEEFC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ECC2BE86-A75D-4526-B10B-A12915EEEEFC}.Release|Any CPU.Build.0 = Release|Any CPU + {FFFB1BAF-52A1-4773-83BE-2BA1ED34D745}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FFFB1BAF-52A1-4773-83BE-2BA1ED34D745}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FFFB1BAF-52A1-4773-83BE-2BA1ED34D745}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FFFB1BAF-52A1-4773-83BE-2BA1ED34D745}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {2F26B697-A6FE-4A9F-A7DB-756E364B7BD8} = {0B1211B7-ADC5-4934-9573-D19E429C7178} + {ECC2BE86-A75D-4526-B10B-A12915EEEEFC} = {2F26B697-A6FE-4A9F-A7DB-756E364B7BD8} + {FFFB1BAF-52A1-4773-83BE-2BA1ED34D745} = {2F26B697-A6FE-4A9F-A7DB-756E364B7BD8} + EndGlobalSection +EndGlobal diff --git a/basics/timelock-contract/azure-pipelines.yml b/azure-pipelines.yml similarity index 100% rename from basics/timelock-contract/azure-pipelines.yml rename to azure-pipelines.yml diff --git a/basics/timelock-contract/TimelockContract.sln b/basics/timelock-contract/TimelockContract.sln deleted file mode 100644 index 987bf94..0000000 --- a/basics/timelock-contract/TimelockContract.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31903.59 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Contracts.TimelockContract", "src\AElf.Contracts.TimelockContract.csproj", "{6F60504C-6113-41EA-B230-531E00FDACF9}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6F60504C-6113-41EA-B230-531E00FDACF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6F60504C-6113-41EA-B230-531E00FDACF9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6F60504C-6113-41EA-B230-531E00FDACF9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6F60504C-6113-41EA-B230-531E00FDACF9}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal diff --git a/basics/timelock-contract/build.cake b/build.cake old mode 100755 new mode 100644 similarity index 100% rename from basics/timelock-contract/build.cake rename to build.cake diff --git a/basics/timelock-contract/build.config b/build.config similarity index 100% rename from basics/timelock-contract/build.config rename to build.config diff --git a/basics/timelock-contract/build.ps1 b/build.ps1 similarity index 100% rename from basics/timelock-contract/build.ps1 rename to build.ps1 diff --git a/basics/timelock-contract/build.sh b/build.sh old mode 100755 new mode 100644 similarity index 100% rename from basics/timelock-contract/build.sh rename to build.sh diff --git a/basics/timelock-contract/codecov.sh b/codecov.sh similarity index 100% rename from basics/timelock-contract/codecov.sh rename to codecov.sh diff --git a/basics/timelock-contract/codecov.yml b/codecov.yml similarity index 100% rename from basics/timelock-contract/codecov.yml rename to codecov.yml From 49536e0ef4fc3378cad7b88e8a78f8e30a39e30c Mon Sep 17 00:00:00 2001 From: aelf-lu Date: Mon, 21 Aug 2023 12:04:38 +0800 Subject: [PATCH 13/20] fix: fix bugs --- build.cake | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/build.cake b/build.cake index 4d39c94..1a48dc8 100644 --- a/build.cake +++ b/build.cake @@ -5,9 +5,8 @@ var target = Argument("target", "Default"); var configuration = Argument("configuration", "Debug"); var rootPath = "./"; var srcPath = rootPath + "src/"; -var contractPath = rootPath + "contract/"; var testPath = rootPath + "test/"; -var solution = rootPath + "TimelockContract.sln"; +var solution = rootPath + "ExampleContract.sln"; Task("Clean") .Description("clean up project cache") @@ -15,8 +14,6 @@ Task("Clean") { CleanDirectories(srcPath + "**/bin"); CleanDirectories(srcPath + "**/obj"); - CleanDirectories(contractPath + "**/bin"); - CleanDirectories(contractPath + "**/obj"); CleanDirectories(testPath + "**/bin"); CleanDirectories(testPath + "**/obj"); }); From e33c7b3093da24bae6b88a7dbaaa5f4bb5e3438a Mon Sep 17 00:00:00 2001 From: aelf-lu Date: Mon, 28 Aug 2023 17:56:12 +0800 Subject: [PATCH 14/20] fix: resolve conflict --- .gitignore | 152 +++++------------- .idea/.idea.ExampleContract/.idea/.gitignore | 13 ++ .idea/.idea.ExampleContract/.idea/.name | 1 + .../.idea.ExampleContract/.idea/encodings.xml | 4 + .../.idea/indexLayout.xml | 8 + .idea/.idea.ExampleContract/.idea/vcs.xml | 6 + .../.idea.examples.dir/.idea/indexLayout.xml | 8 + .../.idea/projectSettingsUpdater.xml | 6 + .idea/.idea.examples.dir/.idea/vcs.xml | 6 + .../.idea/.idea.basics.dir/.idea/.gitignore | 13 ++ .../.idea.basics.dir/.idea/encodings.xml | 4 + .../.idea.basics.dir/.idea/indexLayout.xml | 8 + basics/.idea/.idea.basics.dir/.idea/vcs.xml | 6 + .../.idea.TimelockContract/.idea/.gitignore | 13 ++ .../.idea/.idea.TimelockContract/.idea/.name | 1 + .../.idea/encodings.xml | 4 + .../.idea/indexLayout.xml | 10 ++ .../.idea.TimelockContract/.idea/vcs.xml | 6 + .../.idea/.gitignore | 0 .../.idea/encodings.xml | 4 + .../.idea/indexLayout.xml | 8 + .../.idea/projectSettingsUpdater.xml | 6 + .../.idea.timelock-contract.dir/.idea/vcs.xml | 6 + 23 files changed, 183 insertions(+), 110 deletions(-) create mode 100644 .idea/.idea.ExampleContract/.idea/.gitignore create mode 100644 .idea/.idea.ExampleContract/.idea/.name create mode 100644 .idea/.idea.ExampleContract/.idea/encodings.xml create mode 100644 .idea/.idea.ExampleContract/.idea/indexLayout.xml create mode 100644 .idea/.idea.ExampleContract/.idea/vcs.xml create mode 100644 .idea/.idea.examples.dir/.idea/indexLayout.xml create mode 100644 .idea/.idea.examples.dir/.idea/projectSettingsUpdater.xml create mode 100644 .idea/.idea.examples.dir/.idea/vcs.xml create mode 100644 basics/.idea/.idea.basics.dir/.idea/.gitignore create mode 100644 basics/.idea/.idea.basics.dir/.idea/encodings.xml create mode 100644 basics/.idea/.idea.basics.dir/.idea/indexLayout.xml create mode 100644 basics/.idea/.idea.basics.dir/.idea/vcs.xml create mode 100644 basics/timelock-contract/.idea/.idea.TimelockContract/.idea/.gitignore create mode 100644 basics/timelock-contract/.idea/.idea.TimelockContract/.idea/.name create mode 100644 basics/timelock-contract/.idea/.idea.TimelockContract/.idea/encodings.xml create mode 100644 basics/timelock-contract/.idea/.idea.TimelockContract/.idea/indexLayout.xml create mode 100644 basics/timelock-contract/.idea/.idea.TimelockContract/.idea/vcs.xml create mode 100644 basics/timelock-contract/.idea/.idea.timelock-contract.dir/.idea/.gitignore create mode 100644 basics/timelock-contract/.idea/.idea.timelock-contract.dir/.idea/encodings.xml create mode 100644 basics/timelock-contract/.idea/.idea.timelock-contract.dir/.idea/indexLayout.xml create mode 100644 basics/timelock-contract/.idea/.idea.timelock-contract.dir/.idea/projectSettingsUpdater.xml create mode 100644 basics/timelock-contract/.idea/.idea.timelock-contract.dir/.idea/vcs.xml diff --git a/.gitignore b/.gitignore index 71ec0d1..58da635 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,5 @@ -# The following command works for downloading when using Git for Windows: -# curl -LOf http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore -# -# Download this file using PowerShell v3 under Windows with the following comand: -# Invoke-WebRequest https://gist.githubusercontent.com/kmorcinek/2710267/raw/ -OutFile .gitignore -# -# or wget: -# wget --no-check-certificate http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. # User-specific files *.suo @@ -13,22 +7,12 @@ *.sln.docstates # Build results + [Dd]ebug/ [Rr]elease/ x64/ [Bb]in/ [Oo]bj/ -# build folder is nowadays used for build scripts and should not be ignored -#build/ - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/packages/* -# except build/, which is used as an MSBuild target. -!**/packages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config # MSTest test Results [Tt]est[Rr]esult*/ @@ -36,6 +20,7 @@ x64/ *_i.c *_p.c +*_i.h *.ilk *.meta *.obj @@ -55,6 +40,8 @@ x64/ *.vssscc .builds *.pidb +*.log +*.svclog *.scc # Visual C++ cache files @@ -76,58 +63,41 @@ ipch/ # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# NCrunch -*.ncrunch* -.*crunch*.local.xml - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html +*.DotSettings.user # Click-Once directory publish/ # Publish Web Output *.Publish.xml +*.pubxml +*.azurePubxml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +packages/ +## TODO: If the tool you use requires repositories.config, also uncomment the next line +!packages/repositories.config # Windows Azure Build Output -csx +csx/ *.build.csdef # Windows Store app package directory AppPackages/ # Others +sql/ *.Cache ClientBin/ [Ss]tyle[Cc]op.* +![Ss]tyle[Cc]op.targets ~$* *~ *.dbmdl *.[Pp]ublish.xml -*.pfx -*.publishsettings -modulesbin/ -tempbin/ -# EPiServer Site file (VPP) -AppData/ +*.publishsettings # RIA/Silverlight projects Generated_Code/ @@ -139,33 +109,9 @@ Backup*/ UpgradeLog*.XML UpgradeLog*.htm -# vim -*.txt~ -*.swp -*.swo - -# Temp files when opening LibreOffice on ubuntu -.~lock.* - -# svn -.svn - -# CVS - Source Control -**/CVS/ - -# Remainings from resolving conflicts in Source Control -*.orig - # SQL Server files -**/App_Data/*.mdf -**/App_Data/*.ldf -**/App_Data/*.sdf - - -#LightSwitch generated files -GeneratedArtifacts/ -_Pvt_Extensions/ -ModelManifest.xml +App_Data/*.mdf +App_Data/*.ldf # ========================= # Windows detritus @@ -181,46 +127,32 @@ Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ -# OS generated files # -Icon? - # Mac desktop service store files .DS_Store -# SASS Compiler cache -.sass-cache - -# Visual Studio 2014 CTP -**/*.sln.ide +_NCrunch* -# Visual Studio temp something -.vs/ +# ================================= +# Common IntelliJ Platform excludes +# ================================= -# dotnet stuff -project.lock.json +# User specific +**/.idea/**/workspace.xml +**/.idea/**/tasks.xml +**/.idea/shelf/* +**/.idea/dictionaries +**/.idea/httpRequests/ -# VS 2015+ -*.vc.vc.opendb -*.vc.db +# Sensitive or high-churn files +**/.idea/**/dataSources/ +**/.idea/**/dataSources.ids +**/.idea/**/dataSources.xml +**/.idea/**/dataSources.local.xml +**/.idea/**/sqlDataSources.xml +**/.idea/**/dynamic.xml # Rider -.idea/ - -# Visual Studio Code -.vscode/ - -# Output folder used by Webpack or other FE stuff -**/node_modules/* -**/wwwroot/* - -# SpecFlow specific -*.feature.cs -*.feature.xlsx.* -*.Specs_*.html - -# UWP Projects -AppPackages/ - -##### -# End of core ignore list, below put you custom 'per project' settings (patterns or path) -##### \ No newline at end of file +# Rider auto-generates .iml files, and contentModel.xml +**/.idea/**/*.iml +**/.idea/**/contentModel.xml +**/.idea/**/modules.xml \ No newline at end of file diff --git a/.idea/.idea.ExampleContract/.idea/.gitignore b/.idea/.idea.ExampleContract/.idea/.gitignore new file mode 100644 index 0000000..087463d --- /dev/null +++ b/.idea/.idea.ExampleContract/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/.idea.ExampleContract.iml +/modules.xml +/contentModel.xml +/projectSettingsUpdater.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.ExampleContract/.idea/.name b/.idea/.idea.ExampleContract/.idea/.name new file mode 100644 index 0000000..c99d07a --- /dev/null +++ b/.idea/.idea.ExampleContract/.idea/.name @@ -0,0 +1 @@ +ExampleContract \ No newline at end of file diff --git a/.idea/.idea.ExampleContract/.idea/encodings.xml b/.idea/.idea.ExampleContract/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.ExampleContract/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.ExampleContract/.idea/indexLayout.xml b/.idea/.idea.ExampleContract/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.ExampleContract/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.ExampleContract/.idea/vcs.xml b/.idea/.idea.ExampleContract/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/.idea.ExampleContract/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/.idea.examples.dir/.idea/indexLayout.xml b/.idea/.idea.examples.dir/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.examples.dir/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.examples.dir/.idea/projectSettingsUpdater.xml b/.idea/.idea.examples.dir/.idea/projectSettingsUpdater.xml new file mode 100644 index 0000000..4bb9f4d --- /dev/null +++ b/.idea/.idea.examples.dir/.idea/projectSettingsUpdater.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/.idea.examples.dir/.idea/vcs.xml b/.idea/.idea.examples.dir/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/.idea.examples.dir/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/basics/.idea/.idea.basics.dir/.idea/.gitignore b/basics/.idea/.idea.basics.dir/.idea/.gitignore new file mode 100644 index 0000000..a896001 --- /dev/null +++ b/basics/.idea/.idea.basics.dir/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/modules.xml +/contentModel.xml +/projectSettingsUpdater.xml +/.idea.basics.iml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/basics/.idea/.idea.basics.dir/.idea/encodings.xml b/basics/.idea/.idea.basics.dir/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/basics/.idea/.idea.basics.dir/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/basics/.idea/.idea.basics.dir/.idea/indexLayout.xml b/basics/.idea/.idea.basics.dir/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/basics/.idea/.idea.basics.dir/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/basics/.idea/.idea.basics.dir/.idea/vcs.xml b/basics/.idea/.idea.basics.dir/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/basics/.idea/.idea.basics.dir/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/basics/timelock-contract/.idea/.idea.TimelockContract/.idea/.gitignore b/basics/timelock-contract/.idea/.idea.TimelockContract/.idea/.gitignore new file mode 100644 index 0000000..affda2e --- /dev/null +++ b/basics/timelock-contract/.idea/.idea.TimelockContract/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/modules.xml +/contentModel.xml +/projectSettingsUpdater.xml +/.idea.TimelockContract.iml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/basics/timelock-contract/.idea/.idea.TimelockContract/.idea/.name b/basics/timelock-contract/.idea/.idea.TimelockContract/.idea/.name new file mode 100644 index 0000000..de88a6e --- /dev/null +++ b/basics/timelock-contract/.idea/.idea.TimelockContract/.idea/.name @@ -0,0 +1 @@ +TimelockContract \ No newline at end of file diff --git a/basics/timelock-contract/.idea/.idea.TimelockContract/.idea/encodings.xml b/basics/timelock-contract/.idea/.idea.TimelockContract/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/basics/timelock-contract/.idea/.idea.TimelockContract/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/basics/timelock-contract/.idea/.idea.TimelockContract/.idea/indexLayout.xml b/basics/timelock-contract/.idea/.idea.TimelockContract/.idea/indexLayout.xml new file mode 100644 index 0000000..0fd1d51 --- /dev/null +++ b/basics/timelock-contract/.idea/.idea.TimelockContract/.idea/indexLayout.xml @@ -0,0 +1,10 @@ + + + + + ../../../examples + + + + + \ No newline at end of file diff --git a/basics/timelock-contract/.idea/.idea.TimelockContract/.idea/vcs.xml b/basics/timelock-contract/.idea/.idea.TimelockContract/.idea/vcs.xml new file mode 100644 index 0000000..b2bdec2 --- /dev/null +++ b/basics/timelock-contract/.idea/.idea.TimelockContract/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/basics/timelock-contract/.idea/.idea.timelock-contract.dir/.idea/.gitignore b/basics/timelock-contract/.idea/.idea.timelock-contract.dir/.idea/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/basics/timelock-contract/.idea/.idea.timelock-contract.dir/.idea/encodings.xml b/basics/timelock-contract/.idea/.idea.timelock-contract.dir/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/basics/timelock-contract/.idea/.idea.timelock-contract.dir/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/basics/timelock-contract/.idea/.idea.timelock-contract.dir/.idea/indexLayout.xml b/basics/timelock-contract/.idea/.idea.timelock-contract.dir/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/basics/timelock-contract/.idea/.idea.timelock-contract.dir/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/basics/timelock-contract/.idea/.idea.timelock-contract.dir/.idea/projectSettingsUpdater.xml b/basics/timelock-contract/.idea/.idea.timelock-contract.dir/.idea/projectSettingsUpdater.xml new file mode 100644 index 0000000..4bb9f4d --- /dev/null +++ b/basics/timelock-contract/.idea/.idea.timelock-contract.dir/.idea/projectSettingsUpdater.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/basics/timelock-contract/.idea/.idea.timelock-contract.dir/.idea/vcs.xml b/basics/timelock-contract/.idea/.idea.timelock-contract.dir/.idea/vcs.xml new file mode 100644 index 0000000..b2bdec2 --- /dev/null +++ b/basics/timelock-contract/.idea/.idea.timelock-contract.dir/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From fcdef39c3739e69fd3294d3f4ca32a1f379f4d34 Mon Sep 17 00:00:00 2001 From: aelf-lu Date: Wed, 30 Aug 2023 18:45:05 +0800 Subject: [PATCH 15/20] fix: fix CI configs --- azure-pipelines.yml | 12 +++++------- .../AElf.Contracts.TimelockContract.Tests.csproj | 4 ++++ build.cake | 10 +++++----- build.config | 4 ++-- codecov.sh | 6 +++--- 5 files changed, 19 insertions(+), 17 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b1995e6..8c05883 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -30,15 +30,13 @@ jobs: timeoutInMinutes: 120 pool: vmImage: ubuntu-latest + variables: + CI_TEST: true steps: - task: UseDotNet@2 displayName: 'Install .NET Core SDK' inputs: - version: 3.1.102 - - script: bash scripts/download_binary.sh - displayName: 'Download AElf build tools' - - script: bash scripts/install.sh - displayName: 'Install protobuf' + version: 7.0.x - script: bash build.sh --target=Test-with-Codecov displayName: 'Build and Test' - task: PublishTestResults@2 @@ -46,10 +44,10 @@ jobs: inputs: testRunner: VSTest testResultsFiles: '**/*.trx' - - task: Palmmedia.reportgenerator.reportgenerator-build-release-task.reportgenerator@4 + - task: reportgenerator@5 displayName: ReportGenerator inputs: - reports: '$(Build.SourcesDirectory)/test/*/TestResults/*/coverage.cobertura.xml' + reports: '$(Build.SourcesDirectory)/basics/timelock-contract/test/TestResults/*/coverage.cobertura.xml' targetdir: '$(Build.SourcesDirectory)/CodeCoverage' reporttypes: 'Cobertura' assemblyfilters: '-xunit*' diff --git a/basics/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj b/basics/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj index 125ae03..4ce450e 100644 --- a/basics/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj +++ b/basics/timelock-contract/test/AElf.Contracts.TimelockContract.Tests.csproj @@ -27,6 +27,10 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/build.cake b/build.cake index 1a48dc8..b31682a 100644 --- a/build.cake +++ b/build.cake @@ -1,11 +1,11 @@ -#tool dotnet:?package=Codecov.Tool&version=1.13.0 -#addin nuget:?package=Cake.Codecov&version=1.0.1 +#tool nuget:?package=Codecov +#addin nuget:?package=Cake.Codecov&version=0.8.0 var target = Argument("target", "Default"); var configuration = Argument("configuration", "Debug"); var rootPath = "./"; -var srcPath = rootPath + "src/"; -var testPath = rootPath + "test/"; +var srcPath = rootPath + "basics/timelock-contract/src/"; +var testPath = rootPath + "basics/timelock-contract/test/"; var solution = rootPath + "ExampleContract.sln"; Task("Clean") @@ -61,7 +61,7 @@ Task("Test-with-Codecov") .Append("--collect:\"XPlat Code Coverage\""); } }; - var testProjects = GetFiles("./test/*.Tests/*.csproj"); + var testProjects = GetFiles("./basics/timelock-contract/test/*.csproj"); var testProjectList = testProjects.OrderBy(p=>p.FullPath).ToList(); foreach(var testProject in testProjectList) { diff --git a/build.config b/build.config index 8e365f9..6b20136 100644 --- a/build.config +++ b/build.config @@ -1,3 +1,3 @@ #!/usr/bin/env bash -CAKE_VERSION=1.3.0 -DOTNET_VERSION=3.1.100 +CAKE_VERSION=0.37.0 +DOTNET_VERSION=7.0.100 \ No newline at end of file diff --git a/codecov.sh b/codecov.sh index 6b97c5b..9199f8f 100644 --- a/codecov.sh +++ b/codecov.sh @@ -1,12 +1,12 @@ #!/usr/bin/env bash TOKEN=$1 -rm -r ./test/*.Tests/TestResults +rm -r ./basics/timelock-contract/test/TestResults rm -r CodeCoverage -for name in `ls ./test/*.Tests/*.csproj | awk '{print $NF}'`; +for name in `ls ./basics/timelock-contract/test/*.csproj | awk '{print $NF}'`; do echo ${name} dotnet test ${name} --logger trx --settings CodeCoverage.runsettings --collect:"XPlat Code Coverage" done -reportgenerator /test/*/TestResults/*/coverage.cobertura.xml -reports:./test/*/TestResults/*/coverage.cobertura.xml -targetdir:./CodeCoverage -reporttypes:Cobertura -assemblyfilters:-xunit* +reportgenerator /basics/timelock-contract/test/TestResults/*/coverage.cobertura.xml -reports:./basics/timelock-contract/test/TestResults/*/coverage.cobertura.xml -targetdir:./CodeCoverage -reporttypes:Cobertura -assemblyfilters:-xunit* codecov -f ./CodeCoverage/Cobertura.xml -t ${TOKEN} \ No newline at end of file From 6aeb942118c022a183438578f2c6c786cdc07f82 Mon Sep 17 00:00:00 2001 From: aelf-lu Date: Fri, 8 Sep 2023 15:18:16 +0800 Subject: [PATCH 16/20] fix: fix CI configs --- azure-pipelines.yml | 2 +- build.cake | 6 +++--- codecov.sh | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8c05883..2a830d1 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -47,7 +47,7 @@ jobs: - task: reportgenerator@5 displayName: ReportGenerator inputs: - reports: '$(Build.SourcesDirectory)/basics/timelock-contract/test/TestResults/*/coverage.cobertura.xml' + reports: '$(Build.SourcesDirectory)/**/test/TestResults/*/coverage.cobertura.xml' targetdir: '$(Build.SourcesDirectory)/CodeCoverage' reporttypes: 'Cobertura' assemblyfilters: '-xunit*' diff --git a/build.cake b/build.cake index b31682a..cfc3109 100644 --- a/build.cake +++ b/build.cake @@ -4,8 +4,8 @@ var target = Argument("target", "Default"); var configuration = Argument("configuration", "Debug"); var rootPath = "./"; -var srcPath = rootPath + "basics/timelock-contract/src/"; -var testPath = rootPath + "basics/timelock-contract/test/"; +var srcPath = rootPath + "**/src/"; +var testPath = rootPath + "**/test/"; var solution = rootPath + "ExampleContract.sln"; Task("Clean") @@ -61,7 +61,7 @@ Task("Test-with-Codecov") .Append("--collect:\"XPlat Code Coverage\""); } }; - var testProjects = GetFiles("./basics/timelock-contract/test/*.csproj"); + var testProjects = GetFiles("./**/test/*.csproj"); var testProjectList = testProjects.OrderBy(p=>p.FullPath).ToList(); foreach(var testProject in testProjectList) { diff --git a/codecov.sh b/codecov.sh index 9199f8f..1d6b417 100644 --- a/codecov.sh +++ b/codecov.sh @@ -1,12 +1,12 @@ #!/usr/bin/env bash TOKEN=$1 -rm -r ./basics/timelock-contract/test/TestResults +rm -r ./**/test/TestResults rm -r CodeCoverage -for name in `ls ./basics/timelock-contract/test/*.csproj | awk '{print $NF}'`; +for name in `ls ./**/test/*.csproj | awk '{print $NF}'`; do echo ${name} dotnet test ${name} --logger trx --settings CodeCoverage.runsettings --collect:"XPlat Code Coverage" done -reportgenerator /basics/timelock-contract/test/TestResults/*/coverage.cobertura.xml -reports:./basics/timelock-contract/test/TestResults/*/coverage.cobertura.xml -targetdir:./CodeCoverage -reporttypes:Cobertura -assemblyfilters:-xunit* +reportgenerator /**/test/TestResults/*/coverage.cobertura.xml -reports:./**/test/TestResults/*/coverage.cobertura.xml -targetdir:./CodeCoverage -reporttypes:Cobertura -assemblyfilters:-xunit* codecov -f ./CodeCoverage/Cobertura.xml -t ${TOKEN} \ No newline at end of file From df80a6191ff042c965ca4fa0cec8ab4f5057dc28 Mon Sep 17 00:00:00 2001 From: aelf-lu Date: Wed, 20 Sep 2023 09:40:59 +0800 Subject: [PATCH 17/20] fix: fix code - add setDelay - modfiy unit test --- .../Protobuf/contract/timelock_contract.proto | 35 ++++++------ .../timelock-contract/src/TimelockContract.cs | 23 ++++++-- .../src/TimelockContractConstants.cs | 4 +- .../Protobuf/stub/timelock_contract.proto | 35 ++++++------ .../test/TimelockContractTests.cs | 57 +++++++++++++++---- 5 files changed, 104 insertions(+), 50 deletions(-) diff --git a/basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto b/basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto index cfcc59d..79f40e1 100644 --- a/basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto +++ b/basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto @@ -14,6 +14,8 @@ service TimelockContract { // Actions rpc Initialize (InitializeInput) returns (google.protobuf.Empty) { } + rpc SetDelay (SetDelayInput) returns (google.protobuf.Empty) { + } rpc ChangeAdmin (ChangeAdminInput) returns (google.protobuf.Empty) { } rpc QueueTransaction (TransactionInput) returns (aelf.Hash) { @@ -40,6 +42,10 @@ message InitializeInput { uint64 delay = 1; } +message SetDelayInput { + uint64 delay = 1; +} + message ChangeAdminInput { aelf.Address admin = 1; } @@ -60,32 +66,29 @@ message NewAdmin { message NewDelay { option (aelf.is_event) = true; - uint64 new_delay = 1; + uint64 delay = 1; } message QueueTransaction { option (aelf.is_event) = true; - aelf.Hash txn_hash = 1; - aelf.Address target = 2; - string method = 3; - bytes data = 4; // data equivalent to ConvertToByteString(IMessage) - google.protobuf.Timestamp execute_time = 5; + aelf.Address target = 1; + string method = 2; + bytes data = 3; // data equivalent to ConvertToByteString(IMessage) + google.protobuf.Timestamp execute_time = 4; } message ExecuteTransaction { option (aelf.is_event) = true; - aelf.Hash txn_hash = 1; - aelf.Address target = 2; - string method = 3; - bytes data = 4; - google.protobuf.Timestamp execute_time = 5; + aelf.Address target = 1; + string method = 2; + bytes data = 3; + google.protobuf.Timestamp execute_time = 4; } message CancelTransaction { option (aelf.is_event) = true; - aelf.Hash txn_hash = 1; - aelf.Address target = 2; - string method = 3; - bytes data = 4; - google.protobuf.Timestamp execute_time = 5; + aelf.Address target = 1; + string method = 2; + bytes data = 3; + google.protobuf.Timestamp execute_time = 4; } \ No newline at end of file diff --git a/basics/timelock-contract/src/TimelockContract.cs b/basics/timelock-contract/src/TimelockContract.cs index 71980d9..1559c4d 100644 --- a/basics/timelock-contract/src/TimelockContract.cs +++ b/basics/timelock-contract/src/TimelockContract.cs @@ -9,6 +9,8 @@ public partial class TimelockContract : TimelockContractContainer.TimelockContra { public override Empty Initialize(InitializeInput input) { + Assert(input.Delay <= TimelockContractConstants.MAX_DELAY, "Delay must not exceed maximum delay"); + Assert(input.Delay >= TimelockContractConstants.MIN_DELAY, "Delay must exceed minimum delay"); if (State.Initialized.Value) { return new Empty(); @@ -21,7 +23,7 @@ public override Empty Initialize(InitializeInput input) public override Empty ChangeAdmin(ChangeAdminInput input) { - Assert(Context.Sender == State.Admin.Value, "No permission"); + Assert(Context.Sender == Context.Self, "No permission"); Assert(input.Admin != null, "NewAdmin must not be null"); State.Admin.Value = input.Admin; Context.Fire(new NewAdmin @@ -31,6 +33,19 @@ public override Empty ChangeAdmin(ChangeAdminInput input) return new Empty(); } + public override Empty SetDelay(SetDelayInput input) + { + Assert(Context.Sender == Context.Self, "No permission"); + Assert(input.Delay <= TimelockContractConstants.MAX_DELAY, "Delay must not exceed maximum delay"); + Assert(input.Delay >= TimelockContractConstants.MIN_DELAY, "Delay must exceed minimum delay"); + State.Delay.Value = input.Delay; + Context.Fire(new NewDelay + { + Delay = State.Delay.Value + }); + return new Empty(); + } + public override Hash QueueTransaction(TransactionInput input) { Assert(Context.Sender == State.Admin.Value, "No permission"); @@ -39,7 +54,6 @@ public override Hash QueueTransaction(TransactionInput input) State.TransactionQueue[txnHash] = true; Context.Fire(new QueueTransaction { - TxnHash = txnHash, Target = input.Target, Method = input.Method, Data = input.Data, @@ -52,10 +66,9 @@ public override Empty CancelTransaction(TransactionInput input) { Assert(Context.Sender == State.Admin.Value, "No permission"); Hash txnHash = HashHelper.ComputeFrom(input); - State.TransactionQueue.Remove(txnHash); + State.TransactionQueue[txnHash] = false; Context.Fire(new CancelTransaction { - TxnHash = txnHash, Target = input.Target, Method = input.Method, Data = input.Data, @@ -76,13 +89,11 @@ public override Empty ExecuteTransaction(TransactionInput input) Context.SendInline(input.Target, input.Method, input.Data); Context.Fire(new ExecuteTransaction { - TxnHash = txnHash, Target = input.Target, Method = input.Method, Data = input.Data, ExecuteTime = input.ExecuteTime }); - State.TransactionQueue.Remove(txnHash); return new Empty(); } } diff --git a/basics/timelock-contract/src/TimelockContractConstants.cs b/basics/timelock-contract/src/TimelockContractConstants.cs index 2736621..3b155b4 100644 --- a/basics/timelock-contract/src/TimelockContractConstants.cs +++ b/basics/timelock-contract/src/TimelockContractConstants.cs @@ -5,7 +5,7 @@ public static class TimelockContractConstants public const string SYMBOL = "ELF"; // unit is second public const long MIN_DELAY = 1 * 24 * 60 * 60; - public const long MAX_DELAY = 3 * 24 * 60 * 60; - public const long GRACE_PERIOD = 2 * 24 * 60 * 60; + public const long MAX_DELAY = 7 * 24 * 60 * 60; + public const long GRACE_PERIOD = 3 * 24 * 60 * 60; } } \ No newline at end of file diff --git a/basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto b/basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto index cfcc59d..79f40e1 100644 --- a/basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto +++ b/basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto @@ -14,6 +14,8 @@ service TimelockContract { // Actions rpc Initialize (InitializeInput) returns (google.protobuf.Empty) { } + rpc SetDelay (SetDelayInput) returns (google.protobuf.Empty) { + } rpc ChangeAdmin (ChangeAdminInput) returns (google.protobuf.Empty) { } rpc QueueTransaction (TransactionInput) returns (aelf.Hash) { @@ -40,6 +42,10 @@ message InitializeInput { uint64 delay = 1; } +message SetDelayInput { + uint64 delay = 1; +} + message ChangeAdminInput { aelf.Address admin = 1; } @@ -60,32 +66,29 @@ message NewAdmin { message NewDelay { option (aelf.is_event) = true; - uint64 new_delay = 1; + uint64 delay = 1; } message QueueTransaction { option (aelf.is_event) = true; - aelf.Hash txn_hash = 1; - aelf.Address target = 2; - string method = 3; - bytes data = 4; // data equivalent to ConvertToByteString(IMessage) - google.protobuf.Timestamp execute_time = 5; + aelf.Address target = 1; + string method = 2; + bytes data = 3; // data equivalent to ConvertToByteString(IMessage) + google.protobuf.Timestamp execute_time = 4; } message ExecuteTransaction { option (aelf.is_event) = true; - aelf.Hash txn_hash = 1; - aelf.Address target = 2; - string method = 3; - bytes data = 4; - google.protobuf.Timestamp execute_time = 5; + aelf.Address target = 1; + string method = 2; + bytes data = 3; + google.protobuf.Timestamp execute_time = 4; } message CancelTransaction { option (aelf.is_event) = true; - aelf.Hash txn_hash = 1; - aelf.Address target = 2; - string method = 3; - bytes data = 4; - google.protobuf.Timestamp execute_time = 5; + aelf.Address target = 1; + string method = 2; + bytes data = 3; + google.protobuf.Timestamp execute_time = 4; } \ No newline at end of file diff --git a/basics/timelock-contract/test/TimelockContractTests.cs b/basics/timelock-contract/test/TimelockContractTests.cs index 010ee2d..37243a7 100644 --- a/basics/timelock-contract/test/TimelockContractTests.cs +++ b/basics/timelock-contract/test/TimelockContractTests.cs @@ -20,22 +20,59 @@ public async Task InitializeTests() { InitializeInput input = new InitializeInput { - Delay = 5 + Delay = 2 * 24 * 60 * 60 }; await TimelockContractStub.Initialize.SendAsync(input); } [Fact] - public async Task NewAdminTests() + public async Task ChangeAdminTests() { await InitializeTests(); - ChangeAdminInput changeAdminInput = new ChangeAdminInput + var address = await TimelockContractStub.GetAdmin.CallAsync(new Empty()); + address.ShouldBe(DefaultAddress); + ChangeAdminInput changeAdminInput = new ChangeAdminInput() { Admin = UserAddress }; - await TimelockContractStub.ChangeAdmin.SendAsync(changeAdminInput); - var result = await TimelockContractStub.GetAdmin.CallAsync(new Empty()); - result.ShouldBe(UserAddress); + TransactionInput transactionInput = new TransactionInput + { + Target = ContractAddress, + Method = "ChangeAdmin", + Data = changeAdminInput.ToByteString(), + ExecuteTime = _currentTime.AddSeconds(3 * 24 * 60 * 60) + }; + var txnHash = await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); + BlockTimeProvider.SetBlockTime(_currentTime.AddSeconds(3 * 24 * 60 * 60)); + await TimelockContractStub.ExecuteTransaction.SendAsync(transactionInput); + + var addressAfter = await TimelockContractStub.GetAdmin.CallAsync(new Empty()); + addressAfter.ShouldBe(UserAddress); + } + + [Fact] + public async Task SetDelayTests() + { + await InitializeTests(); + UInt64Value delay = await TimelockContractStub.GetDelay.CallAsync(new Empty()); + delay.Value.ShouldBe((uint)(2 * 24 * 60 * 60)); + SetDelayInput setDelayInput = new SetDelayInput + { + Delay = 4 * 24 * 60 * 60 + }; + TransactionInput transactionInput = new TransactionInput + { + Target = ContractAddress, + Method = "SetDelay", + Data = setDelayInput.ToByteString(), + ExecuteTime = _currentTime.AddSeconds(3 * 24 * 60 * 60) + }; + var txnHash = await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); + BlockTimeProvider.SetBlockTime(_currentTime.AddSeconds(3 * 24 * 60 * 60)); + await TimelockContractStub.ExecuteTransaction.SendAsync(transactionInput); + + UInt64Value delayAfter = await TimelockContractStub.GetDelay.CallAsync(new Empty()); + delayAfter.Value.ShouldBe((uint)(4 * 24 * 60 * 60)); } [Fact] @@ -55,7 +92,7 @@ public async Task QueueTransactionTests() Target = TokenContractAddress, Method = "TransferFrom", Data = transferFromInput.ToByteString(), - ExecuteTime = _currentTime.AddSeconds(10) + ExecuteTime = _currentTime.AddSeconds(3 * 24 * 60 * 60) }; Hash txnHash = HashHelper.ComputeFrom(transactionInput); await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); @@ -84,7 +121,7 @@ public async Task CancelTransactionTests() Target = TokenContractAddress, Method = "TransferFrom", Data = transferFromInput.ToByteString(), - ExecuteTime = _currentTime.AddSeconds(10) + ExecuteTime = _currentTime.AddSeconds(3 * 24 * 60 * 60) }; Hash txnHash = HashHelper.ComputeFrom(transactionInput); await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); @@ -139,10 +176,10 @@ public async Task ExecuteTransactionTests() Target = TokenContractAddress, Method = "TransferFrom", Data = transferFromInput.ToByteString(), - ExecuteTime = _currentTime.AddSeconds(10) + ExecuteTime = _currentTime.AddSeconds(3 * 24 * 60 * 60) }; var txnHash = await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); - BlockTimeProvider.SetBlockTime(_currentTime.AddSeconds(10)); + BlockTimeProvider.SetBlockTime(_currentTime.AddSeconds(3 * 24 * 60 * 60)); await TimelockContractStub.ExecuteTransaction.SendAsync(transactionInput); var balance2 = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput From f39a3a86601cd5d714e7692dfe4b9c869a566953 Mon Sep 17 00:00:00 2001 From: aelf-lu Date: Thu, 21 Sep 2023 18:43:29 +0800 Subject: [PATCH 18/20] fix: fix initialize methods --- .../src/Protobuf/reference/acs0.proto | 287 ++++++++++++++++++ .../timelock-contract/src/TimelockContract.cs | 8 +- .../src/TimelockContractReferenceState.cs | 9 + .../test/Protobuf/stub/acs0.proto | 287 ++++++++++++++++++ .../test/TimelockContractTests.cs | 8 +- basics/timelock-contract/test/_Setup.cs | 33 +- 6 files changed, 621 insertions(+), 11 deletions(-) create mode 100644 basics/timelock-contract/src/Protobuf/reference/acs0.proto create mode 100644 basics/timelock-contract/src/TimelockContractReferenceState.cs create mode 100644 basics/timelock-contract/test/Protobuf/stub/acs0.proto diff --git a/basics/timelock-contract/src/Protobuf/reference/acs0.proto b/basics/timelock-contract/src/Protobuf/reference/acs0.proto new file mode 100644 index 0000000..d55d01a --- /dev/null +++ b/basics/timelock-contract/src/Protobuf/reference/acs0.proto @@ -0,0 +1,287 @@ +/** + * AElf Standards ACS0(Contract Deployment Standard) + * + * Used to manage the deployment and update of contracts. + */ +syntax = "proto3"; + +package acs0; +option csharp_namespace = "AElf.Standards.ACS0"; + +import public "aelf/core.proto"; +import "aelf/options.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/wrappers.proto"; + +service ACS0 { + // Deploy a system smart contract on chain and return the address of the system contract deployed. + rpc DeploySystemSmartContract (SystemContractDeploymentInput) returns (aelf.Address) { + } + + // Deploy a smart contract on chain and return the address of the contract deployed. + rpc DeploySmartContract (ContractDeploymentInput) returns (aelf.Address) { + } + + // Update a smart contract on chain. + rpc UpdateSmartContract (ContractUpdateInput) returns (aelf.Address) { + } + + // Create a proposal to deploy a new contract and returns the id of the proposed contract. + rpc ProposeNewContract (ContractDeploymentInput) returns (aelf.Hash) { + } + + // Create a proposal to check the code of a contract and return the id of the proposed contract. + rpc ProposeContractCodeCheck (ContractCodeCheckInput) returns (aelf.Hash) { + } + + // Create a proposal to update the specified contract + // and return the id of the proposed contract. + rpc ProposeUpdateContract (ContractUpdateInput) returns (aelf.Hash) { + } + + // Release the contract proposal which has been approved. + rpc ReleaseApprovedContract (ReleaseContractInput) returns (google.protobuf.Empty) { + } + + // Release the proposal which has passed the code check. + rpc ReleaseCodeCheckedContract (ReleaseContractInput) returns (google.protobuf.Empty) { + } + + // Deploy a user smart contract on chain and return the hash of the contract code. + rpc DeployUserSmartContract (ContractDeploymentInput) returns (DeployUserSmartContractOutput) { + } + + // Update a user smart contract on chain. + rpc UpdateUserSmartContract (ContractUpdateInput) returns (google.protobuf.Empty) { + } + + // Release the proposal which has passed the code check. + rpc ReleaseApprovedUserSmartContract(ReleaseContractInput) returns (google.protobuf.Empty) { + } + + // Perform user contract deployment. + rpc PerformDeployUserSmartContract(ContractDeploymentInput) returns (aelf.Address) { + } + + // Perform user contract update. + rpc PerformUpdateUserSmartContract(ContractUpdateInput) returns (google.protobuf.Empty) { + } + + // Set author of the specified contract. + rpc SetContractAuthor (SetContractAuthorInput) returns (google.protobuf.Empty) { + } + + // Validate whether the input system contract exists. + rpc ValidateSystemContractAddress(ValidateSystemContractAddressInput) returns (google.protobuf.Empty){ + } + + // Set authority of contract deployment. + rpc SetContractProposerRequiredState (google.protobuf.BoolValue) returns (google.protobuf.Empty) { + } + + // Get the current serial number of genesis contract + // (corresponds to the serial number that will be given to the next deployed contract). + rpc CurrentContractSerialNumber (google.protobuf.Empty) returns (google.protobuf.Int64Value) { + option (aelf.is_view) = true; + } + + // Get detailed information about the specified contract. + rpc GetContractInfo (aelf.Address) returns (ContractInfo) { + option (aelf.is_view) = true; + } + + // Get author of the specified contract. + rpc GetContractAuthor (aelf.Address) returns (aelf.Address) { + option (aelf.is_view) = true; + } + + // Get the code hash of the contract about the specified address. + rpc GetContractHash (aelf.Address) returns (aelf.Hash) { + option (aelf.is_view) = true; + } + + // Get the address of a system contract by its name. + rpc GetContractAddressByName (aelf.Hash) returns (aelf.Address) { + option (aelf.is_view) = true; + } + + // Get the registration of a smart contract by its address. + rpc GetSmartContractRegistrationByAddress (aelf.Address) returns (aelf.SmartContractRegistration) { + option (aelf.is_view) = true; + } + + // Get the registration of a smart contract by code hash. + rpc GetSmartContractRegistrationByCodeHash (aelf.Hash) returns (aelf.SmartContractRegistration) { + option (aelf.is_view) = true; + } + + rpc GetContractCodeHashListByDeployingBlockHeight (google.protobuf.Int64Value) returns (ContractCodeHashList) { + option (aelf.is_view) = true; + } +} + +message ContractInfo +{ + // The serial number of the contract. + int64 serial_number = 1; + // The author of the contract, this is the person who deployed the contract. + aelf.Address author = 2; + // The category of contract code(0: C#). + sint32 category = 3; + // The hash of the contract code. + aelf.Hash code_hash = 4; + // Whether it is a system contract. + bool is_system_contract = 5; + // The version of the current contract. + int32 version = 6; + string contract_version = 7; + // Indicates if the contract is the user contract. + bool is_user_contract = 8; +} + +message ContractDeploymentInput { + // The category of contract code(0: C#). + sint32 category = 1; + // The byte array of the contract code. + bytes code = 2; + // The parameter of contract constructor. + bytes parameter = 3; +} + +message SystemContractDeploymentInput { + message SystemTransactionMethodCall { + // The method name of system transaction. + string method_name = 1; + // The params of system transaction method. + bytes params = 2; + } + message SystemTransactionMethodCallList { + // The list of system transactions. + repeated SystemTransactionMethodCall value = 1; + } + // The category of contract code(0: C#). + sint32 category = 1; + // The byte array of the contract code. + bytes code = 2; + // The name of the contract. It has to be unique. + aelf.Hash name = 3; + // An initial list of transactions for the system contract, + // which is executed in sequence when the contract is deployed. + SystemTransactionMethodCallList transaction_method_call_list = 4; +} + +message ContractUpdateInput { + // The contract address that needs to be updated. + aelf.Address address = 1; + // The byte array of the new contract code. + bytes code = 2; +} + +message ContractCodeCheckInput{ + // The byte array of the contract code to be checked. + bytes contract_input = 1; + // Whether the input contract is to be deployed or updated. + bool is_contract_deployment = 2; + // Method to call after code check complete(DeploySmartContract or UpdateSmartContract). + string code_check_release_method = 3; + // The id of the proposed contract. + aelf.Hash proposed_contract_input_hash = 4; + // The category of contract code(0: C#). + sint32 category = 5; + // Indicates if the contract is the system contract. + bool is_system_contract = 6; +} + +message ContractProposed +{ + option (aelf.is_event) = true; + // The id of the proposed contract. + aelf.Hash proposed_contract_input_hash = 1; +} + +message ContractDeployed +{ + option (aelf.is_event) = true; + // The author of the contract, this is the person who deployed the contract. + aelf.Address author = 1 [(aelf.is_indexed) = true]; + // The hash of the contract code. + aelf.Hash code_hash = 2 [(aelf.is_indexed) = true]; + // The address of the contract. + aelf.Address address = 3; + // The version of the current contract. + int32 version = 4; + // The name of the contract. It has to be unique. + aelf.Hash Name = 5; + string contract_version = 6; +} + +message CodeCheckRequired +{ + option (aelf.is_event) = true; + // The byte array of the contract code. + bytes code = 1; + // The id of the proposed contract. + aelf.Hash proposed_contract_input_hash = 2; + // The category of contract code(0: C#). + sint32 category = 3; + // Indicates if the contract is the system contract. + bool is_system_contract = 4; + // Indicates if the contract is the user contract. + bool is_user_contract = 5; +} + +message CodeUpdated +{ + option (aelf.is_event) = true; + // The address of the updated contract. + aelf.Address address = 1 [(aelf.is_indexed) = true]; + // The byte array of the old contract code. + aelf.Hash old_code_hash = 2; + // The byte array of the new contract code. + aelf.Hash new_code_hash = 3; + // The version of the current contract. + int32 version = 4; + string contract_version = 5; +} + +message AuthorUpdated +{ + option (aelf.is_event) = true; + // The address of the contract. + aelf.Address address = 1 [(aelf.is_indexed) = true]; + // The old author of the contract. + aelf.Address old_author = 2; + // The new author of the contract. + aelf.Address new_author = 3; +} + +message ValidateSystemContractAddressInput { + // The name hash of the contract. + aelf.Hash system_contract_hash_name = 1; + // The address of the contract. + aelf.Address address = 2; +} + +message ReleaseContractInput { + // The hash of the proposal. + aelf.Hash proposal_id = 1; + // The id of the proposed contract. + aelf.Hash proposed_contract_input_hash = 2; +} + +message ContractCodeHashList { + repeated aelf.Hash value = 1; +} + +message ContractCodeHashMap { + map value = 1; +} + +message SetContractAuthorInput{ + aelf.Address contract_address = 1; + aelf.Address new_author = 2; +} + +message DeployUserSmartContractOutput{ + aelf.Hash code_hash = 1; +} \ No newline at end of file diff --git a/basics/timelock-contract/src/TimelockContract.cs b/basics/timelock-contract/src/TimelockContract.cs index 1559c4d..b37385b 100644 --- a/basics/timelock-contract/src/TimelockContract.cs +++ b/basics/timelock-contract/src/TimelockContract.cs @@ -9,12 +9,12 @@ public partial class TimelockContract : TimelockContractContainer.TimelockContra { public override Empty Initialize(InitializeInput input) { + Assert(!State.Initialized.Value, "Already initialized."); + State.GenesisContract.Value = Context.GetZeroSmartContractAddress(); + var author = State.GenesisContract.GetContractAuthor.Call(Context.Self); + Assert(author == Context.Sender, "No permission."); Assert(input.Delay <= TimelockContractConstants.MAX_DELAY, "Delay must not exceed maximum delay"); Assert(input.Delay >= TimelockContractConstants.MIN_DELAY, "Delay must exceed minimum delay"); - if (State.Initialized.Value) - { - return new Empty(); - } State.Admin.Value = Context.Sender; State.Delay.Value = input.Delay; State.Initialized.Value = true; diff --git a/basics/timelock-contract/src/TimelockContractReferenceState.cs b/basics/timelock-contract/src/TimelockContractReferenceState.cs new file mode 100644 index 0000000..4c5ed85 --- /dev/null +++ b/basics/timelock-contract/src/TimelockContractReferenceState.cs @@ -0,0 +1,9 @@ +using AElf.Standards.ACS0; + +namespace AElf.Contracts.Timelock +{ + public partial class TimelockContractState + { + internal ACS0Container.ACS0ReferenceState GenesisContract { get; set; } + } +} \ No newline at end of file diff --git a/basics/timelock-contract/test/Protobuf/stub/acs0.proto b/basics/timelock-contract/test/Protobuf/stub/acs0.proto new file mode 100644 index 0000000..d55d01a --- /dev/null +++ b/basics/timelock-contract/test/Protobuf/stub/acs0.proto @@ -0,0 +1,287 @@ +/** + * AElf Standards ACS0(Contract Deployment Standard) + * + * Used to manage the deployment and update of contracts. + */ +syntax = "proto3"; + +package acs0; +option csharp_namespace = "AElf.Standards.ACS0"; + +import public "aelf/core.proto"; +import "aelf/options.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/wrappers.proto"; + +service ACS0 { + // Deploy a system smart contract on chain and return the address of the system contract deployed. + rpc DeploySystemSmartContract (SystemContractDeploymentInput) returns (aelf.Address) { + } + + // Deploy a smart contract on chain and return the address of the contract deployed. + rpc DeploySmartContract (ContractDeploymentInput) returns (aelf.Address) { + } + + // Update a smart contract on chain. + rpc UpdateSmartContract (ContractUpdateInput) returns (aelf.Address) { + } + + // Create a proposal to deploy a new contract and returns the id of the proposed contract. + rpc ProposeNewContract (ContractDeploymentInput) returns (aelf.Hash) { + } + + // Create a proposal to check the code of a contract and return the id of the proposed contract. + rpc ProposeContractCodeCheck (ContractCodeCheckInput) returns (aelf.Hash) { + } + + // Create a proposal to update the specified contract + // and return the id of the proposed contract. + rpc ProposeUpdateContract (ContractUpdateInput) returns (aelf.Hash) { + } + + // Release the contract proposal which has been approved. + rpc ReleaseApprovedContract (ReleaseContractInput) returns (google.protobuf.Empty) { + } + + // Release the proposal which has passed the code check. + rpc ReleaseCodeCheckedContract (ReleaseContractInput) returns (google.protobuf.Empty) { + } + + // Deploy a user smart contract on chain and return the hash of the contract code. + rpc DeployUserSmartContract (ContractDeploymentInput) returns (DeployUserSmartContractOutput) { + } + + // Update a user smart contract on chain. + rpc UpdateUserSmartContract (ContractUpdateInput) returns (google.protobuf.Empty) { + } + + // Release the proposal which has passed the code check. + rpc ReleaseApprovedUserSmartContract(ReleaseContractInput) returns (google.protobuf.Empty) { + } + + // Perform user contract deployment. + rpc PerformDeployUserSmartContract(ContractDeploymentInput) returns (aelf.Address) { + } + + // Perform user contract update. + rpc PerformUpdateUserSmartContract(ContractUpdateInput) returns (google.protobuf.Empty) { + } + + // Set author of the specified contract. + rpc SetContractAuthor (SetContractAuthorInput) returns (google.protobuf.Empty) { + } + + // Validate whether the input system contract exists. + rpc ValidateSystemContractAddress(ValidateSystemContractAddressInput) returns (google.protobuf.Empty){ + } + + // Set authority of contract deployment. + rpc SetContractProposerRequiredState (google.protobuf.BoolValue) returns (google.protobuf.Empty) { + } + + // Get the current serial number of genesis contract + // (corresponds to the serial number that will be given to the next deployed contract). + rpc CurrentContractSerialNumber (google.protobuf.Empty) returns (google.protobuf.Int64Value) { + option (aelf.is_view) = true; + } + + // Get detailed information about the specified contract. + rpc GetContractInfo (aelf.Address) returns (ContractInfo) { + option (aelf.is_view) = true; + } + + // Get author of the specified contract. + rpc GetContractAuthor (aelf.Address) returns (aelf.Address) { + option (aelf.is_view) = true; + } + + // Get the code hash of the contract about the specified address. + rpc GetContractHash (aelf.Address) returns (aelf.Hash) { + option (aelf.is_view) = true; + } + + // Get the address of a system contract by its name. + rpc GetContractAddressByName (aelf.Hash) returns (aelf.Address) { + option (aelf.is_view) = true; + } + + // Get the registration of a smart contract by its address. + rpc GetSmartContractRegistrationByAddress (aelf.Address) returns (aelf.SmartContractRegistration) { + option (aelf.is_view) = true; + } + + // Get the registration of a smart contract by code hash. + rpc GetSmartContractRegistrationByCodeHash (aelf.Hash) returns (aelf.SmartContractRegistration) { + option (aelf.is_view) = true; + } + + rpc GetContractCodeHashListByDeployingBlockHeight (google.protobuf.Int64Value) returns (ContractCodeHashList) { + option (aelf.is_view) = true; + } +} + +message ContractInfo +{ + // The serial number of the contract. + int64 serial_number = 1; + // The author of the contract, this is the person who deployed the contract. + aelf.Address author = 2; + // The category of contract code(0: C#). + sint32 category = 3; + // The hash of the contract code. + aelf.Hash code_hash = 4; + // Whether it is a system contract. + bool is_system_contract = 5; + // The version of the current contract. + int32 version = 6; + string contract_version = 7; + // Indicates if the contract is the user contract. + bool is_user_contract = 8; +} + +message ContractDeploymentInput { + // The category of contract code(0: C#). + sint32 category = 1; + // The byte array of the contract code. + bytes code = 2; + // The parameter of contract constructor. + bytes parameter = 3; +} + +message SystemContractDeploymentInput { + message SystemTransactionMethodCall { + // The method name of system transaction. + string method_name = 1; + // The params of system transaction method. + bytes params = 2; + } + message SystemTransactionMethodCallList { + // The list of system transactions. + repeated SystemTransactionMethodCall value = 1; + } + // The category of contract code(0: C#). + sint32 category = 1; + // The byte array of the contract code. + bytes code = 2; + // The name of the contract. It has to be unique. + aelf.Hash name = 3; + // An initial list of transactions for the system contract, + // which is executed in sequence when the contract is deployed. + SystemTransactionMethodCallList transaction_method_call_list = 4; +} + +message ContractUpdateInput { + // The contract address that needs to be updated. + aelf.Address address = 1; + // The byte array of the new contract code. + bytes code = 2; +} + +message ContractCodeCheckInput{ + // The byte array of the contract code to be checked. + bytes contract_input = 1; + // Whether the input contract is to be deployed or updated. + bool is_contract_deployment = 2; + // Method to call after code check complete(DeploySmartContract or UpdateSmartContract). + string code_check_release_method = 3; + // The id of the proposed contract. + aelf.Hash proposed_contract_input_hash = 4; + // The category of contract code(0: C#). + sint32 category = 5; + // Indicates if the contract is the system contract. + bool is_system_contract = 6; +} + +message ContractProposed +{ + option (aelf.is_event) = true; + // The id of the proposed contract. + aelf.Hash proposed_contract_input_hash = 1; +} + +message ContractDeployed +{ + option (aelf.is_event) = true; + // The author of the contract, this is the person who deployed the contract. + aelf.Address author = 1 [(aelf.is_indexed) = true]; + // The hash of the contract code. + aelf.Hash code_hash = 2 [(aelf.is_indexed) = true]; + // The address of the contract. + aelf.Address address = 3; + // The version of the current contract. + int32 version = 4; + // The name of the contract. It has to be unique. + aelf.Hash Name = 5; + string contract_version = 6; +} + +message CodeCheckRequired +{ + option (aelf.is_event) = true; + // The byte array of the contract code. + bytes code = 1; + // The id of the proposed contract. + aelf.Hash proposed_contract_input_hash = 2; + // The category of contract code(0: C#). + sint32 category = 3; + // Indicates if the contract is the system contract. + bool is_system_contract = 4; + // Indicates if the contract is the user contract. + bool is_user_contract = 5; +} + +message CodeUpdated +{ + option (aelf.is_event) = true; + // The address of the updated contract. + aelf.Address address = 1 [(aelf.is_indexed) = true]; + // The byte array of the old contract code. + aelf.Hash old_code_hash = 2; + // The byte array of the new contract code. + aelf.Hash new_code_hash = 3; + // The version of the current contract. + int32 version = 4; + string contract_version = 5; +} + +message AuthorUpdated +{ + option (aelf.is_event) = true; + // The address of the contract. + aelf.Address address = 1 [(aelf.is_indexed) = true]; + // The old author of the contract. + aelf.Address old_author = 2; + // The new author of the contract. + aelf.Address new_author = 3; +} + +message ValidateSystemContractAddressInput { + // The name hash of the contract. + aelf.Hash system_contract_hash_name = 1; + // The address of the contract. + aelf.Address address = 2; +} + +message ReleaseContractInput { + // The hash of the proposal. + aelf.Hash proposal_id = 1; + // The id of the proposed contract. + aelf.Hash proposed_contract_input_hash = 2; +} + +message ContractCodeHashList { + repeated aelf.Hash value = 1; +} + +message ContractCodeHashMap { + map value = 1; +} + +message SetContractAuthorInput{ + aelf.Address contract_address = 1; + aelf.Address new_author = 2; +} + +message DeployUserSmartContractOutput{ + aelf.Hash code_hash = 1; +} \ No newline at end of file diff --git a/basics/timelock-contract/test/TimelockContractTests.cs b/basics/timelock-contract/test/TimelockContractTests.cs index 37243a7..6b7b599 100644 --- a/basics/timelock-contract/test/TimelockContractTests.cs +++ b/basics/timelock-contract/test/TimelockContractTests.cs @@ -37,7 +37,7 @@ public async Task ChangeAdminTests() }; TransactionInput transactionInput = new TransactionInput { - Target = ContractAddress, + Target = TimelockContractAddress, Method = "ChangeAdmin", Data = changeAdminInput.ToByteString(), ExecuteTime = _currentTime.AddSeconds(3 * 24 * 60 * 60) @@ -62,7 +62,7 @@ public async Task SetDelayTests() }; TransactionInput transactionInput = new TransactionInput { - Target = ContractAddress, + Target = TimelockContractAddress, Method = "SetDelay", Data = setDelayInput.ToByteString(), ExecuteTime = _currentTime.AddSeconds(3 * 24 * 60 * 60) @@ -138,13 +138,13 @@ private async Task InitializeAsync() { await TokenContractStub.Transfer.SendAsync(new TransferInput { - To = ContractAddress, + To = TimelockContractAddress, Symbol = "ELF", Amount = 1000_00000000 }); await TokenContractStub.Approve.SendAsync(new ApproveInput { - Spender = ContractAddress, + Spender = TimelockContractAddress, Symbol = "ELF", Amount = 1000_00000000 }); diff --git a/basics/timelock-contract/test/_Setup.cs b/basics/timelock-contract/test/_Setup.cs index ded1ae3..e8cf478 100644 --- a/basics/timelock-contract/test/_Setup.cs +++ b/basics/timelock-contract/test/_Setup.cs @@ -1,15 +1,26 @@ -using AElf.Contracts.MultiToken; +using System.IO; +using AElf.Contracts.MultiToken; using AElf.ContractTestBase.ContractTestKit; using AElf.Cryptography.ECDSA; +using AElf.Kernel; +using AElf.Kernel.SmartContract; +using AElf.Standards.ACS0; using AElf.Types; +using Google.Protobuf; using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; +using Volo.Abp.Threading; namespace AElf.Contracts.Timelock { // This class is used to load the context required for unit testing. public class Module : Testing.TestBase.ContractTestModule { - + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddSingleton(); + Configure(o => o.ContractDeploymentAuthorityRequired = false); + } } // The TestBase class inherit ContractTestBase class, which is used to define and get stub classes required for unit testing. @@ -23,15 +34,26 @@ public class TestBase : Testing.TestBase.ContractTestBase // private readonly ECKeyPair KeyPair; internal TimelockContractContainer.TimelockContractStub TimelockContractStub; internal TokenContractContainer.TokenContractStub TokenContractStub; + internal ACS0Container.ACS0Stub ZeroContractStub; protected ECKeyPair DefaultKeyPair => Accounts[0].KeyPair; protected Address DefaultAddress => Accounts[0].Address; protected Address UserAddress => Accounts[1].Address; + protected Address TimelockContractAddress; protected IBlockTimeProvider BlockTimeProvider => Application.ServiceProvider.GetRequiredService(); public TestBase() { + ZeroContractStub = GetContractZeroTester(DefaultKeyPair); + var result = AsyncHelper.RunSync(async () =>await ZeroContractStub.DeploySmartContract.SendAsync(new ContractDeploymentInput + { + Category = KernelConstants.CodeCoverageRunnerCategory, + Code = ByteString.CopyFrom( + File.ReadAllBytes(typeof(TimelockContract).Assembly.Location)) + })); + + TimelockContractAddress = Address.Parser.ParseFrom(result.TransactionResult.ReturnValue); // KeyPair = SampleAccount.Accounts.First().KeyPair; TimelockContractStub = GetTimelockContractStub(DefaultKeyPair); TokenContractStub = GetTokenContractStub(DefaultKeyPair); @@ -39,13 +61,18 @@ public TestBase() private TimelockContractContainer.TimelockContractStub GetTimelockContractStub(ECKeyPair senderKeyPair) { - return GetTester(ContractAddress, senderKeyPair); + return GetTester(TimelockContractAddress, senderKeyPair); } private TokenContractContainer.TokenContractStub GetTokenContractStub(ECKeyPair senderKeyPair) { return GetTester(TokenContractAddress, senderKeyPair); } + + private ACS0Container.ACS0Stub GetContractZeroTester(ECKeyPair keyPair) + { + return GetTester(BasicContractZeroAddress, keyPair); + } } } \ No newline at end of file From 618399da98873ace8f9771eeeb803518f1ec5fef Mon Sep 17 00:00:00 2001 From: aelf-lu Date: Wed, 27 Sep 2023 14:10:16 +0800 Subject: [PATCH 19/20] fix: add unit test --- .../test/TimelockContractTests.cs | 466 +++++++++++++++++- 1 file changed, 450 insertions(+), 16 deletions(-) diff --git a/basics/timelock-contract/test/TimelockContractTests.cs b/basics/timelock-contract/test/TimelockContractTests.cs index 6b7b599..01a7cbb 100644 --- a/basics/timelock-contract/test/TimelockContractTests.cs +++ b/basics/timelock-contract/test/TimelockContractTests.cs @@ -1,3 +1,5 @@ +using System; +using System.Linq; using System.Threading.Tasks; using AElf.Contracts.MultiToken; using AElf.CSharp.Core; @@ -18,13 +20,34 @@ public partial class TimelockContractTests : TestBase [Fact] public async Task InitializeTests() { + ulong delay = 2 * 24 * 60 * 60; InitializeInput input = new InitializeInput { - Delay = 2 * 24 * 60 * 60 + Delay = delay }; await TimelockContractStub.Initialize.SendAsync(input); + var getDelay = await TimelockContractStub.GetDelay.CallAsync(new Empty()); + delay.ShouldBe(getDelay.Value); } + [Fact] + public async Task InitializeDuplicateTests() + { + InitializeInput input = new InitializeInput + { + Delay = 2 * 24 * 60 * 60 + }; + try + { + await TimelockContractStub.Initialize.SendAsync(input); + await TimelockContractStub.Initialize.SendAsync(input); + } + catch (Exception e) + { + e.Message.ShouldContain("Already initialized."); + } + } + [Fact] public async Task ChangeAdminTests() { @@ -44,10 +67,67 @@ public async Task ChangeAdminTests() }; var txnHash = await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); BlockTimeProvider.SetBlockTime(_currentTime.AddSeconds(3 * 24 * 60 * 60)); - await TimelockContractStub.ExecuteTransaction.SendAsync(transactionInput); + var txRes = await TimelockContractStub.ExecuteTransaction.SendAsync(transactionInput); var addressAfter = await TimelockContractStub.GetAdmin.CallAsync(new Empty()); addressAfter.ShouldBe(UserAddress); + + var newAdminEvent = NewAdmin.Parser + .ParseFrom(txRes.TransactionResult.Logs.First(l => l.Name.Contains(nameof(NewAdmin))) + .NonIndexed); + newAdminEvent.Admin.ShouldBe(changeAdminInput.Admin); + } + + [Fact] + public async Task ChangeAdminTests_NoPermission() + { + await InitializeTests(); + ChangeAdminInput changeAdminInput = new ChangeAdminInput() + { + Admin = UserAddress + }; + try + { + await TimelockContractStub.ChangeAdmin.SendAsync(changeAdminInput); + } + catch (Exception e) + { + e.Message.ShouldContain("No permission"); + } + var addressAfter = await TimelockContractStub.GetAdmin.CallAsync(new Empty()); + addressAfter.ShouldBe(DefaultAddress); + } + + [Fact] + public async Task ChangeAdminTests_InputNull() + { + await InitializeTests(); + var address = await TimelockContractStub.GetAdmin.CallAsync(new Empty()); + address.ShouldBe(DefaultAddress); + ChangeAdminInput changeAdminInput = new ChangeAdminInput() + { + Admin = null + }; + TransactionInput transactionInput = new TransactionInput + { + Target = TimelockContractAddress, + Method = "ChangeAdmin", + Data = changeAdminInput.ToByteString(), + ExecuteTime = _currentTime.AddSeconds(3 * 24 * 60 * 60) + }; + var txnHash = await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); + BlockTimeProvider.SetBlockTime(_currentTime.AddSeconds(3 * 24 * 60 * 60)); + + try + { + await TimelockContractStub.ExecuteTransaction.SendAsync(transactionInput); + } + catch (Exception e) + { + e.Message.ShouldContain("NewAdmin must not be null"); + } + var addressAfter = await TimelockContractStub.GetAdmin.CallAsync(new Empty()); + addressAfter.ShouldBe(DefaultAddress); } [Fact] @@ -69,10 +149,35 @@ public async Task SetDelayTests() }; var txnHash = await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); BlockTimeProvider.SetBlockTime(_currentTime.AddSeconds(3 * 24 * 60 * 60)); - await TimelockContractStub.ExecuteTransaction.SendAsync(transactionInput); + var txRes = await TimelockContractStub.ExecuteTransaction.SendAsync(transactionInput); UInt64Value delayAfter = await TimelockContractStub.GetDelay.CallAsync(new Empty()); delayAfter.Value.ShouldBe((uint)(4 * 24 * 60 * 60)); + + var newDelayEvent = NewDelay.Parser + .ParseFrom(txRes.TransactionResult.Logs.First(l => l.Name.Contains(nameof(NewDelay))) + .NonIndexed); + newDelayEvent.Delay.ShouldBe(setDelayInput.Delay); + } + + [Fact] + public async Task SetDelayTests_NoPermission() + { + await InitializeTests(); + UInt64Value delay = await TimelockContractStub.GetDelay.CallAsync(new Empty()); + delay.Value.ShouldBe((uint)(2 * 24 * 60 * 60)); + SetDelayInput setDelayInput = new SetDelayInput + { + Delay = 4 * 24 * 60 * 60 + }; + try + { + await TimelockContractStub.SetDelay.SendAsync(setDelayInput); + } + catch (Exception e) + { + e.Message.ShouldContain("No Permission"); + } } [Fact] @@ -95,15 +200,95 @@ public async Task QueueTransactionTests() ExecuteTime = _currentTime.AddSeconds(3 * 24 * 60 * 60) }; Hash txnHash = HashHelper.ComputeFrom(transactionInput); - await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); + var txRes = await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); var result = await TimelockContractStub.GetTransaction.CallAsync(txnHash); var expectResult = new BoolValue { Value = true }; result.ShouldBe(expectResult); + + var queueTransactionEvent = QueueTransaction.Parser + .ParseFrom(txRes.TransactionResult.Logs.First(l => l.Name.Contains(nameof(QueueTransaction))) + .NonIndexed); + queueTransactionEvent.Target.ShouldBe(transactionInput.Target); + queueTransactionEvent.Method.ShouldBe(transactionInput.Method); + queueTransactionEvent.Data.ShouldBe(transactionInput.Data); + queueTransactionEvent.ExecuteTime.ShouldBe(transactionInput.ExecuteTime); } + [Fact] + public async Task QueueTransactionTests_NoPermission() + { + await ChangeAdminTests(); + TransferFromInput transferFromInput = new TransferFromInput + { + From = DefaultAddress, + To = UserAddress, + Amount = 502, + Symbol = "ELF", + Memo = "TEST" + }; + TransactionInput transactionInput = new TransactionInput + { + Target = TokenContractAddress, + Method = "TransferFrom", + Data = transferFromInput.ToByteString(), + ExecuteTime = _currentTime.AddSeconds(3 * 24 * 60 * 60) + }; + Hash txnHash = HashHelper.ComputeFrom(transactionInput); + try + { + await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); + } + catch (Exception e) + { + e.Message.ShouldContain("No Permission"); + } + var result = await TimelockContractStub.GetTransaction.CallAsync(txnHash); + var expectResult = new BoolValue + { + Value = false + }; + result.ShouldBe(expectResult); + } + + [Fact] + public async Task QueueTransactionTests_TimeLimit() + { + await InitializeTests(); + TransferFromInput transferFromInput = new TransferFromInput + { + From = DefaultAddress, + To = UserAddress, + Amount = 502, + Symbol = "ELF", + Memo = "TEST" + }; + TransactionInput transactionInput = new TransactionInput + { + Target = TokenContractAddress, + Method = "TransferFrom", + Data = transferFromInput.ToByteString(), + ExecuteTime = _currentTime.AddSeconds(-(1 * 24 * 60 * 60)) + }; + Hash txnHash = HashHelper.ComputeFrom(transactionInput); + try + { + await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); + } + catch (Exception e) + { + e.Message.ShouldContain("Estimated execution block must satisfy delay"); + } + var result = await TimelockContractStub.GetTransaction.CallAsync(txnHash); + var expectResult = new BoolValue + { + Value = false + }; + result.ShouldBe(expectResult); + } + [Fact] public async Task CancelTransactionTests() { @@ -125,29 +310,62 @@ public async Task CancelTransactionTests() }; Hash txnHash = HashHelper.ComputeFrom(transactionInput); await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); - await TimelockContractStub.CancelTransaction.SendAsync(transactionInput); + var txRes = await TimelockContractStub.CancelTransaction.SendAsync(transactionInput); var result = await TimelockContractStub.GetTransaction.CallAsync(txnHash); var expectResult = new BoolValue { Value = false }; result.ShouldBe(expectResult); + + var cancelTransactionEvent = CancelTransaction.Parser + .ParseFrom(txRes.TransactionResult.Logs.First(l => l.Name.Contains(nameof(CancelTransaction))) + .NonIndexed); + cancelTransactionEvent.Target.ShouldBe(transactionInput.Target); + cancelTransactionEvent.Method.ShouldBe(transactionInput.Method); + cancelTransactionEvent.Data.ShouldBe(transactionInput.Data); + cancelTransactionEvent.ExecuteTime.ShouldBe(transactionInput.ExecuteTime); } - private async Task InitializeAsync() + [Fact] + public async Task CancelTransactionTests_NoPermission() { - await TokenContractStub.Transfer.SendAsync(new TransferInput + await InitializeTests(); + TransferFromInput transferFromInput = new TransferFromInput { - To = TimelockContractAddress, + From = DefaultAddress, + To = UserAddress, + Amount = 502, Symbol = "ELF", - Amount = 1000_00000000 - }); - await TokenContractStub.Approve.SendAsync(new ApproveInput + Memo = "TEST" + }; + TransactionInput transactionInput = new TransactionInput { - Spender = TimelockContractAddress, - Symbol = "ELF", - Amount = 1000_00000000 - }); + Target = TokenContractAddress, + Method = "TransferFrom", + Data = transferFromInput.ToByteString(), + ExecuteTime = _currentTime.AddSeconds(3 * 24 * 60 * 60) + }; + Hash txnHash = HashHelper.ComputeFrom(transactionInput); + await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); + + await ChangeAdminForTestPermission(); + + try + { + await TimelockContractStub.CancelTransaction.SendAsync(transactionInput); + } + catch (Exception e) + { + e.Message.ShouldContain("No Permission"); + } + + var result = await TimelockContractStub.GetTransaction.CallAsync(txnHash); + var expectResult = new BoolValue + { + Value = true + }; + result.ShouldBe(expectResult); } /** @@ -180,7 +398,8 @@ public async Task ExecuteTransactionTests() }; var txnHash = await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); BlockTimeProvider.SetBlockTime(_currentTime.AddSeconds(3 * 24 * 60 * 60)); - await TimelockContractStub.ExecuteTransaction.SendAsync(transactionInput); + + var txRes = await TimelockContractStub.ExecuteTransaction.SendAsync(transactionInput); var balance2 = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { @@ -200,7 +419,222 @@ public async Task ExecuteTransactionTests() Value = false }; result.ShouldBe(expectResult); + + var executeTransactionEvent = ExecuteTransaction.Parser + .ParseFrom(txRes.TransactionResult.Logs.First(l => l.Name.Contains(nameof(ExecuteTransaction))) + .NonIndexed); + executeTransactionEvent.Target.ShouldBe(transactionInput.Target); + executeTransactionEvent.Method.ShouldBe(transactionInput.Method); + executeTransactionEvent.Data.ShouldBe(transactionInput.Data); + executeTransactionEvent.ExecuteTime.ShouldBe(transactionInput.ExecuteTime); + } + + [Fact] + public async Task ExecuteTransactionTests_NoPermission() + { + await InitializeTests(); + await InitializeAsync(); + var balance1 = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = DefaultAddress, + Symbol = "ELF" + }); + TransferFromInput transferFromInput = new TransferFromInput + { + From = DefaultAddress, + To = UserAddress, + Amount = 502, + Symbol = "ELF", + Memo = "TEST" + }; + TransactionInput transactionInput = new TransactionInput + { + Target = TokenContractAddress, + Method = "TransferFrom", + Data = transferFromInput.ToByteString(), + ExecuteTime = _currentTime.AddSeconds(3 * 24 * 60 * 60) + }; + var txnHash = await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); + + await ChangeAdminForTestPermission(); + + BlockTimeProvider.SetBlockTime(_currentTime.AddSeconds(3 * 24 * 60 * 60)); + + try + { + await TimelockContractStub.ExecuteTransaction.SendAsync(transactionInput); + } + catch (Exception e) + { + e.Message.ShouldContain("No Permission"); + } + + var result = await TimelockContractStub.GetTransaction.CallAsync(txnHash.Output); + var expectResult = new BoolValue + { + Value = true + }; + result.ShouldBe(expectResult); } + + [Fact] + public async Task ExecuteTransactionTests_NotInQueue() + { + await InitializeTests(); + await InitializeAsync(); + var balance1 = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = DefaultAddress, + Symbol = "ELF" + }); + TransferFromInput transferFromInput = new TransferFromInput + { + From = DefaultAddress, + To = UserAddress, + Amount = 502, + Symbol = "ELF", + Memo = "TEST" + }; + TransactionInput transactionInput = new TransactionInput + { + Target = TokenContractAddress, + Method = "TransferFrom", + Data = transferFromInput.ToByteString(), + ExecuteTime = _currentTime.AddSeconds(3 * 24 * 60 * 60) + }; + + BlockTimeProvider.SetBlockTime(_currentTime.AddSeconds(3 * 24 * 60 * 60)); + + try + { + await TimelockContractStub.ExecuteTransaction.SendAsync(transactionInput); + } + catch (Exception e) + { + e.Message.ShouldContain("executeTransaction: Transaction hasn't been queued"); + } + } + + [Fact] + public async Task ExecuteTransactionTests_NotReachExecuteTime() + { + await InitializeTests(); + await InitializeAsync(); + var balance1 = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = DefaultAddress, + Symbol = "ELF" + }); + TransferFromInput transferFromInput = new TransferFromInput + { + From = DefaultAddress, + To = UserAddress, + Amount = 502, + Symbol = "ELF", + Memo = "TEST" + }; + TransactionInput transactionInput = new TransactionInput + { + Target = TokenContractAddress, + Method = "TransferFrom", + Data = transferFromInput.ToByteString(), + ExecuteTime = _currentTime.AddSeconds(3 * 24 * 60 * 60) + }; + var txnHash = await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); + BlockTimeProvider.SetBlockTime(_currentTime.AddSeconds(2 * 24 * 60 * 60)); + + try + { + await TimelockContractStub.ExecuteTransaction.SendAsync(transactionInput); + } + catch (Exception e) + { + e.Message.ShouldContain("executeTransaction: Transaction hasn't surpassed time lock"); + } + + var result = await TimelockContractStub.GetTransaction.CallAsync(txnHash.Output); + var expectResult = new BoolValue + { + Value = true + }; + result.ShouldBe(expectResult); + } + + [Fact] + public async Task ExecuteTransactionTests_ExceedGracePeriod() + { + await InitializeTests(); + await InitializeAsync(); + var balance1 = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = DefaultAddress, + Symbol = "ELF" + }); + TransferFromInput transferFromInput = new TransferFromInput + { + From = DefaultAddress, + To = UserAddress, + Amount = 502, + Symbol = "ELF", + Memo = "TEST" + }; + TransactionInput transactionInput = new TransactionInput + { + Target = TokenContractAddress, + Method = "TransferFrom", + Data = transferFromInput.ToByteString(), + ExecuteTime = _currentTime.AddSeconds(3 * 24 * 60 * 60) + }; + var txnHash = await TimelockContractStub.QueueTransaction.SendAsync(transactionInput); + BlockTimeProvider.SetBlockTime(_currentTime.AddSeconds(8 * 24 * 60 * 60)); + + try + { + await TimelockContractStub.ExecuteTransaction.SendAsync(transactionInput); + } + catch (Exception e) + { + e.Message.ShouldContain("executeTransaction: Transaction is stale"); + } + + var result = await TimelockContractStub.GetTransaction.CallAsync(txnHash.Output); + var expectResult = new BoolValue + { + Value = true + }; + result.ShouldBe(expectResult); + } + + private async Task InitializeAsync() + { + await TokenContractStub.Transfer.SendAsync(new TransferInput + { + To = TimelockContractAddress, + Symbol = "ELF", + Amount = 1000_00000000 + }); + await TokenContractStub.Approve.SendAsync(new ApproveInput + { + Spender = TimelockContractAddress, + Symbol = "ELF", + Amount = 1000_00000000 + }); + } + + private async Task ChangeAdminForTestPermission() + { + TransactionInput changeAdminTransactionInput = new TransactionInput + { + Target = TimelockContractAddress, + Method = "ChangeAdmin", + Data = new ChangeAdminInput() {Admin = UserAddress}.ToByteString(), + ExecuteTime = _currentTime.AddSeconds(3 * 24 * 60 * 60) + }; + await TimelockContractStub.QueueTransaction.SendAsync(changeAdminTransactionInput); + BlockTimeProvider.SetBlockTime(_currentTime.AddSeconds(3 * 24 * 60 * 60)); + await TimelockContractStub.ExecuteTransaction.SendAsync(changeAdminTransactionInput); + } + } } \ No newline at end of file From 2a9cd595c082ccd008c54d567674abff9343fcf4 Mon Sep 17 00:00:00 2001 From: aelf-lu Date: Wed, 27 Sep 2023 18:00:51 +0800 Subject: [PATCH 20/20] fix: remove useless code --- .../src/Protobuf/contract/timelock_contract.proto | 9 ++++----- .../test/Protobuf/stub/timelock_contract.proto | 9 ++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto b/basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto index 79f40e1..09dfd6c 100644 --- a/basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto +++ b/basics/timelock-contract/src/Protobuf/contract/timelock_contract.proto @@ -51,11 +51,10 @@ message ChangeAdminInput { } message TransactionInput { - aelf.Hash txn_hash = 1; - aelf.Address target = 2; - string method = 3; - bytes data = 4; - google.protobuf.Timestamp execute_time = 5; + aelf.Address target = 1; + string method = 2; + bytes data = 3; + google.protobuf.Timestamp execute_time = 4; } // Events diff --git a/basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto b/basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto index 79f40e1..09dfd6c 100644 --- a/basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto +++ b/basics/timelock-contract/test/Protobuf/stub/timelock_contract.proto @@ -51,11 +51,10 @@ message ChangeAdminInput { } message TransactionInput { - aelf.Hash txn_hash = 1; - aelf.Address target = 2; - string method = 3; - bytes data = 4; - google.protobuf.Timestamp execute_time = 5; + aelf.Address target = 1; + string method = 2; + bytes data = 3; + google.protobuf.Timestamp execute_time = 4; } // Events