sawtooth_xo/handler/
mod.rs1mod game;
19mod payload;
20mod state;
21
22cfg_if! {
23 if #[cfg(target_arch = "wasm32")] {
24 use sabre_sdk::{ApplyError, TpProcessRequest, TransactionContext, TransactionHandler,};
25 } else {
26 use sawtooth_sdk::messages::processor::TpProcessRequest;
27 use sawtooth_sdk::processor::handler::{ApplyError, TransactionContext, TransactionHandler};
28 }
29}
30
31use crate::handler::game::Game;
32use crate::handler::payload::XoPayload;
33use crate::handler::state::{get_xo_prefix, XoState};
34
35pub struct XoTransactionHandler {
36 family_name: String,
37 family_versions: Vec<String>,
38 namespaces: Vec<String>,
39}
40
41impl XoTransactionHandler {
42 pub fn new() -> XoTransactionHandler {
43 XoTransactionHandler {
44 family_name: "xo".into(),
45 family_versions: vec!["1.0".into()],
46 namespaces: vec![get_xo_prefix()],
47 }
48 }
49}
50
51impl Default for XoTransactionHandler {
52 fn default() -> Self {
53 Self::new()
54 }
55}
56
57impl TransactionHandler for XoTransactionHandler {
58 fn family_name(&self) -> String {
59 self.family_name.clone()
60 }
61
62 fn family_versions(&self) -> Vec<String> {
63 self.family_versions.clone()
64 }
65
66 fn namespaces(&self) -> Vec<String> {
67 self.namespaces.clone()
68 }
69
70 fn apply(
71 &self,
72 request: &TpProcessRequest,
73 context: &mut dyn TransactionContext,
74 ) -> Result<(), ApplyError> {
75 let signer = request.get_header().get_signer_public_key();
76
77 let payload = XoPayload::new(request.get_payload())?;
78
79 let mut state = XoState::new(context);
80
81 info!(
82 "Payload: {} {} {}",
83 payload.get_name(),
84 payload.get_action(),
85 payload.get_space(),
86 );
87
88 let game = state.get_game(payload.get_name().as_str())?;
89
90 match payload.get_action().as_str() {
91 "delete" => {
92 if game.is_none() {
93 return Err(ApplyError::InvalidTransaction(String::from(
94 "Invalid action: game does not exist",
95 )));
96 }
97 state.delete_game(payload.get_name().as_str())?;
98 }
99 "create" => {
100 if game.is_none() {
101 let game = Game::new(payload.get_name());
102 state.set_game(payload.get_name().as_str(), game)?;
103 info!("Created game: {}", payload.get_name().as_str());
104 } else {
105 return Err(ApplyError::InvalidTransaction(String::from(
106 "Invalid action: Game already exists",
107 )));
108 }
109 }
110 "take" => {
111 if let Some(mut g) = game {
112 match g.get_state().as_str() {
113 "P1-WIN" | "P2-WIN" | "TIE" => {
114 return Err(ApplyError::InvalidTransaction(String::from(
115 "Invalid action: Game has ended",
116 )));
117 }
118 "P1-NEXT" => {
119 let p1 = g.get_player1();
120 if !p1.is_empty() && p1.as_str() != signer {
121 return Err(ApplyError::InvalidTransaction(String::from(
122 "Not player 2's turn",
123 )));
124 }
125 }
126 "P2-NEXT" => {
127 let p2 = g.get_player2();
128 if !p2.is_empty() && p2.as_str() != signer {
129 return Err(ApplyError::InvalidTransaction(String::from(
130 "Not player 1's turn",
131 )));
132 }
133 }
134 _ => {
135 return Err(ApplyError::InvalidTransaction(String::from(
136 "Invalid state",
137 )));
138 }
139 }
140
141 let board_chars: Vec<char> = g.get_board().chars().collect();
142 if board_chars[payload.get_space() - 1] != '-' {
143 return Err(ApplyError::InvalidTransaction(format!(
144 "Space {} is already taken",
145 payload.get_space()
146 )));
147 }
148
149 if g.get_player1().is_empty() {
150 g.set_player1(signer);
151 } else if g.get_player2().is_empty() {
152 g.set_player2(signer)
153 }
154
155 g.mark_space(payload.get_space())?;
156 g.update_state()?;
157
158 g.display();
159
160 state.set_game(payload.get_name().as_str(), g)?;
161 } else {
162 return Err(ApplyError::InvalidTransaction(String::from(
163 "Invalid action: Take requires an existing game",
164 )));
165 }
166 }
167 other_action => {
168 return Err(ApplyError::InvalidTransaction(format!(
169 "Invalid action: '{}'",
170 other_action
171 )));
172 }
173 }
174
175 Ok(())
176 }
177}
178
179#[cfg(target_arch = "wasm32")]
180pub fn apply(
182 request: &TpProcessRequest,
183 context: &mut dyn TransactionContext,
184) -> Result<bool, ApplyError> {
185 let handler = XoTransactionHandler::new();
186 match handler.apply(request, context) {
187 Ok(_) => Ok(true),
188 Err(err) => {
189 info!("{}", err);
190 Err(err)
191 }
192 }
193}