diff --git a/contract/AElf.Contracts.Oracle/OracleContract.cs b/contract/AElf.Contracts.Oracle/OracleContract.cs index 67da7b14..eb889d99 100644 --- a/contract/AElf.Contracts.Oracle/OracleContract.cs +++ b/contract/AElf.Contracts.Oracle/OracleContract.cs @@ -86,7 +86,7 @@ public override Hash Query(QueryInput input) var callbackInfo = input.CallbackInfo ?? new CallbackInfo { ContractAddress = Context.Self, - MethodName = "NotSetCallbackInfo" + MethodName = NotSetCallbackInfo }; var queryRecord = new QueryRecord { @@ -120,6 +120,63 @@ public override Hash Query(QueryInput input) return queryId; } + public override Hash CreateQueryTask(CreateQueryTaskInput input) + { + // TODO: Pay tx fee to contract. + + var taskId = Context.TransactionId; + State.QueryTaskMap[taskId.ToHex()] = new QueryTask + { + Creator = Context.Sender, + CallbackInfo = input.CallbackInfo, + EachPayment = input.EachPayment, + SupposedQueryTimes = input.SupposedQueryTimes, + QueryInfo = input.QueryInfo, + EndTime = input.EndTime, + AggregatorContractAddress = input.AggregatorContractAddress + }; + return taskId; + } + + public override Empty CompleteQueryTask(CompleteQueryTaskInput input) + { + var queryTask = State.QueryTaskMap[input.TaskId.ToHex()]; + Assert(Context.Sender == queryTask.Creator, "No permission."); + + var designatedNodeList = GetActualDesignatedNodeList(input.DesignatedNodeList); + Assert(designatedNodeList.Value.Count >= State.MinimumOracleNodesCount.Value, + $"Invalid designated nodes count, should at least be {State.MinimumOracleNodesCount.Value}."); + + queryTask.DesignatedNodeList = input.DesignatedNodeList; + queryTask.AggregateThreshold = Math.Max(GetAggregateThreshold(designatedNodeList.Value.Count), + input.AggregateThreshold); + State.QueryTaskMap[input.TaskId.ToHex()] = queryTask; + return new Empty(); + } + + public override Hash TaskQuery(TaskQueryInput input) + { + var queryTask = State.QueryTaskMap[input.TaskId.ToHex()]; + if (queryTask == null) + { + throw new AssertionException("Query task not found."); + } + + Assert(Context.Sender == queryTask.Creator, "No permission."); + var queryInput = new QueryInput + { + Payment = queryTask.EachPayment, + AggregateThreshold = queryTask.AggregateThreshold, + AggregatorContractAddress = queryTask.AggregatorContractAddress, + CallbackInfo = queryTask.CallbackInfo, + DesignatedNodeList = queryTask.DesignatedNodeList, + QueryInfo = queryTask.QueryInfo, + Token = input.TaskId.ToHex() + }; + + return Query(queryInput); + } + private AddressList GetActualDesignatedNodeList(AddressList designatedNodeList) { if (designatedNodeList.Value.Count != 1) return designatedNodeList; @@ -172,12 +229,14 @@ public override Empty Commit(CommitInput input) var actualDesignatedNodeList = GetActualDesignatedNodeList(queryRecord.DesignatedNodeList); Assert(actualDesignatedNodeList.Value.Contains(Context.Sender), "Sender is not in designated node list."); - Assert(actualDesignatedNodeList.Value.Count >= State.MinimumOracleNodesCount.Value, "Invalid designated nodes count."); + Assert(actualDesignatedNodeList.Value.Count >= State.MinimumOracleNodesCount.Value, + "Invalid designated nodes count."); var updatedResponseCount = State.ResponseCount[input.QueryId].Add(1); State.CommitmentMap[input.QueryId][Context.Sender] = input.Commitment; - if (updatedResponseCount >= GetRevealThreshold(actualDesignatedNodeList.Value.Count, queryRecord.AggregateThreshold)) + if (updatedResponseCount >= + GetRevealThreshold(actualDesignatedNodeList.Value.Count, queryRecord.AggregateThreshold)) { // Move to next stage: Reveal queryRecord.IsSufficientCommitmentsCollected = true; @@ -230,7 +289,8 @@ public override Empty Reveal(RevealInput input) // Permission check. var actualDesignatedNodeList = GetActualDesignatedNodeList(queryRecord.DesignatedNodeList); - Assert(actualDesignatedNodeList.Value.Contains(Context.Sender), "Sender was removed from designated node list."); + Assert(actualDesignatedNodeList.Value.Contains(Context.Sender), + "Sender was removed from designated node list."); var dataHash = HashHelper.ComputeFrom(input.Data); @@ -275,7 +335,7 @@ public override Empty Reveal(RevealInput input) Assert(!helpfulNodeList.Value.Contains(Context.Sender), "Sender already revealed commitment."); helpfulNodeList.Value.Add(Context.Sender); State.HelpfulNodeListMap[input.QueryId] = helpfulNodeList; - + // Reorg helpful nodes list. helpfulNodeList = new AddressList { @@ -388,6 +448,14 @@ private void PayToNodesAndAggregateResults(QueryRecord queryRecord, AddressList OracleNodes = {queryRecord.DesignatedNodeList.Value} }); } + + // If this query is from a query task. + var maybeQueryTask = State.QueryTaskMap[queryRecord.Token]; + if (maybeQueryTask != null) + { + maybeQueryTask.ActualQueriedTimes = maybeQueryTask.ActualQueriedTimes.Add(1); + State.QueryTaskMap[queryRecord.Token] = maybeQueryTask; + } } public override Empty CancelQuery(Hash input) diff --git a/contract/AElf.Contracts.Oracle/OracleContractState.cs b/contract/AElf.Contracts.Oracle/OracleContractState.cs index 992e83a6..f1a6cd27 100644 --- a/contract/AElf.Contracts.Oracle/OracleContractState.cs +++ b/contract/AElf.Contracts.Oracle/OracleContractState.cs @@ -43,5 +43,7 @@ public partial class OracleContractState : ContractState public MappedState LockedTokenFromAddressMap { get; set; } public BoolState IsChargeFee { get; set; } + + public MappedState QueryTaskMap { get; set; } } } \ No newline at end of file diff --git a/contract/AElf.Contracts.Oracle/OracleContract_Constant.cs b/contract/AElf.Contracts.Oracle/OracleContract_Constant.cs index c31dbaa9..fe53841a 100644 --- a/contract/AElf.Contracts.Oracle/OracleContract_Constant.cs +++ b/contract/AElf.Contracts.Oracle/OracleContract_Constant.cs @@ -13,5 +13,7 @@ public partial class OracleContract private const int DefaultAggregateThreshold = 1; private const int DefaultMinimumOracleNodesCount = 3; + + private const string NotSetCallbackInfo = "NotSetCallbackInfo"; } } \ No newline at end of file diff --git a/protobuf/oracle_contract.proto b/protobuf/oracle_contract.proto index 284dc4e4..02a2650b 100644 --- a/protobuf/oracle_contract.proto +++ b/protobuf/oracle_contract.proto @@ -23,6 +23,12 @@ service OracleContract { rpc Reveal (RevealInput) returns (google.protobuf.Empty) {} rpc CancelQuery (aelf.Hash) returns (google.protobuf.Empty) {} + + rpc CreateQueryTask (CreateQueryTaskInput) returns (aelf.Hash) {} + + rpc TaskQuery (TaskQueryInput) returns (aelf.Hash) {} + + rpc CompleteQueryTask (CompleteQueryTaskInput) returns (google.protobuf.Empty) {} // controller @@ -95,6 +101,25 @@ message QueryInput { string token = 7; } +message CreateQueryTaskInput { + int64 each_payment = 1; + int32 supposed_query_times = 2; + QueryInfo query_info = 3; + google.protobuf.Timestamp end_time = 4; + CallbackInfo callback_info = 5; + aelf.Address aggregator_contract_address = 6; +} + +message TaskQueryInput { + aelf.Hash task_id = 1; +} + +message CompleteQueryTaskInput { + aelf.Hash task_id = 1; + AddressList designated_node_list = 2; + int32 aggregate_threshold = 3; +} + message CallbackInfo { aelf.Address contract_address = 1; string method_name = 2; @@ -124,6 +149,19 @@ message QueryRecord { DataRecords data_records = 17; } +message QueryTask { + int64 each_payment = 1; + int32 supposed_query_times = 2; + QueryInfo query_info = 3; + google.protobuf.Timestamp end_time = 4; + AddressList designated_node_list = 5; + aelf.Address aggregator_contract_address = 6; + int32 actual_queried_times = 7; + CallbackInfo callback_info = 8; + aelf.Address creator = 9; + int32 aggregate_threshold = 10; +} + message CommitInput { aelf.Hash query_id = 1; aelf.Hash commitment = 2;