Skip to content
Open
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
56 changes: 30 additions & 26 deletions docs/ArkadeKitties.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ function computeChildGeneration(sireGenerationBE8: bytes8, dameGenerationBE8: by
// Contract 1: Commits to a breeding pair and a secret salt.
// This creates a temporary UTXO locked with the BreedRevealContract script.
contract BreedCommit(
assetId speciesControlId,
bytes32 speciesControlIdTxid,
int speciesControlIdGidx,
script feeScript, // A generic script for the fee output
int fee, // The required fee to prevent spam
pubkey oracle // The public key of the oracle to be used for the reveal
Expand All @@ -147,8 +148,8 @@ contract BreedCommit(
) {
function commit(
// Sire & Dame details
sireId: assetId, sireGenome: bytes32, sireGenerationBE8: bytes8, script sireOwner,
dameId: assetId, dameGenome: bytes32, dameGenerationBE8: bytes8, script dameOwner,
sireIdTxid: bytes32, sireIdGidx: int, sireGenome: bytes32, sireGenerationBE8: bytes8, script sireOwner,
dameIdTxid: bytes32, dameIdGidx: int, dameGenome: bytes32, dameGenerationBE8: bytes8, script dameOwner,
// A secret salt from the user, hashed
saltHash: bytes32,
// The output index for the reveal UTXO
Expand All @@ -163,20 +164,20 @@ contract BreedCommit(
// 1. Verify a fee is paid to the designated fee script
require(tx.outputs[feeOutputIndex].scriptPubKey == feeScript, "Fee output script mismatch");
require(tx.outputs[feeOutputIndex].value >= fee, "Fee not paid");
require(tx.outputs[revealOutputIndex].assets.lookup(speciesControlId) == 1, "Species Control not locked in reveal output");
require(tx.outputs[revealOutputIndex].assets.lookup(sireId) == 1, "Sire not locked in reveal output");
require(tx.outputs[revealOutputIndex].assets.lookup(dameId) == 1, "Dame not locked in reveal output");
require(tx.outputs[revealOutputIndex].assets.lookup(speciesControlIdTxid, speciesControlIdGidx) == 1, "Species Control not locked in reveal output");
require(tx.outputs[revealOutputIndex].assets.lookup(sireIdTxid, sireIdGidx) == 1, "Sire not locked in reveal output");
require(tx.outputs[revealOutputIndex].assets.lookup(dameIdTxid, dameIdGidx) == 1, "Dame not locked in reveal output");
// 2. Verify parent assets are present and valid
let sireGroup = tx.assetGroups.find(sireId);
let dameGroup = tx.assetGroups.find(dameId);
let sireGroup = tx.assetGroups.find(sireIdTxid, sireIdGidx);
let dameGroup = tx.assetGroups.find(dameIdTxid, dameIdGidx);
require(sireGroup != null && dameGroup != null, "Sire and Dame assets must be spent");
require(sireGroup.control == speciesControlId, "Sire not Species-Controlled");
require(dameGroup.control == speciesControlId, "Dame not Species-Controlled");
require(sireGroup.controlIs(speciesControlIdTxid, speciesControlIdGidx), "Sire not Species-Controlled");
Comment on lines +171 to +174

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove stale null-guard examples after find(txid, gidx).

Line 171, Line 246, and Line 281 use find(...) followed by != null checks. Under the new success-flag semantics, find already verifies presence; these null checks are obsolete and can misdocument runtime behavior.

Suggested doc fix
-        let sireGroup = tx.assetGroups.find(sireIdTxid, sireIdGidx);
-        let dameGroup = tx.assetGroups.find(dameIdTxid, dameIdGidx);
-        require(sireGroup != null && dameGroup != null, "Sire and Dame assets must be spent");
+        let sireGroup = tx.assetGroups.find(sireIdTxid, sireIdGidx);
+        let dameGroup = tx.assetGroups.find(dameIdTxid, dameIdGidx);

-        let speciesGroup = tx.assetGroups.find(speciesControlIdTxid, speciesControlIdGidx);
-        require(speciesGroup != null && speciesGroup.delta == 0, "Species Control must be present and retained");
+        let speciesGroup = tx.assetGroups.find(speciesControlIdTxid, speciesControlIdGidx);
+        require(speciesGroup.delta == 0, "Species Control must be present and retained");

-        let newKittyGroup = tx.assetGroups.find(newKittyIdTxid, newKittyIdGidx);
-        require(newKittyGroup != null, "New Kitty asset group not found");
+        let newKittyGroup = tx.assetGroups.find(newKittyIdTxid, newKittyIdGidx);

-        let speciesGroup = tx.assetGroups.find(speciesControlIdTxid, speciesControlIdGidx);
-        require(speciesGroup != null && speciesGroup.delta == 0, "Species Control must be retained");
+        let speciesGroup = tx.assetGroups.find(speciesControlIdTxid, speciesControlIdGidx);
+        require(speciesGroup.delta == 0, "Species Control must be retained");

Also applies to: 246-253, 281-282

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/ArkadeKitties.md` around lines 171 - 174, Remove the obsolete null-guard
checks that follow the find(txid, gidx) method calls in the documentation
examples. The find method under the new success-flag semantics already verifies
asset group presence, so the != null comparisons in the require statements at
lines 171, 246, and 281 are unnecessary. Delete these null checks from the
require statements while keeping the rest of the validation logic intact (such
as the controlIs checks that follow them).

require(dameGroup.controlIs(speciesControlIdTxid, speciesControlIdGidx), "Dame not Species-Controlled");
require(sireGroup.metadataHash == computeKittyMetadataRoot(sireGenome, sireGenerationBE8), "Sire metadata hash mismatch");
require(dameGroup.metadataHash == computeKittyMetadataRoot(dameGenome, dameGenerationBE8), "Dame metadata hash mismatch");

// 2. Verify Species Control asset is present and retained
let speciesGroup = tx.assetGroups.find(speciesControlId);
let speciesGroup = tx.assetGroups.find(speciesControlIdTxid, speciesControlIdGidx);
require(speciesGroup != null && speciesGroup.delta == 0, "Species Control must be present and retained");

// 3. Construct the reveal script and enforce its creation
Expand All @@ -186,9 +187,10 @@ contract BreedCommit(
// with this exact script, which it reconstructs here for verification.

Script revealScript = new BreedReveal(
speciesControlId,
speciesControlIdTxid, speciesControlIdGidx,
oracle,
sireId, dameId,
sireIdTxid, sireIdGidx,
dameIdTxid, dameIdGidx,
sireGenome, sireGenerationBE8,
dameGenome, dameGenerationBE8,
saltHash,
Expand All @@ -207,9 +209,10 @@ contract BreedCommit(
// Contract 2: Spends the commit UTXO, verifies oracle randomness, and creates the new Kitty.
contract BreedReveal(
// Note: All parameters are now baked into the contract's script at creation time.
assetId speciesControlId,
bytes32 speciesControlIdTxid, int speciesControlIdGidx,
pubkey oracle,
assetId sireId, assetId dameId,
bytes32 sireIdTxid, int sireIdGidx,
bytes32 dameIdTxid, int dameIdGidx,
bytes32 sireGenome, bytes8 sireGenerationBE8,
bytes32 dameGenome, bytes8 dameGenerationBE8,
bytes32 saltHash,
Expand All @@ -223,8 +226,9 @@ contract BreedReveal(
// Oracle provides randomness and a signature
oracleRand: bytes32,
oracleSig: signature,
// The assetId of the new Kitty being created
newKittyId: assetId,
// The explicit Asset ID operands of the new Kitty being created
newKittyIdTxid: bytes32,
newKittyIdGidx: int,
kittyOutputIndex: int,
sireOutputIndex: int,
dameOutputIndex: int,
Expand All @@ -239,17 +243,17 @@ contract BreedReveal(
require(checkDataSig(oracleSig, sha256(commitOutpoint + oracleRand), oracle), "Invalid oracle signature");

// 3. Verify Species Control is present and retained (delta == 0)
let speciesGroup = tx.assetGroups.find(speciesControlId);
let speciesGroup = tx.assetGroups.find(speciesControlIdTxid, speciesControlIdGidx);
require(speciesGroup != null && speciesGroup.delta == 0, "Species Control must be present and retained");
require(tx.outputs[speciesControlOutputIndex].assets.lookup(speciesControlId) == 1, "Species Control not in output");
require(tx.outputs[speciesControlOutputIndex].assets.lookup(speciesControlIdTxid, speciesControlIdGidx) == 1, "Species Control not in output");

// 4. Find the new Kitty's asset group
let newKittyGroup = tx.assetGroups.find(newKittyId);
let newKittyGroup = tx.assetGroups.find(newKittyIdTxid, newKittyIdGidx);
require(newKittyGroup != null, "New Kitty asset group not found");
require(newKittyGroup.isFresh && newKittyGroup.delta == 1, "Child must be a fresh NFT");
require(newKittyGroup.control == speciesControlId, "Child not Species-Controlled");
require(newKittyGroup.controlIs(speciesControlIdTxid, speciesControlIdGidx), "Child not Species-Controlled");
let newKittyOutput = tx.outputs[kittyOutputIndex];
require(newKittyOutput.assets.lookup(newKittyId) == 1, "New Kitty not locked in output");
require(newKittyOutput.assets.lookup(newKittyIdTxid, newKittyIdGidx) == 1, "New Kitty not locked in output");
require(newKittyOutput.scriptPubKey == newKittyOwner, "New Kitty must be sent to a P2PKH address");

// 5. Generate the unpredictable genome and expected metadata hash
Expand All @@ -268,15 +272,15 @@ contract BreedReveal(
require(tx.locktime >= expirationTime, "Timeout not yet reached");

// 2. Verify parents are returned to their owners
require(tx.outputs[sireOutputIndex].assets.lookup(sireId) == 1, "Sire not refunded");
require(tx.outputs[sireOutputIndex].assets.lookup(sireIdTxid, sireIdGidx) == 1, "Sire not refunded");
require(tx.outputs[sireOutputIndex].scriptPubKey == sireOwner, "Sire not refunded to owner");
require(tx.outputs[dameOutputIndex].assets.lookup(dameId) == 1, "Dame not refunded");
require(tx.outputs[dameOutputIndex].assets.lookup(dameIdTxid, dameIdGidx) == 1, "Dame not refunded");
require(tx.outputs[dameOutputIndex].scriptPubKey == dameOwner, "Dame not refunded to owner");

// 3. Verify Species Control is retained (delta == 0)
let speciesGroup = tx.assetGroups.find(speciesControlId);
let speciesGroup = tx.assetGroups.find(speciesControlIdTxid, speciesControlIdGidx);
require(speciesGroup != null && speciesGroup.delta == 0, "Species Control must be retained");
require(tx.outputs[speciesControlOutputIndex].assets.lookup(speciesControlId) == 1, "Species Control not in output");
require(tx.outputs[speciesControlOutputIndex].assets.lookup(speciesControlIdTxid, speciesControlIdGidx) == 1, "Species Control not in output");
}

}
Expand Down
Loading
Loading