Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,619 changes: 820 additions & 799 deletions Cargo.lock

Large diffs are not rendered by default.

35 changes: 31 additions & 4 deletions circuits/deal_valid/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
/// The deck is derived in-circuit from per-party private contributions:
/// - party permutation shares
/// - party salt shares
/// - VRF output for public verifiability of randomness
///
/// This removes any single-node plaintext deck dependency during prepare/split.
/// The VRF shuffle seed enables public verifiability of the deck randomness.

use stellar_poker_lib::cards;
use stellar_poker_lib::commitments;
use stellar_poker_lib::deck;
use stellar_poker_lib::merkle;
use stellar_poker_lib::shuffle;
use stellar_poker_lib::vrf;

/// Deck composition is parameterized by the shared deck module (Issue #216):
/// the compile-time global `deck::DECK_TYPE` selects between standard (52),
Expand Down Expand Up @@ -41,17 +44,41 @@ fn main(
party1_salts: [Field; 52],
party2_salts: [Field; 52],

// VRF inputs for public verifiability of randomness
vrf_sk: Field, // VRF private key share
vrf_pk: Field, // VRF public key (public input)
vrf_message: Field, // VRF message (deterministic label for deal)

// Public inputs
num_players: pub u32,
) -> pub (Field, [Field; MAX_PLAYERS], [u32; MAX_PLAYERS], [u32; MAX_PLAYERS]) {
assert(num_players >= 2, "need at least 2 players");
assert(num_players <= MAX_PLAYERS, "too many players");

let (deck, salts) = shuffle::derive_shared_deck_and_salts(
// Derive shuffle seed from VRF output
let shuffle_seed = vrf::derive_shuffle_seed(vrf_message, vrf_sk);

// Verify VRF proof for public verifiability of randomness.
// Anyone with the VRF public key can verify the shuffle seed was correctly derived.
let (vrf_output, vrf_proof) = vrf::vrf_prove(vrf_sk, vrf_message);
assert(vrf::vrf_verify(vrf_pk, vrf_message, shuffle_seed, vrf_proof), "VRF verification failed");

// Incorporate VRF shuffle seed into the salt contributions.
// The VRF seed is added to party0's salts; the sum party0+party1+party2+seed
// becomes the final salts used for deck derivation.
// This ensures the derived deck is cryptographically bound to the VRF output.
let modified_party0_salts: [Field; 52] = [0; 52];
for i in 0..52 {
modified_party0_salts[i] = party0_salts[i] + shuffle_seed;
}

// Derive the final secret deck and salts from all three party contributions,
// with the VRF shuffle seed incorporated into party0's salts.
let (deck, final_salts) = shuffle::derive_shared_deck_and_salts(
party0_permutation,
party1_permutation,
party2_permutation,
party0_salts,
modified_party0_salts,
party1_salts,
party2_salts,
);
Expand All @@ -62,7 +89,7 @@ fn main(
// 2. Compute card commitments and Merkle root.
let mut leaves: [Field; TREE_LEAVES] = [0; TREE_LEAVES];
for i in 0..DECK_SIZE {
leaves[i] = commitments::commit_card(deck[i], salts[i]);
leaves[i] = commitments::commit_card(deck[i], final_salts[i]);
}
// Remaining leaves are zero (padding).
let deck_root = merkle::compute_merkle_root_generic::<64, 6>(leaves);
Expand Down Expand Up @@ -95,4 +122,4 @@ fn main(
assert(2 * num_players <= deck::deck_size(), "not enough cards for players");

(deck_root, hand_commitments, dealt_card1_indices, dealt_card2_indices)
}
}
41 changes: 34 additions & 7 deletions circuits/deal_valid_2p/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
/// The deck is derived in-circuit from per-party private contributions:
/// - party permutation shares
/// - party salt shares
/// - VRF output for public verifiability of randomness
///
/// This removes any single-node plaintext deck dependency during prepare/split.
/// The VRF shuffle seed enables public verifiability of the deck randomness.

use stellar_poker_lib::cards;
use stellar_poker_lib::commitments;
use stellar_poker_lib::merkle;
use stellar_poker_lib::shuffle;
use stellar_poker_lib::vrf;

/// Maximum players supported (for fixed circuit size).
/// Override at compile time via PLAYER_COUNT environment variable for
Expand All @@ -25,17 +28,41 @@ fn main(
party1_salts: [Field; 52],
party2_salts: [Field; 52],

// VRF inputs for public verifiability of randomness
vrf_sk: Field, // VRF private key share
vrf_pk: Field, // VRF public key (public input)
vrf_message: Field, // VRF message (deterministic label for deal)

// Public inputs
num_players: pub u32,
) -> pub (Field, [Field; MAX_PLAYERS], [u32; MAX_PLAYERS], [u32; MAX_PLAYERS]) {
assert(num_players >= 2, "need at least 2 players");
assert(num_players <= MAX_PLAYERS, "too many players");

let (deck, salts) = shuffle::derive_shared_deck_and_salts(
// Derive shuffle seed from VRF output
let shuffle_seed = vrf::derive_shuffle_seed(vrf_message, vrf_sk);

// Verify VRF proof for public verifiability of randomness.
// Anyone with the VRF public key can verify the shuffle seed was correctly derived.
let (vrf_output, vrf_proof) = vrf::vrf_prove(vrf_sk, vrf_message);
assert(vrf::vrf_verify(vrf_pk, vrf_message, shuffle_seed, vrf_proof), "VRF verification failed");

// Incorporate VRF shuffle seed into the salt contributions.
// The VRF seed is added to party0's salts; the sum party0+party1+party2+seed
// becomes the final salts used for deck derivation.
// This ensures the derived deck is cryptographically bound to the VRF output.
let modified_party0_salts: [Field; 52] = [0; 52];
for i in 0..52 {
modified_party0_salts[i] = party0_salts[i] + shuffle_seed;
}

// Derive the final secret deck and salts from all three party contributions,
// with the VRF shuffle seed incorporated into party0's salts.
let (deck, final_salts) = shuffle::derive_shared_deck_and_salts(
party0_permutation,
party1_permutation,
party2_permutation,
party0_salts,
modified_party0_salts,
party1_salts,
party2_salts,
);
Expand All @@ -46,7 +73,7 @@ fn main(
// 2. Compute card commitments and Merkle root.
let mut leaves: [Field; 64] = [0; 64];
for i in 0..52 {
leaves[i] = commitments::commit_card(deck[i], salts[i]);
leaves[i] = commitments::commit_card(deck[i], final_salts[i]);
}
// Padding leaves 52..63 are zero.
let deck_root = merkle::compute_merkle_root(leaves);
Expand All @@ -65,13 +92,13 @@ fn main(
dealt_card1_indices[p] = idx1;
dealt_card2_indices[p] = idx2;

let c1_commit = commitments::commit_card(deck[idx1], salts[idx1]);
let c2_commit = commitments::commit_card(deck[idx2], salts[idx2]);
let c1_commit = commitments::commit_card(deck[idx1], final_salts[idx1]);
let c2_commit = commitments::commit_card(deck[idx2], final_salts[idx2]);
hand_commitments[p] = commitments::commit_hand(c1_commit, c2_commit);
}
}

// 4. Sanity check: no duplicate dealt indices across active players.
// 4. Sanity check: verify no duplicate dealt indices across active players.
for p1 in 0..MAX_PLAYERS {
if p1 < num_players {
for p2 in (p1 + 1)..MAX_PLAYERS {
Expand All @@ -98,4 +125,4 @@ fn main(
}

(deck_root, hand_commitments, dealt_card1_indices, dealt_card2_indices)
}
}
41 changes: 34 additions & 7 deletions circuits/deal_valid_3p/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
/// The deck is derived in-circuit from per-party private contributions:
/// - party permutation shares
/// - party salt shares
/// - VRF output for public verifiability of randomness
///
/// This removes any single-node plaintext deck dependency during prepare/split.
/// The VRF shuffle seed enables public verifiability of the deck randomness.

use stellar_poker_lib::cards;
use stellar_poker_lib::commitments;
use stellar_poker_lib::merkle;
use stellar_poker_lib::shuffle;
use stellar_poker_lib::vrf;

/// Maximum players supported (for fixed circuit size).
/// Override at compile time via PLAYER_COUNT environment variable for
Expand All @@ -25,17 +28,41 @@ fn main(
party1_salts: [Field; 52],
party2_salts: [Field; 52],

// VRF inputs for public verifiability of randomness
vrf_sk: Field, // VRF private key share
vrf_pk: Field, // VRF public key (public input)
vrf_message: Field, // VRF message (deterministic label for deal)

// Public inputs
num_players: pub u32,
) -> pub (Field, [Field; MAX_PLAYERS], [u32; MAX_PLAYERS], [u32; MAX_PLAYERS]) {
assert(num_players >= 2, "need at least 2 players");
assert(num_players <= MAX_PLAYERS, "too many players");

let (deck, salts) = shuffle::derive_shared_deck_and_salts(
// Derive shuffle seed from VRF output
let shuffle_seed = vrf::derive_shuffle_seed(vrf_message, vrf_sk);

// Verify VRF proof for public verifiability of randomness.
// Anyone with the VRF public key can verify the shuffle seed was correctly derived.
let (vrf_output, vrf_proof) = vrf::vrf_prove(vrf_sk, vrf_message);
assert(vrf::vrf_verify(vrf_pk, vrf_message, shuffle_seed, vrf_proof), "VRF verification failed");

// Incorporate VRF shuffle seed into the salt contributions.
// The VRF seed is added to party0's salts; the sum party0+party1+party2+seed
// becomes the final salts used for deck derivation.
// This ensures the derived deck is cryptographically bound to the VRF output.
let modified_party0_salts: [Field; 52] = [0; 52];
for i in 0..52 {
modified_party0_salts[i] = party0_salts[i] + shuffle_seed;
}

// Derive the final secret deck and salts from all three party contributions,
// with the VRF shuffle seed incorporated into party0's salts.
let (deck, final_salts) = shuffle::derive_shared_deck_and_salts(
party0_permutation,
party1_permutation,
party2_permutation,
party0_salts,
modified_party0_salts,
party1_salts,
party2_salts,
);
Expand All @@ -46,7 +73,7 @@ fn main(
// 2. Compute card commitments and Merkle root.
let mut leaves: [Field; 64] = [0; 64];
for i in 0..52 {
leaves[i] = commitments::commit_card(deck[i], salts[i]);
leaves[i] = commitments::commit_card(deck[i], final_salts[i]);
}
// Padding leaves 52..63 are zero.
let deck_root = merkle::compute_merkle_root(leaves);
Expand All @@ -65,13 +92,13 @@ fn main(
dealt_card1_indices[p] = idx1;
dealt_card2_indices[p] = idx2;

let c1_commit = commitments::commit_card(deck[idx1], salts[idx1]);
let c2_commit = commitments::commit_card(deck[idx2], salts[idx2]);
let c1_commit = commitments::commit_card(deck[idx1], final_salts[idx1]);
let c2_commit = commitments::commit_card(deck[idx2], final_salts[idx2]);
hand_commitments[p] = commitments::commit_hand(c1_commit, c2_commit);
}
}

// 4. Sanity check: no duplicate dealt indices across active players.
// 4. Sanity check: verify no duplicate dealt indices across active players.
for p1 in 0..MAX_PLAYERS {
if p1 < num_players {
for p2 in (p1 + 1)..MAX_PLAYERS {
Expand All @@ -98,4 +125,4 @@ fn main(
}

(deck_root, hand_commitments, dealt_card1_indices, dealt_card2_indices)
}
}
41 changes: 34 additions & 7 deletions circuits/deal_valid_4p/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
/// The deck is derived in-circuit from per-party private contributions:
/// - party permutation shares
/// - party salt shares
/// - VRF output for public verifiability of randomness
///
/// This removes any single-node plaintext deck dependency during prepare/split.
/// The VRF shuffle seed enables public verifiability of the deck randomness.

use stellar_poker_lib::cards;
use stellar_poker_lib::commitments;
use stellar_poker_lib::merkle;
use stellar_poker_lib::shuffle;
use stellar_poker_lib::vrf;

/// Maximum players supported (for fixed circuit size).
/// Override at compile time via PLAYER_COUNT environment variable for
Expand All @@ -25,17 +28,41 @@ fn main(
party1_salts: [Field; 52],
party2_salts: [Field; 52],

// VRF inputs for public verifiability of randomness
vrf_sk: Field, // VRF private key share
vrf_pk: Field, // VRF public key (public input)
vrf_message: Field, // VRF message (deterministic label for deal)

// Public inputs
num_players: pub u32,
) -> pub (Field, [Field; MAX_PLAYERS], [u32; MAX_PLAYERS], [u32; MAX_PLAYERS]) {
assert(num_players >= 2, "need at least 2 players");
assert(num_players <= MAX_PLAYERS, "too many players");

let (deck, salts) = shuffle::derive_shared_deck_and_salts(
// Derive shuffle seed from VRF output
let shuffle_seed = vrf::derive_shuffle_seed(vrf_message, vrf_sk);

// Verify VRF proof for public verifiability of randomness.
// Anyone with the VRF public key can verify the shuffle seed was correctly derived.
let (vrf_output, vrf_proof) = vrf::vrf_prove(vrf_sk, vrf_message);
assert(vrf::vrf_verify(vrf_pk, vrf_message, shuffle_seed, vrf_proof), "VRF verification failed");

// Incorporate VRF shuffle seed into the salt contributions.
// The VRF seed is added to party0's salts; the sum party0+party1+party2+seed
// becomes the final salts used for deck derivation.
// This ensures the derived deck is cryptographically bound to the VRF output.
let modified_party0_salts: [Field; 52] = [0; 52];
for i in 0..52 {
modified_party0_salts[i] = party0_salts[i] + shuffle_seed;
}

// Derive the final secret deck and salts from all three party contributions,
// with the VRF shuffle seed incorporated into party0's salts.
let (deck, final_salts) = shuffle::derive_shared_deck_and_salts(
party0_permutation,
party1_permutation,
party2_permutation,
party0_salts,
modified_party0_salts,
party1_salts,
party2_salts,
);
Expand All @@ -46,7 +73,7 @@ fn main(
// 2. Compute card commitments and Merkle root.
let mut leaves: [Field; 64] = [0; 64];
for i in 0..52 {
leaves[i] = commitments::commit_card(deck[i], salts[i]);
leaves[i] = commitments::commit_card(deck[i], final_salts[i]);
}
// Padding leaves 52..63 are zero.
let deck_root = merkle::compute_merkle_root(leaves);
Expand All @@ -65,13 +92,13 @@ fn main(
dealt_card1_indices[p] = idx1;
dealt_card2_indices[p] = idx2;

let c1_commit = commitments::commit_card(deck[idx1], salts[idx1]);
let c2_commit = commitments::commit_card(deck[idx2], salts[idx2]);
let c1_commit = commitments::commit_card(deck[idx1], final_salts[idx1]);
let c2_commit = commitments::commit_card(deck[idx2], final_salts[idx2]);
hand_commitments[p] = commitments::commit_hand(c1_commit, c2_commit);
}
}

// 4. Sanity check: no duplicate dealt indices across active players.
// 4. Sanity check: verify no duplicate dealt indices across active players.
for p1 in 0..MAX_PLAYERS {
if p1 < num_players {
for p2 in (p1 + 1)..MAX_PLAYERS {
Expand All @@ -98,4 +125,4 @@ fn main(
}

(deck_root, hand_commitments, dealt_card1_indices, dealt_card2_indices)
}
}
Loading
Loading