Skip to content

Commit

Permalink
Part 1 of changes for reaching and testing slash on chain
Browse files Browse the repository at this point in the history
  • Loading branch information
prozacchiwawa committed Jan 9, 2025
1 parent dc80749 commit 156bcaa
Show file tree
Hide file tree
Showing 13 changed files with 168 additions and 55 deletions.
2 changes: 1 addition & 1 deletion clsp/calpoker_include_calpoker_factory.hex

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion clsp/calpoker_include_calpoker_factory_hash.hex
Original file line number Diff line number Diff line change
@@ -1 +1 @@
a0f093ebbb30e63030a4f582d99b87160a3e5b0996eacb3d4e79da782a1f99cd73
a01b087e04f9be63c39fecefa2d4b203d1d34f340c820b684948f9f193b73307f0
2 changes: 1 addition & 1 deletion clsp/calpoker_include_calpoker_template.hex

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion clsp/calpoker_include_calpoker_template_hash.hex
Original file line number Diff line number Diff line change
@@ -1 +1 @@
a08a51b40fc76cb43b94c5cb1bea47088727f4954ea9360f9afb9e78e9265fc957
a0e5fd8bef31613b8bdd72f5f3099a3b51d2b648c8cd468149498939f4fae068f4
2 changes: 1 addition & 1 deletion clsp/onchain/calpoker/a.clsp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
; move is alice commit to a salted word
; evidence is empty
; ME below is the object whose hash is mod_hash
(export (mod_hash (move new_validation_hash max_move_size mover_share previous_validation_hash
(export (mod_hash (move new_validation_hash mover_share previous_validation_hash
mover_puzzle_hash waiter_puzzle_hash amount timeout max_move_size referee_hash)
state me mover_puzzle solution evidence)

Expand Down
2 changes: 1 addition & 1 deletion clsp/onchain/calpoker/a.hex
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ff02ffff03ffff02ffff03ffff09ff15ffff0bffff01a0d0ae87434a55a2af35392fb6563e06d617b0925d536ace533c72e512c2284a40ffff0bffff0101ff09808080ffff01ff02ffff01ff02ffff03ffff09ffff0dff0980ffff012080ffff01ff02ffff01ff09ff2dffff011080ff0180ffff01ff02ffff01ff0180ff018080ff0180ff0180ffff01ff02ffff01ff0180ff018080ff0180ffff01ff02ffff01ff0880ff0180ffff01ff02ffff01ff0180ff018080ff0180
ff02ffff03ffff02ffff03ffff09ff15ffff0bffff01a0971cabee566e2af04e8b8741cc9fde220238774d6d8a3223e522371978b5fcceffff0bffff0101ff09808080ffff01ff02ffff01ff02ffff03ffff09ffff0dff0980ffff012080ffff01ff02ffff01ff09ff820bfdffff011080ff0180ffff01ff02ffff01ff0180ff018080ff0180ff0180ffff01ff02ffff01ff0180ff018080ff0180ffff01ff02ffff01ff0880ff0180ffff01ff02ffff01ff0180ff018080ff0180
2 changes: 1 addition & 1 deletion clsp/onchain/calpoker/b.clsp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
; state is alice's commit
; move is bob's seed
; evidence is empty
(export (mod_hash (move new_validation_hash max_move_size mover_share previous_validation_hash
(export (mod_hash (move new_validation_hash mover_share previous_validation_hash
mover_puzzle_hash waiter_puzzle_hash amount timeout max_move_size referee_hash)
alice_commit me mover_puzzle solution evidence)
(if_any_fail
Expand Down
2 changes: 1 addition & 1 deletion clsp/onchain/calpoker/b.hex
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ff02ffff01ff02ffff03ffff02ffff03ffff09ff2bffff0bffff01a0142453e408bc4d8782fa52ab4f692f3bca82a781a90d6466baaee38a48a1c386ffff02ff02ffff04ff02ffff04ffff04ff17ffff04ff13ffff01808080ff808080808080ffff01ff02ffff01ff02ffff03ffff09ffff0dff1380ffff011080ffff01ff02ffff01ff09ff5bffff013080ff0180ffff01ff02ffff01ff0180ff018080ff0180ff0180ffff01ff02ffff01ff0180ff018080ff0180ffff01ff02ffff01ff0880ff0180ffff01ff02ffff01ff0180ff018080ff0180ffff04ffff01ff02ffff03ffff07ff0580ffff01ff02ffff01ff0bffff0102ffff02ff02ffff04ff02ffff04ffff05ff0580ff80808080ffff02ff02ffff04ff02ffff04ffff06ff0580ff8080808080ff0180ffff01ff02ffff01ff0bffff0101ff0580ff018080ff0180ff018080
ff02ffff01ff02ffff03ffff02ffff03ffff09ff2bffff0bffff01a0142453e408bc4d8782fa52ab4f692f3bca82a781a90d6466baaee38a48a1c386ffff02ff02ffff04ff02ffff04ffff04ff17ffff04ff13ffff01808080ff808080808080ffff01ff02ffff01ff02ffff03ffff09ffff0dff1380ffff011080ffff01ff02ffff01ff09ff8217fbffff013080ff0180ffff01ff02ffff01ff0180ff018080ff0180ff0180ffff01ff02ffff01ff0180ff018080ff0180ffff01ff02ffff01ff0880ff0180ffff01ff02ffff01ff0180ff018080ff0180ffff04ffff01ff02ffff03ffff07ff0580ffff01ff02ffff01ff0bffff0102ffff02ff02ffff04ff02ffff04ffff05ff0580ff80808080ffff02ff02ffff04ff02ffff04ffff06ff0580ff8080808080ff0180ffff01ff02ffff01ff0bffff0101ff0580ff018080ff0180ff018080
17 changes: 16 additions & 1 deletion src/channel_handler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use log::debug;

use rand::prelude::*;

use clvm_traits::ToClvm;
use clvm_traits::{ClvmEncoder, ToClvm};
use clvmr::allocator::NodePtr;

use crate::channel_handler::game_handler::TheirTurnResult;
Expand Down Expand Up @@ -892,6 +892,21 @@ impl ChannelHandler {
self.current_state_number,
)?;

// Not used along this route, but provided.
let coin_string = self.state_channel_coin().coin_string();
let slash_no_evidence = env.allocator.encode_atom(&[]).into_gen()?;
debug!("{} calling slash", self.is_initial_potato());
if let Some(_) = self.live_games[game_idx].check_their_turn_for_slash(
env.allocator,
slash_no_evidence,
coin_string,
)? {
// Slash isn't allowed in off chain, we'll go on chain via error.
return Err(Error::StrErr(
"slash when off chain: go on chain".to_string(),
));
}

debug!(
"{} their_move_result {their_move_result:?}",
self.unroll.coin.started_with_potato
Expand Down
10 changes: 10 additions & 0 deletions src/channel_handler/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -954,6 +954,16 @@ impl LiveGame {
Ok(their_move_result)
}

pub fn check_their_turn_for_slash(
&self,
allocator: &mut AllocEncoder,
evidence: NodePtr,
coin_string: &CoinString,
) -> Result<Option<TheirTurnCoinSpentResult>, Error> {
self.referee_maker
.check_their_turn_for_slash(allocator, evidence, coin_string)
}

pub fn get_rewind_outcome(&self) -> Option<usize> {
self.rewind_outcome
}
Expand Down
158 changes: 114 additions & 44 deletions src/referee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use crate::common::standard_coin::{
use crate::common::types::{
chia_dialect, u64_from_atom, usize_from_atom, Aggsig, AllocEncoder, Amount,
BrokenOutCoinSpendInfo, CoinCondition, CoinSpend, CoinString, Error, GameID, Hash, IntoErr,
Node, Program, Puzzle, PuzzleHash, RcNode, Sha256tree, Spend, Timeout,
Node, Program, Puzzle, PuzzleHash, RcNode, Sha256Input, Sha256tree, Spend, Timeout,
};

pub const REM_CONDITION_FIELDS: usize = 4;
Expand Down Expand Up @@ -95,6 +95,11 @@ pub enum TheirTurnCoinSpentResult {
},
Slash(Box<SlashOutcome>),
}
#[derive(Debug)]
pub enum ValidatorResult {
MoveOk,
Slash(NodePtr),
}

/// Adjudicates a two player turn based game
///
Expand Down Expand Up @@ -671,11 +676,26 @@ impl RefereeMaker {
initial_move.mover_share.clone()
};
// TODO: Revisit how we create initial_move
let is_hash = game_start_info
.initial_state
.sha256tree(allocator)
.hash()
.clone();
let ip_hash = game_start_info
.initial_validation_program
.sha256tree(allocator)
.hash()
.clone();
let vi_hash = Sha256Input::Array(vec![
Sha256Input::Hash(&is_hash),
Sha256Input::Hash(&ip_hash),
])
.hash();
let ref_puzzle_args = Rc::new(RefereePuzzleArgs::new(
&fixed_info,
&initial_move,
None,
&Hash::default(),
&vi_hash,
Some(&mover_share),
my_turn,
));
Expand Down Expand Up @@ -810,8 +830,16 @@ impl RefereeMaker {
&self,
) -> Result<(&Program, ValidationProgram), Error> {
match self.state.borrow() {
RefereeMakerGameState::Initial { .. } => {
Err(Error::StrErr("no moves have been made yet".to_string()))
RefereeMakerGameState::Initial {
game_handler,
initial_state,
initial_validation_program,
..
} => {
if game_handler.is_my_turn() {
return Err(Error::StrErr("no moves have been made yet".to_string()));
}
Ok((&initial_state, initial_validation_program.clone()))
}
RefereeMakerGameState::AfterOurTurn { .. } => Err(Error::StrErr(
"we accepted an our move so their move has sunsetted".to_string(),
Expand Down Expand Up @@ -931,15 +959,44 @@ impl RefereeMaker {
initial_validation_program,
initial_state,
..
} => RefereeMakerGameState::AfterTheirTurn {
game_handler: raw_game_handler.clone(),
our_turn_game_handler: raw_game_handler.clone(),
most_recent_our_state_result: initial_state.clone(),
most_recent_our_validation_program: initial_validation_program.clone(),
create_this_coin: old_args,
spend_this_coin: referee_args,
},
} => {
let is_hash = initial_state.sha256tree(allocator).hash().clone();
let ip_hash = initial_validation_program
.sha256tree(allocator)
.hash()
.clone();
let vi_hash = Sha256Input::Array(vec![
Sha256Input::Hash(&is_hash),
Sha256Input::Hash(&ip_hash),
])
.hash();
debug!("accept their move: state hash {is_hash:?}");
debug!("accept their move: valprog hash {ip_hash:?}");
debug!("accept their move: validation info hash {vi_hash:?}");
RefereeMakerGameState::AfterTheirTurn {
game_handler: raw_game_handler.clone(),
our_turn_game_handler: raw_game_handler.clone(),
most_recent_our_state_result: initial_state.clone(),
most_recent_our_validation_program: initial_validation_program.clone(),
create_this_coin: old_args,
spend_this_coin: referee_args,
}
}
RefereeMakerGameState::AfterOurTurn { my_turn_result, .. } => {
let is_hash = my_turn_result.state.sha256tree(allocator).hash().clone();
let ip_hash = my_turn_result
.validation_program
.sha256tree(allocator)
.hash()
.clone();
let vi_hash = Sha256Input::Array(vec![
Sha256Input::Hash(&is_hash),
Sha256Input::Hash(&ip_hash),
])
.hash();
debug!("accept their move: state hash {is_hash:?}");
debug!("accept their move: valprog hash {ip_hash:?}");
debug!("accept their move: validation info hash {vi_hash:?}");
RefereeMakerGameState::AfterTheirTurn {
game_handler: raw_game_handler.clone(),
most_recent_our_state_result: my_turn_result.state.clone(),
Expand Down Expand Up @@ -1431,7 +1488,7 @@ impl RefereeMaker {
&self,
allocator: &mut AllocEncoder,
evidence: NodePtr,
) -> Result<NodePtr, Error> {
) -> Result<ValidatorResult, Error> {
let previous_puzzle_args = self.args_for_this_coin();
let puzzle_args = self.spend_this_coin();
let new_puzzle_hash = curry_referee_puzzle_hash(
Expand Down Expand Up @@ -1469,9 +1526,14 @@ impl RefereeMaker {
solution: solution_program,
},
};

debug!("getting validation program");
let (_state, validation_program) = self.get_validation_program_for_their_move()?;
debug!("validation_program {validation_program:?}");
let validation_program_mod_hash = validation_program.hash();
debug!("validation_program_mod_hash {validation_program_mod_hash:?}");
let validation_program_nodeptr = validation_program.to_nodeptr(allocator)?;

let validator_full_args_node = validator_move_args.to_nodeptr(
allocator,
validation_program_nodeptr,
Expand All @@ -1490,9 +1552,12 @@ impl RefereeMaker {
validation_program_nodeptr,
validator_full_args_node,
0,
)
.into_gen()?;
Ok(result.1)
);

Ok(match result {
Ok(res) => ValidatorResult::Slash(res.1),
Err(_) => ValidatorResult::MoveOk,
})
}

pub fn their_turn_move_off_chain(
Expand Down Expand Up @@ -1587,17 +1652,21 @@ impl RefereeMaker {
&self,
allocator: &mut AllocEncoder,
state: NodePtr,
my_validation_info_hash: PuzzleHash,
validation_program_clvm: NodePtr,
slash_solution: NodePtr,
evidence: Evidence,
) -> Result<NodePtr, Error> {
(
Node(state),
(
Node(validation_program_clvm),
my_validation_info_hash,
(
self.target_puzzle_hash_for_slash(),
(Node(slash_solution), (Node(evidence.to_nodeptr()), ())),
Node(validation_program_clvm),
(
RcNode::new(self.fixed.my_identity.puzzle.to_program()),
(Node(slash_solution), (Node(evidence.to_nodeptr()), ())),
),
),
),
)
Expand Down Expand Up @@ -1633,9 +1702,11 @@ impl RefereeMaker {

let state_nodeptr = state.to_nodeptr(allocator)?;
let validation_program_node = validation_program.to_nodeptr(allocator)?;
let validation_program_hash = validation_program.sha256tree(allocator);
let slashing_coin_solution = self.slashing_coin_solution(
allocator,
state_nodeptr,
validation_program_hash,
validation_program_node,
slash_solution,
evidence,
Expand Down Expand Up @@ -1712,9 +1783,9 @@ impl RefereeMaker {
pub fn check_their_turn_for_slash(
&self,
allocator: &mut AllocEncoder,
evidence: NodePtr,
coin_string: &CoinString,
) -> Result<Option<TheirTurnCoinSpentResult>, Error> {
let evidence = allocator.allocator().null();
let puzzle_args = self.spend_this_coin();
let new_puzzle = Rc::new(curry_referee_puzzle(
allocator,
Expand All @@ -1730,18 +1801,15 @@ impl RefereeMaker {
)?;
// my_inner_solution maker is just in charge of making aggsigs from
// conditions.
let full_slash_result = self.run_validator_for_their_move(allocator, evidence);
debug!("run validator for their move");
let full_slash_result = self.run_validator_for_their_move(allocator, evidence)?;
match full_slash_result {
Ok(slash) => {
ValidatorResult::Slash(slash) => {
debug!(
"slash was allowed: {}",
disassemble(allocator.allocator(), slash, None)
);

// Ultimately each of these cases returns some kind of
// TheirTurnCoinSpentResult.
let nil_evidence = Evidence::nil(allocator);

// result is NodePtr containing solution and aggsig.
// The aggsig for the nil slash is the same as the slash
// below, having been created for the reward coin by using
Expand All @@ -1754,12 +1822,12 @@ impl RefereeMaker {
new_puzzle,
&new_puzzle_hash,
full_slash_solution,
nil_evidence,
Evidence::from_nodeptr(evidence),
&slash_spend.signature,
)
.map(Some)
}
Err(_) => Ok(None),
ValidatorResult::MoveOk => Ok(None),
}
}

Expand Down Expand Up @@ -1872,22 +1940,24 @@ impl RefereeMaker {
curry_referee_puzzle_hash(allocator, &self.fixed.referee_coin_puzzle_hash, &args)?;
debug!("THEIR TURN MOVE OFF CHAIN SUCCEEDED {new_puzzle_hash:?}\n");

let check_and_report_slash =
|allocator: &mut AllocEncoder, readable_move: NodePtr, _mover_share: Amount| {
if let Some(result) = self.check_their_turn_for_slash(allocator, coin_string)? {
Ok(result)
} else {
Ok(TheirTurnCoinSpentResult::Moved {
new_coin_string: CoinString::from_parts(
&coin_string.to_coin_id(),
&new_puzzle_hash,
&self.fixed.amount,
),
readable: ReadableMove::from_nodeptr(allocator, readable_move)?,
mover_share: args.game_move.basic.mover_share.clone(),
})
}
};
let check_and_report_slash = |allocator: &mut AllocEncoder,
readable_move: NodePtr,
_mover_share: Amount| {
let nil = allocator.encode_atom(&[]).into_gen()?;
if let Some(result) = self.check_their_turn_for_slash(allocator, nil, coin_string)? {
Ok(result)
} else {
Ok(TheirTurnCoinSpentResult::Moved {
new_coin_string: CoinString::from_parts(
&coin_string.to_coin_id(),
&new_puzzle_hash,
&self.fixed.amount,
),
readable: ReadableMove::from_nodeptr(allocator, readable_move)?,
mover_share: args.game_move.basic.mover_share.clone(),
})
}
};

debug!("referee move details {details:?}");
match result.original {
Expand Down
18 changes: 18 additions & 0 deletions src/tests/peer/potato_handler_sim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1288,3 +1288,21 @@ fn sim_test_with_peer_container_piss_off_peer_timeout() {
assert_eq!(p1_balance, 2000000000000);
assert_eq!(p1_balance, p2_balance);
}

#[test]
fn sim_test_with_peer_container_piss_off_peer_slash() {
let mut allocator = AllocEncoder::new();

let mut moves = test_moves_1(&mut allocator).to_vec();
let move_3_node = [1, 0, 1, 0, 1, 0, 1, 1]
.to_clvm(&mut allocator)
.expect("should work");
moves[3] = GameAction::Move(1, move_3_node, true);

let outcome =
run_calpoker_container_with_action_list(&mut allocator, &moves).expect("should finish");

let (p1_balance, p2_balance) = get_balances_from_outcome(&outcome).expect("should work");
assert_eq!(p1_balance, 2000000000000);
assert_eq!(p1_balance, p2_balance);
}
Loading

0 comments on commit 156bcaa

Please sign in to comment.