1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#![deny(missing_docs)]

//! # Types for interacting with the Essential Server.

use std::collections::BTreeMap;

use essential_types::{
    contract::Contract,
    predicate::Predicate,
    solution::{Solution, SolutionData, SolutionDataIndex},
    ContentAddress, Key, PredicateAddress, StateReadBytecode, Value,
};

const ZEROED_PREDICATE: PredicateAddress = PredicateAddress {
    contract: ContentAddress([0; 32]),
    predicate: ContentAddress([0; 32]),
};

pub mod ser;

/// Utility and gas used as a result of checking a solution's state transitions.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
pub struct CheckSolutionOutput {
    /// The utility of the solution.
    pub utility: f64,
    /// The gas used by the solution.
    pub gas: u64,
}

/// The outcome of a solution, that is:
/// - Utility if the solution was included in a block.
/// - Failure reason if solution failed constraint checking or was not composable with other solutions.
/// This may be a stringified `SolutionFailReason`.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
pub enum SolutionOutcome {
    /// The solution was successful and included in a block.
    Success(u64),
    /// The solution failed and was not included in a block.
    Fail(String),
}

/// Solution with contract read from storage that will be used for checking.
#[derive(serde::Serialize, serde::Deserialize)]
pub struct CheckSolution {
    /// The solution to check.
    pub solution: Solution,
    /// The contracts this solution depends on.
    pub contracts: Vec<Contract>,
}

/// Query the results of running an ordered list of state read programs.
///
/// The query can be derived from a solution, or be inline.
/// The request type can be for just the keys and values read, or for the slots read
/// or both.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
pub struct QueryStateReads {
    /// The programs that read state.
    #[serde(
        serialize_with = "essential_types::serde::bytecode::serialize_vec",
        deserialize_with = "essential_types::serde::bytecode::deserialize_vec"
    )]
    pub state_read: Vec<StateReadBytecode>,
    /// The index of the solution data that is used for the state query,
    pub index: SolutionDataIndex,
    /// The solution for this query.
    pub solution: Solution,
    /// The type of results for this request.
    pub request_type: StateReadRequestType,
}

/// The type of results for this request.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
pub enum StateReadRequestType {
    /// Request the keys and values that are read with the state slots.
    All(SlotsRequest),
    /// Request only the slots that are read into.
    Slots(SlotsRequest),
    /// Request only the keys and values that are read.
    Reads,
}

/// The slots that are returned for the state read request.
#[derive(Default, Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
pub enum SlotsRequest {
    /// Return both the pre and post state slots.
    #[default]
    All,
    /// Return only the pre state slots.
    Pre,
    /// Return only the post state slots.
    Post,
}

/// The output of a state read query.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
pub enum QueryStateReadsOutput {
    #[serde(
        serialize_with = "ser::serialize_map",
        deserialize_with = "ser::deserialize_map"
    )]
    /// The keys and values that were read.
    Reads(BTreeMap<ContentAddress, BTreeMap<Key, Value>>),
    /// The slots that were read into.
    Slots(Slots),
    /// The keys and values that were read and the slots that were read into.
    All(
        #[serde(
            serialize_with = "ser::serialize_map",
            deserialize_with = "ser::deserialize_map"
        )]
        BTreeMap<ContentAddress, BTreeMap<Key, Value>>,
        Slots,
    ),
    /// The state reads failed.
    Failure(String),
}

/// Pre and post state slots.
#[derive(Default, Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
pub struct Slots {
    /// The pre state slots.
    pub pre: Vec<Value>,
    /// The post state slots. Read after the mutations are applied.
    pub post: Vec<Value>,
}

impl QueryStateReads {
    /// Create a query from a solution and a predicate.
    ///
    /// It is assumed the provided predicate is the predicate that the solution data
    /// at the provided index is solving. This is not checked.
    pub fn from_solution(
        mut solution: Solution,
        index: SolutionDataIndex,
        predicate: &Predicate,
        request_type: StateReadRequestType,
    ) -> Self {
        for (i, d) in solution.data.iter_mut().enumerate() {
            if i as SolutionDataIndex == index {
                continue;
            }
            d.decision_variables = Default::default();
        }
        Self {
            state_read: predicate.state_read.clone(),
            index,
            solution,
            request_type,
        }
    }

    /// Create a query that only reads external state.
    /// The predicate address is zeroed out.
    pub fn inline_empty(
        state_read: Vec<StateReadBytecode>,
        request_type: StateReadRequestType,
    ) -> Self {
        let data = SolutionData {
            predicate_to_solve: ZEROED_PREDICATE,
            decision_variables: Default::default(),
            transient_data: Default::default(),
            state_mutations: Default::default(),
        };

        Self::inline(state_read, data, request_type)
    }

    /// Create an inline query from state reads and a single solution data.
    pub fn inline(
        state_read: Vec<StateReadBytecode>,
        data: SolutionData,
        request_type: StateReadRequestType,
    ) -> Self {
        Self {
            state_read,
            index: 0,
            solution: Solution { data: vec![data] },
            request_type,
        }
    }
}

impl Default for StateReadRequestType {
    fn default() -> Self {
        Self::All(SlotsRequest::default())
    }
}