Skip to content

Instantly share code, notes, and snippets.

@shubham-kanodia
Created October 25, 2023 16:33
Show Gist options
  • Save shubham-kanodia/f7a9d7f1baf852ce49fedbacfbc0a881 to your computer and use it in GitHub Desktop.
Save shubham-kanodia/f7a9d7f1baf852ce49fedbacfbc0a881 to your computer and use it in GitHub Desktop.
Magic Square: Local Deployment

Deployment and Execution Steps

Code:

// The 'magic_square' program.
program magic_square.aleo {
    
    // State
    // Public
    mapping puzzles: u64 => u8;
    mapping scores: address => u64;

    // Data Structure
    struct Board {
        r1c1: u8,
        r1c2: u8,
        r1c3: u8,
        r2c1: u8,
        r2c3: u8,
        r3c1: u8,
        r3c2: u8,
        r3c3: u8
    }

    record Token {
        owner: address,
        amount: u64
    }

    record Solution {
        owner: address,
        solution: Board
    }

    transition add_puzzle(public goal: u8) {
        assert_eq(self.caller, aleo1fu0k2qfytzs5fhesgfgjuax6wsh9xx4ftpdapnhzrtruy0tx3urqx3p0ut);
        // assert(goal >= 12u8 && goal <= 15u8);
        return then finalize(goal);
    }

    finalize add_puzzle(goal: u8) {
        assert(goal >= 12u8 && goal <= 15u8);
        
        // Calculate mapping key from the goal
        let key: u64 = BHP256::hash_to_u64(goal);
        // Set the key, goal in the puzzles mapping
        Mapping::set(puzzles, key, goal);
    }

    transition get_puzzle_id(goal: u8) -> u64 {
        let key: u64 = BHP256::hash_to_u64(goal);
        return key;
    }

    function check_unique(solution: Board) {
        assert_neq(solution.r1c1, solution.r1c2);
        assert_neq(solution.r1c1, solution.r1c3);
        assert_neq(solution.r1c1, solution.r2c1);
        assert_neq(solution.r1c1, solution.r2c3);
        assert_neq(solution.r1c1, solution.r3c1);
        assert_neq(solution.r1c1, solution.r3c2);
        assert_neq(solution.r1c1, solution.r3c3);
        assert_neq(solution.r1c2, solution.r1c3);
        assert_neq(solution.r1c2, solution.r2c1);
        assert_neq(solution.r1c2, solution.r2c3);
        assert_neq(solution.r1c2, solution.r3c1);
        assert_neq(solution.r1c2, solution.r3c2);
        assert_neq(solution.r1c2, solution.r3c3);
        assert_neq(solution.r1c3, solution.r2c1);
        assert_neq(solution.r1c3, solution.r2c3);
        assert_neq(solution.r1c3, solution.r3c1);
        assert_neq(solution.r1c3, solution.r3c2);
        assert_neq(solution.r1c3, solution.r3c3);
        assert_neq(solution.r2c1, solution.r2c3);
        assert_neq(solution.r2c1, solution.r3c1);
        assert_neq(solution.r2c1, solution.r3c2);
        assert_neq(solution.r2c1, solution.r3c3);
        assert_neq(solution.r2c3, solution.r3c1);
        assert_neq(solution.r2c3, solution.r3c2);
        assert_neq(solution.r2c3, solution.r3c3);
        assert_neq(solution.r3c1, solution.r3c2);
        assert_neq(solution.r3c1, solution.r3c3);
        assert_neq(solution.r3c2, solution.r3c3);
    }

    function check_range(solution: Board) {
        assert(solution.r1c1 < 9u8);
        assert(solution.r1c1 > 0u8);
        assert(solution.r1c2 < 9u8);
        assert(solution.r1c2 > 0u8);
        assert(solution.r1c3 < 9u8);
        assert(solution.r1c3 > 0u8);
        assert(solution.r2c1 < 9u8);
        assert(solution.r2c1 > 0u8);
        assert(solution.r2c3 < 9u8);
        assert(solution.r2c3 > 0u8);
        assert(solution.r3c1 < 9u8);
        assert(solution.r3c1 > 0u8);
        assert(solution.r3c2 < 9u8);
        assert(solution.r3c2 > 0u8);
        assert(solution.r3c3 < 9u8);
        assert(solution.r3c3 > 0u8);
    }

    function check_correctness(solution: Board, goal: u8) {
        assert_eq(solution.r1c1 + solution.r1c2 + solution.r1c3, goal);    
        assert_eq(solution.r3c1 + solution.r3c2 + solution.r3c3, goal);    
        assert_eq(solution.r1c1 + solution.r2c1 + solution.r3c1, goal);    
        assert_eq(solution.r1c3 + solution.r2c3 + solution.r3c3, goal);
    }

    transition add_solution(puzzle_id: u64, goal: u8, solution: Board) -> (Token, Solution) {
        // Checks if the solution is correct
        check_correctness(solution, goal);
        
        // Check if solution is unique
        check_unique(solution);

        // Check range
        check_range(solution);

        let token: Token = Token {
            owner: self.caller,
            amount: 100u64
        };

        let solution_record: Solution = Solution {
            owner: self.caller,
            solution: solution
        };

        return (token, solution_record) then finalize(puzzle_id, goal, self.caller);    
    }

    finalize add_solution(puzzle_id: u64, goal: u8, caller: address) {
        // Use get_or_use first
        // Get the goal from the mapping
        let exists: bool = Mapping::contains(puzzles, puzzle_id);
        assert(exists);
        let goal_from_mapping: u8 = Mapping::get(puzzles, puzzle_id);
        assert_eq(goal, goal_from_mapping);
        
        // Increase score of player
        let initial_score: u64 = Mapping::get_or_use(scores, caller, 0u64);
        Mapping::set(scores, caller, initial_score + 1u64);
    }
}

Accounts Used:

Development Node Account

Private Key  APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH

View Key  AViewKey1mSnpFFC8Mj4fXbK5YiWgZ3mjiV8CxA79bYNa8ymUpTrw

Address  aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px

Developer Account

Private Key  APrivateKey1zkpHVhTAJiZPrDeVo6nDyvq2LDRhP2ZgECvr8zqtcefpgsc

View Key  AViewKey1mzaT1mopkL7TnVxUr6yhmF7AyXPm8DCNk1tkHEEk7Zso

Address  aleo1fu0k2qfytzs5fhesgfgjuax6wsh9xx4ftpdapnhzrtruy0tx3urqx3p0ut

Installation Steps

  • Clone the snarkos repo
git clone git@github.com:AleoHQ/snarkOS.git
  • Checkout the correct version
git checkout 7970ea752ea73ae234f749dd395006e3e231f256
  • Install
cargo install --path .
  • Downgrade leo binary zip from here

  • Rename the leo binary to distinguish it from the newer (or) existing version of leo you already have

  • Place it in a directory included in global path. Replace the destination URL accordingly, an example of how the command would look like is given below

mv leo_old /Users/shubham/.cargo/bin/

Steps

  1. Start a local development node
snarkos start --nodisplay --dev 0 --beacon
  1. Scan for blocks produced by beacon node (You can skip this step as the records always stay the same)
snarkos developer scan -v AViewKey1mSnpFFC8Mj4fXbK5YiWgZ3mjiV8CxA79bYNa8ymUpTrw --start 0 --endpoint "http://localhost:3030"
  1. Transfer credits to our developer account
snarkos developer execute credits.aleo transfer_private "{  owner: aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px.private,  microcredits: 93750000000000u64.private,  _nonce: 4198387698914179364419054715404502698072077412312631840798412024722684186646group.public}" "aleo1fu0k2qfytzs5fhesgfgjuax6wsh9xx4ftpdapnhzrtruy0tx3urqx3p0ut" 83750000000000u64 --private-key "APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH" --query "http://localhost:3030" --broadcast "http://localhost:3030/testnet3/transaction/broadcast" --fee 10000000000 --record "{  owner: aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px.private,  microcredits: 93750000000000u64.private,  _nonce: 6526741553655730916585406598622172875143600009335802058713184455273822212848group.public}"
  1. Once transferred, scan the last 100 blocks for unspent records. Copy the unspent record. You will have to run this program each time after making a transaction to get an unspent record
snarkos developer scan --view-key AViewKey1mzaT1mopkL7TnVxUr6yhmF7AyXPm8DCNk1tkHEEk7Zso --private-key APrivateKey1zkpHVhTAJiZPrDeVo6nDyvq2LDRhP2ZgECvr8zqtcefpgsc --last 100 --endpoint "http://localhost:3030"
  1. Deploy the program
snarkos developer deploy magic_square.aleo --private-key APrivateKey1zkpHVhTAJiZPrDeVo6nDyvq2LDRhP2ZgECvr8zqtcefpgsc --query "http://localhost:3030" --path "./build/" --broadcast "http://localhost:3030/testnet3/transaction/broadcast" --fee 600000 --record "UNSPENT_RECORD"
  1. View the program API
import requests

host = "0.0.0.0"
program_name = "magic_square.aleo"
endpoint = f"http://{host}:3030/testnet3/program/{program_name}"

response = requests.get(endpoint)

content = response.content.decode('utf-8').strip('"').split("\\n")

print(endpoint)

for line in content:
    print(line)
  1. Add a puzzle
snarkos developer execute magic_square.aleo add_puzzle "12u8" --private-key APrivateKey1zkpHVhTAJiZPrDeVo6nDyvq2LDRhP2ZgECvr8zqtcefpgsc --query "http://localhost:3030" --broadcast "http://localhost:3030/testnet3/transaction/broadcast" --fee 600000 --record "UNSPENT_RECORD"
  1. Check the mapping
import requests

host = "0.0.0.0"
program_id = "magic_square_1byx.aleo"
mapping_name = "puzzles"
mapping_key = "67691673676804187u64"

endpoint = f"http://{host}:3030/testnet3/program/{program_id}/mapping/{mapping_name}/{mapping_key}"

result = requests.get(endpoint)
print(endpoint)
print(result.content)
  1. Add Solution
snarkos developer execute magic_square.aleo add_solution "67691673676804187u64" "12u8" "{r1c1: 1u8, r1c2: 8u8, r1c3: 3u8, r2c1: 5u8, r2c3: 7u8, r3c1: 6u8, r3c2: 4u8, r3c3: 2u8}" --private-key APrivateKey1zkpHVhTAJiZPrDeVo6nDyvq2LDRhP2ZgECvr8zqtcefpgsc --query "http://localhost:3030" --broadcast "http://localhost:3030/testnet3/transaction/broadcast" --fee 600000 --record "UNSPENT_RECORD"
  1. Transaction View API
import requests

host = "0.0.0.0"
transaction_id = "at1xmqcyuwd6xv5x9z6tn3j7ee572yhzrvhnhg9kscgl96mu8l47ygqrcps6m"

endpoint = f"http://{host}:3030/testnet3/transaction/{transaction_id}"

result = requests.get(endpoint)
print(endpoint)
print(result.content)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment