1pragma solidity >=0.0;
2import "../Oracles/Oracle.sol";
3
4
5/// @title Majority oracle contract - Allows to resolve an event based on multiple oracles with majority vote
6/// @author Stefan George - <stefan@gnosis.pm>
7contract MajorityOracle is Oracle {
8
9    /*
10     *  Storage
11     */
12    Oracle[] public oracles;
13
14    /*
15     *  Public functions
16     */
17    /// @dev Allows to create an oracle for a majority vote based on other oracles
18    /// @param _oracles List of oracles taking part in the majority vote
19    constructor(Oracle[] memory _oracles)
20    {
21        // At least 2 oracles should be defined
22        require(_oracles.length > 2);
23        for (uint i = 0; i < _oracles.length; i++)
24            // Oracle address cannot be null
25            require(address(_oracles[i]) != address(0));
26        oracles = _oracles;
27    }
28
29    /// @dev Allows to registers oracles for a majority vote
30    /// @return outcomeSet Is outcome set?
31    /// @return outcome Outcome
32    function getStatusAndOutcome()
33        public
34        view
35        returns (bool outcomeSet, int outcome)
36    {
37        uint i;
38        int[] memory outcomes = new int[](oracles.length);
39        uint[] memory validations = new uint[](oracles.length);
40        for (i = 0; i < oracles.length; i++)
41            if (oracles[i].isOutcomeSet()) {
42                int _outcome = oracles[i].getOutcome();
43                for (uint j = 0; j <= i; j++)
44                    if (_outcome == outcomes[j]) {
45                        validations[j] += 1;
46                        break;
47                    }
48                    else if (validations[j] == 0) {
49                        outcomes[j] = _outcome;
50                        validations[j] = 1;
51                        break;
52                    }
53            }
54        uint outcomeValidations = 0;
55        uint outcomeIndex = 0;
56        for (i = 0; i < oracles.length; i++)
57            if (validations[i] > outcomeValidations) {
58                outcomeValidations = validations[i];
59                outcomeIndex = i;
60            }
61        // There is a majority vote
62        if (outcomeValidations * 2 > oracles.length) {
63            outcomeSet = true;
64            outcome = outcomes[outcomeIndex];
65        }
66    }
67
68    /// @dev Returns if winning outcome is set
69    /// @return Is outcome set?
70    function isOutcomeSet()
71        public
72        override
73        view
74        returns (bool)
75    {
76        (bool outcomeSet, ) = getStatusAndOutcome();
77        return outcomeSet;
78    }
79
80    /// @dev Returns winning outcome
81    /// @return Outcome
82    function getOutcome()
83        public
84        override
85        view
86        returns (int)
87    {
88        (, int winningOutcome) = getStatusAndOutcome();
89        return winningOutcome;
90    }
91}
92