Skip to content

Commit

Permalink
Sketch idea: Creator enforces unique kitty dna
Browse files Browse the repository at this point in the history
  • Loading branch information
JoshOrndorff committed Apr 18, 2024
1 parent e0c4dc7 commit b72621a
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions wardrobe/kitties/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
money = { default-features = false, path = "../money" }
parity-scale-codec = { features = [ "derive" ], workspace = true }
scale-info = { features = [ "derive" ], workspace = true }
serde = { features = [ "derive" ], workspace = true }
Expand All @@ -24,4 +25,5 @@ std = [
"sp-std/std",
"sp-core/std",
"serde/std",
"money/std",
]
2 changes: 2 additions & 0 deletions wardrobe/kitties/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ use tuxedo_core::{
SimpleConstraintChecker, Verifier,
};

mod minting;

#[cfg(test)]
mod tests;

Expand Down
121 changes: 121 additions & 0 deletions wardrobe/kitties/src/minting.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//! A constraint checker that allows kitties to be minted. Anyone can mint a kitty out of thin air
//! for a flat fee. Each minted kitty is guaranteed to have unique DNA (assuming Blake2 is collision resistant).

use super::*;
use money::Coin;

/// The Lord said, "Let their be kitties to frolic upon the chain."
/// "Let each kitty be unique with its own unique DNA."
/// The Lord endowed his servant thusly, "Let the plebs create kitties of their own accord,
/// such that each new kitty's DNA be the hash of a sequential nonce."
/// "But let any pleb who creates a kitty with arbitrary DNA be banished from the chain."
/// And the Lord saw that there were kitties and it was good.
/// There was morning, and there was evening, and there was frolicking. The Eighth day.
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Encode, Decode, Debug, TypeInfo)]
pub struct UniversalKittyCreator {
next_nonce: u32,
}

impl UtxoData for UniversalKittyCreator {
const TYPE_ID: [u8; 4] = *b"ctcr";
}

#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Encode, Decode, Debug, TypeInfo)]
pub enum KittyMintingError {
BadlyTyped,
InsufficientFee,
UniversalCreatorNotSupplied,
UniversalCreatorNotUpdatedCorrectly,
TooManyOutputs,
MintedKittyInvalid,
}

/// The fee to mint a kitty. For serious use this should be in a config trait or in storage.
const MINT_FEE: u128 = 10;

#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Encode, Decode, Debug, TypeInfo)]
/// A constraint checker that allows minting a kitty for a fixed (hard-coded) fee.
///
/// Inputs:
/// * One or more coins whose value equals or exceeds the fee
///
/// Evicted Inputs:
/// * The universal creator
///
/// Outputs:
/// * The universal creator, in the first position
/// * The new kitty in the second position
/// * TODO Change coins
pub struct MintKitty;

impl SimpleConstraintChecker for MintKitty {
type Error = KittyMintingError;

fn check(
&self,
input_data: &[DynamicallyTypedData],
evicted_input_data: &[DynamicallyTypedData],
_peek_data: &[DynamicallyTypedData],
output_data: &[DynamicallyTypedData],
) -> Result<TransactionPriority, Self::Error> {
// Ensure the fee is supplied.
// Currently no change is given, so the rest can be used for priority.
let mut total_input_value: u128 = 0;

for input in input_data {
let utxo_value = input
.extract::<Coin<0>>()
.map_err(|_| KittyMintingError::BadlyTyped)?
.0;
total_input_value += utxo_value;
}
ensure!(
total_input_value >= MINT_FEE,
KittyMintingError::InsufficientFee
);
let priority = (total_input_value - MINT_FEE) as u64;

// Ensure the Creator is evicted, updated, and output properly.
ensure!(
evicted_input_data.len() == 1,
KittyMintingError::UniversalCreatorNotSupplied
);
let input_creator = evicted_input_data[0]
.extract::<UniversalKittyCreator>()
.map_err(|_| KittyMintingError::BadlyTyped)?;

ensure!(
output_data.len() > 0,
KittyMintingError::UniversalCreatorNotUpdatedCorrectly
);
let output_creator = output_data[0]
.extract::<UniversalKittyCreator>()
.map_err(|_| KittyMintingError::BadlyTyped)?;

ensure!(
output_creator.next_nonce == input_creator.next_nonce + 1,
KittyMintingError::UniversalCreatorNotUpdatedCorrectly
);

// Ensure the new kitty was created properly.
ensure!(output_data.len() > 1, KittyMintingError::MintedKittyInvalid);
let minted_kitty = output_data[1]
.extract::<KittyData>()
.map_err(|_| KittyMintingError::BadlyTyped)?;

ensure!(
minted_kitty.dna == KittyDNA(BlakeTwo256::hash_of(&input_creator.next_nonce)),
KittyMintingError::MintedKittyInvalid
);

// TODO You may want to assert other things about the kitty that was created here.
// For example its gender and numbers of breedings.

// Ensure no extra outputs.
// TODO ideally we would allow change coins at this point.
ensure!(output_data.len() == 2, KittyMintingError::TooManyOutputs);

// Ensure the new kitty (and nothing)
Ok(priority)
}
}

0 comments on commit b72621a

Please sign in to comment.