diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 886cee84..87e5308e 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -65,7 +65,7 @@ jobs: python3 - <<'PY' >> "$GITHUB_OUTPUT" import json # Crates that produce FFI bindings - crates = ["algokit_transact", "algokit_crypto"] + crates = ["algokit_transact", "algokit_crypto", "algokit_algo25"] items = [] for crate in crates: pascal = ''.join(p.capitalize() for p in crate.split('_')) diff --git a/.github/workflows/swift_ci.yml b/.github/workflows/swift_ci.yml index c3423a5c..8e8e4543 100644 --- a/.github/workflows/swift_ci.yml +++ b/.github/workflows/swift_ci.yml @@ -57,8 +57,43 @@ jobs: run: cargo pkg ${{ env.CRATE }} swift # Ideally we'd use a matrix for the platforms, but due to the limitations of Mac runners on GitHub it's probably better to just have a single job with multiple steps + - name: Detect iOS simulator + id: ios_sim + run: | + DEST_ID="$(python3 - <<'PY' + import json, subprocess, re + + data = json.loads(subprocess.check_output( + ["xcrun", "simctl", "list", "devices", "available", "-j"], + text=True + )) + + def runtime_version(key: str): + # Example key: com.apple.CoreSimulator.SimRuntime.iOS-18-2 + m = re.search(r"iOS-(\d+)-(\d+)", key) + return (int(m.group(1)), int(m.group(2))) if m else (-1, -1) + + ios_runtimes = [k for k in data["devices"].keys() if "SimRuntime.iOS-" in k] + ios_runtimes.sort(key=runtime_version, reverse=True) + + udid = None + for runtime in ios_runtimes: + devices = data["devices"][runtime] + iphones = [d for d in devices if d.get("isAvailable") and d.get("name", "").startswith("iPhone")] + if iphones: + udid = iphones[0]["udid"] + break + + if not udid: + raise SystemExit("No available iPhone simulator found") + + print(udid) + PY + )" + echo "destination=id=${DEST_ID}" >> "$GITHUB_OUTPUT" + - name: Test (iOS) - run: cd packages/swift/${{ env.PACKAGE }} && xcodebuild -scheme ${{ env.PACKAGE }} test -destination "platform=iOS Simulator,name=iPhone 17,OS=latest" + run: cd packages/swift/${{ env.PACKAGE }} && xcodebuild -scheme ${{ env.PACKAGE }} test -destination "${{ steps.ios_sim.outputs.destination }}" - name: Test (macOS) run: cd packages/swift/${{ env.PACKAGE }} && xcodebuild -scheme ${{ env.PACKAGE }} test -destination "platform=macOS" - name: Test (Catalyst) @@ -74,4 +109,3 @@ jobs: with: name: ${{ env.PACKAGE }} path: packages/swift/${{ env.PACKAGE }}.zip - diff --git a/Cargo.lock b/Cargo.lock index 922d7876..f65e3cf8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,6 +67,21 @@ dependencies = [ "snafu", ] +[[package]] +name = "algokit_algo25" +version = "0.1.0" +dependencies = [ + "sha2", +] + +[[package]] +name = "algokit_algo25_ffi" +version = "0.1.0" +dependencies = [ + "algokit_algo25", + "uniffi", +] + [[package]] name = "algokit_crypto" version = "0.1.0" @@ -85,6 +100,7 @@ version = "0.1.0" dependencies = [ "algokit_crypto", "async-trait", + "getrandom 0.4.1", "signature", "tokio", "uniffi", diff --git a/Cargo.toml b/Cargo.toml index 7d423543..7536d155 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,8 @@ members = [ "tools/api_tools", "crates/algokit_crypto", "crates/algokit_crypto_ffi", + "crates/algokit_algo25", + "crates/algokit_algo25_ffi", ] [workspace.dependencies] diff --git a/crates/algokit_algo25/Cargo.toml b/crates/algokit_algo25/Cargo.toml new file mode 100644 index 00000000..f3becb5c --- /dev/null +++ b/crates/algokit_algo25/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "algokit_algo25" +version = "0.1.0" +edition = "2024" + +[dependencies] +sha2 = { workspace = true } diff --git a/crates/algokit_algo25/src/english.rs b/crates/algokit_algo25/src/english.rs new file mode 100644 index 00000000..c53eb5b5 --- /dev/null +++ b/crates/algokit_algo25/src/english.rs @@ -0,0 +1,213 @@ +pub const ENGLISH: &[&str] = &[ + "abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract", "absurd", + "abuse", "access", "accident", "account", "accuse", "achieve", "acid", "acoustic", "acquire", + "across", "act", "action", "actor", "actress", "actual", "adapt", "add", "addict", "address", + "adjust", "admit", "adult", "advance", "advice", "aerobic", "affair", "afford", "afraid", + "again", "age", "agent", "agree", "ahead", "aim", "air", "airport", "aisle", "alarm", "album", + "alcohol", "alert", "alien", "all", "alley", "allow", "almost", "alone", "alpha", "already", + "also", "alter", "always", "amateur", "amazing", "among", "amount", "amused", "analyst", + "anchor", "ancient", "anger", "angle", "angry", "animal", "ankle", "announce", "annual", + "another", "answer", "antenna", "antique", "anxiety", "any", "apart", "apology", "appear", + "apple", "approve", "april", "arch", "arctic", "area", "arena", "argue", "arm", "armed", + "armor", "army", "around", "arrange", "arrest", "arrive", "arrow", "art", "artefact", "artist", + "artwork", "ask", "aspect", "assault", "asset", "assist", "assume", "asthma", "athlete", + "atom", "attack", "attend", "attitude", "attract", "auction", "audit", "august", "aunt", + "author", "auto", "autumn", "average", "avocado", "avoid", "awake", "aware", "away", "awesome", + "awful", "awkward", "axis", "baby", "bachelor", "bacon", "badge", "bag", "balance", "balcony", + "ball", "bamboo", "banana", "banner", "bar", "barely", "bargain", "barrel", "base", "basic", + "basket", "battle", "beach", "bean", "beauty", "because", "become", "beef", "before", "begin", + "behave", "behind", "believe", "below", "belt", "bench", "benefit", "best", "betray", "better", + "between", "beyond", "bicycle", "bid", "bike", "bind", "biology", "bird", "birth", "bitter", + "black", "blade", "blame", "blanket", "blast", "bleak", "bless", "blind", "blood", "blossom", + "blouse", "blue", "blur", "blush", "board", "boat", "body", "boil", "bomb", "bone", "bonus", + "book", "boost", "border", "boring", "borrow", "boss", "bottom", "bounce", "box", "boy", + "bracket", "brain", "brand", "brass", "brave", "bread", "breeze", "brick", "bridge", "brief", + "bright", "bring", "brisk", "broccoli", "broken", "bronze", "broom", "brother", "brown", + "brush", "bubble", "buddy", "budget", "buffalo", "build", "bulb", "bulk", "bullet", "bundle", + "bunker", "burden", "burger", "burst", "bus", "business", "busy", "butter", "buyer", "buzz", + "cabbage", "cabin", "cable", "cactus", "cage", "cake", "call", "calm", "camera", "camp", "can", + "canal", "cancel", "candy", "cannon", "canoe", "canvas", "canyon", "capable", "capital", + "captain", "car", "carbon", "card", "cargo", "carpet", "carry", "cart", "case", "cash", + "casino", "castle", "casual", "cat", "catalog", "catch", "category", "cattle", "caught", + "cause", "caution", "cave", "ceiling", "celery", "cement", "census", "century", "cereal", + "certain", "chair", "chalk", "champion", "change", "chaos", "chapter", "charge", "chase", + "chat", "cheap", "check", "cheese", "chef", "cherry", "chest", "chicken", "chief", "child", + "chimney", "choice", "choose", "chronic", "chuckle", "chunk", "churn", "cigar", "cinnamon", + "circle", "citizen", "city", "civil", "claim", "clap", "clarify", "claw", "clay", "clean", + "clerk", "clever", "click", "client", "cliff", "climb", "clinic", "clip", "clock", "clog", + "close", "cloth", "cloud", "clown", "club", "clump", "cluster", "clutch", "coach", "coast", + "coconut", "code", "coffee", "coil", "coin", "collect", "color", "column", "combine", "come", + "comfort", "comic", "common", "company", "concert", "conduct", "confirm", "congress", + "connect", "consider", "control", "convince", "cook", "cool", "copper", "copy", "coral", + "core", "corn", "correct", "cost", "cotton", "couch", "country", "couple", "course", "cousin", + "cover", "coyote", "crack", "cradle", "craft", "cram", "crane", "crash", "crater", "crawl", + "crazy", "cream", "credit", "creek", "crew", "cricket", "crime", "crisp", "critic", "crop", + "cross", "crouch", "crowd", "crucial", "cruel", "cruise", "crumble", "crunch", "crush", "cry", + "crystal", "cube", "culture", "cup", "cupboard", "curious", "current", "curtain", "curve", + "cushion", "custom", "cute", "cycle", "dad", "damage", "damp", "dance", "danger", "daring", + "dash", "daughter", "dawn", "day", "deal", "debate", "debris", "decade", "december", "decide", + "decline", "decorate", "decrease", "deer", "defense", "define", "defy", "degree", "delay", + "deliver", "demand", "demise", "denial", "dentist", "deny", "depart", "depend", "deposit", + "depth", "deputy", "derive", "describe", "desert", "design", "desk", "despair", "destroy", + "detail", "detect", "develop", "device", "devote", "diagram", "dial", "diamond", "diary", + "dice", "diesel", "diet", "differ", "digital", "dignity", "dilemma", "dinner", "dinosaur", + "direct", "dirt", "disagree", "discover", "disease", "dish", "dismiss", "disorder", "display", + "distance", "divert", "divide", "divorce", "dizzy", "doctor", "document", "dog", "doll", + "dolphin", "domain", "donate", "donkey", "donor", "door", "dose", "double", "dove", "draft", + "dragon", "drama", "drastic", "draw", "dream", "dress", "drift", "drill", "drink", "drip", + "drive", "drop", "drum", "dry", "duck", "dumb", "dune", "during", "dust", "dutch", "duty", + "dwarf", "dynamic", "eager", "eagle", "early", "earn", "earth", "easily", "east", "easy", + "echo", "ecology", "economy", "edge", "edit", "educate", "effort", "egg", "eight", "either", + "elbow", "elder", "electric", "elegant", "element", "elephant", "elevator", "elite", "else", + "embark", "embody", "embrace", "emerge", "emotion", "employ", "empower", "empty", "enable", + "enact", "end", "endless", "endorse", "enemy", "energy", "enforce", "engage", "engine", + "enhance", "enjoy", "enlist", "enough", "enrich", "enroll", "ensure", "enter", "entire", + "entry", "envelope", "episode", "equal", "equip", "era", "erase", "erode", "erosion", "error", + "erupt", "escape", "essay", "essence", "estate", "eternal", "ethics", "evidence", "evil", + "evoke", "evolve", "exact", "example", "excess", "exchange", "excite", "exclude", "excuse", + "execute", "exercise", "exhaust", "exhibit", "exile", "exist", "exit", "exotic", "expand", + "expect", "expire", "explain", "expose", "express", "extend", "extra", "eye", "eyebrow", + "fabric", "face", "faculty", "fade", "faint", "faith", "fall", "false", "fame", "family", + "famous", "fan", "fancy", "fantasy", "farm", "fashion", "fat", "fatal", "father", "fatigue", + "fault", "favorite", "feature", "february", "federal", "fee", "feed", "feel", "female", + "fence", "festival", "fetch", "fever", "few", "fiber", "fiction", "field", "figure", "file", + "film", "filter", "final", "find", "fine", "finger", "finish", "fire", "firm", "first", + "fiscal", "fish", "fit", "fitness", "fix", "flag", "flame", "flash", "flat", "flavor", "flee", + "flight", "flip", "float", "flock", "floor", "flower", "fluid", "flush", "fly", "foam", + "focus", "fog", "foil", "fold", "follow", "food", "foot", "force", "forest", "forget", "fork", + "fortune", "forum", "forward", "fossil", "foster", "found", "fox", "fragile", "frame", + "frequent", "fresh", "friend", "fringe", "frog", "front", "frost", "frown", "frozen", "fruit", + "fuel", "fun", "funny", "furnace", "fury", "future", "gadget", "gain", "galaxy", "gallery", + "game", "gap", "garage", "garbage", "garden", "garlic", "garment", "gas", "gasp", "gate", + "gather", "gauge", "gaze", "general", "genius", "genre", "gentle", "genuine", "gesture", + "ghost", "giant", "gift", "giggle", "ginger", "giraffe", "girl", "give", "glad", "glance", + "glare", "glass", "glide", "glimpse", "globe", "gloom", "glory", "glove", "glow", "glue", + "goat", "goddess", "gold", "good", "goose", "gorilla", "gospel", "gossip", "govern", "gown", + "grab", "grace", "grain", "grant", "grape", "grass", "gravity", "great", "green", "grid", + "grief", "grit", "grocery", "group", "grow", "grunt", "guard", "guess", "guide", "guilt", + "guitar", "gun", "gym", "habit", "hair", "half", "hammer", "hamster", "hand", "happy", + "harbor", "hard", "harsh", "harvest", "hat", "have", "hawk", "hazard", "head", "health", + "heart", "heavy", "hedgehog", "height", "hello", "helmet", "help", "hen", "hero", "hidden", + "high", "hill", "hint", "hip", "hire", "history", "hobby", "hockey", "hold", "hole", "holiday", + "hollow", "home", "honey", "hood", "hope", "horn", "horror", "horse", "hospital", "host", + "hotel", "hour", "hover", "hub", "huge", "human", "humble", "humor", "hundred", "hungry", + "hunt", "hurdle", "hurry", "hurt", "husband", "hybrid", "ice", "icon", "idea", "identify", + "idle", "ignore", "ill", "illegal", "illness", "image", "imitate", "immense", "immune", + "impact", "impose", "improve", "impulse", "inch", "include", "income", "increase", "index", + "indicate", "indoor", "industry", "infant", "inflict", "inform", "inhale", "inherit", + "initial", "inject", "injury", "inmate", "inner", "innocent", "input", "inquiry", "insane", + "insect", "inside", "inspire", "install", "intact", "interest", "into", "invest", "invite", + "involve", "iron", "island", "isolate", "issue", "item", "ivory", "jacket", "jaguar", "jar", + "jazz", "jealous", "jeans", "jelly", "jewel", "job", "join", "joke", "journey", "joy", "judge", + "juice", "jump", "jungle", "junior", "junk", "just", "kangaroo", "keen", "keep", "ketchup", + "key", "kick", "kid", "kidney", "kind", "kingdom", "kiss", "kit", "kitchen", "kite", "kitten", + "kiwi", "knee", "knife", "knock", "know", "lab", "label", "labor", "ladder", "lady", "lake", + "lamp", "language", "laptop", "large", "later", "latin", "laugh", "laundry", "lava", "law", + "lawn", "lawsuit", "layer", "lazy", "leader", "leaf", "learn", "leave", "lecture", "left", + "leg", "legal", "legend", "leisure", "lemon", "lend", "length", "lens", "leopard", "lesson", + "letter", "level", "liar", "liberty", "library", "license", "life", "lift", "light", "like", + "limb", "limit", "link", "lion", "liquid", "list", "little", "live", "lizard", "load", "loan", + "lobster", "local", "lock", "logic", "lonely", "long", "loop", "lottery", "loud", "lounge", + "love", "loyal", "lucky", "luggage", "lumber", "lunar", "lunch", "luxury", "lyrics", "machine", + "mad", "magic", "magnet", "maid", "mail", "main", "major", "make", "mammal", "man", "manage", + "mandate", "mango", "mansion", "manual", "maple", "marble", "march", "margin", "marine", + "market", "marriage", "mask", "mass", "master", "match", "material", "math", "matrix", + "matter", "maximum", "maze", "meadow", "mean", "measure", "meat", "mechanic", "medal", "media", + "melody", "melt", "member", "memory", "mention", "menu", "mercy", "merge", "merit", "merry", + "mesh", "message", "metal", "method", "middle", "midnight", "milk", "million", "mimic", "mind", + "minimum", "minor", "minute", "miracle", "mirror", "misery", "miss", "mistake", "mix", "mixed", + "mixture", "mobile", "model", "modify", "mom", "moment", "monitor", "monkey", "monster", + "month", "moon", "moral", "more", "morning", "mosquito", "mother", "motion", "motor", + "mountain", "mouse", "move", "movie", "much", "muffin", "mule", "multiply", "muscle", "museum", + "mushroom", "music", "must", "mutual", "myself", "mystery", "myth", "naive", "name", "napkin", + "narrow", "nasty", "nation", "nature", "near", "neck", "need", "negative", "neglect", + "neither", "nephew", "nerve", "nest", "net", "network", "neutral", "never", "news", "next", + "nice", "night", "noble", "noise", "nominee", "noodle", "normal", "north", "nose", "notable", + "note", "nothing", "notice", "novel", "now", "nuclear", "number", "nurse", "nut", "oak", + "obey", "object", "oblige", "obscure", "observe", "obtain", "obvious", "occur", "ocean", + "october", "odor", "off", "offer", "office", "often", "oil", "okay", "old", "olive", "olympic", + "omit", "once", "one", "onion", "online", "only", "open", "opera", "opinion", "oppose", + "option", "orange", "orbit", "orchard", "order", "ordinary", "organ", "orient", "original", + "orphan", "ostrich", "other", "outdoor", "outer", "output", "outside", "oval", "oven", "over", + "own", "owner", "oxygen", "oyster", "ozone", "pact", "paddle", "page", "pair", "palace", + "palm", "panda", "panel", "panic", "panther", "paper", "parade", "parent", "park", "parrot", + "party", "pass", "patch", "path", "patient", "patrol", "pattern", "pause", "pave", "payment", + "peace", "peanut", "pear", "peasant", "pelican", "pen", "penalty", "pencil", "people", + "pepper", "perfect", "permit", "person", "pet", "phone", "photo", "phrase", "physical", + "piano", "picnic", "picture", "piece", "pig", "pigeon", "pill", "pilot", "pink", "pioneer", + "pipe", "pistol", "pitch", "pizza", "place", "planet", "plastic", "plate", "play", "please", + "pledge", "pluck", "plug", "plunge", "poem", "poet", "point", "polar", "pole", "police", + "pond", "pony", "pool", "popular", "portion", "position", "possible", "post", "potato", + "pottery", "poverty", "powder", "power", "practice", "praise", "predict", "prefer", "prepare", + "present", "pretty", "prevent", "price", "pride", "primary", "print", "priority", "prison", + "private", "prize", "problem", "process", "produce", "profit", "program", "project", "promote", + "proof", "property", "prosper", "protect", "proud", "provide", "public", "pudding", "pull", + "pulp", "pulse", "pumpkin", "punch", "pupil", "puppy", "purchase", "purity", "purpose", + "purse", "push", "put", "puzzle", "pyramid", "quality", "quantum", "quarter", "question", + "quick", "quit", "quiz", "quote", "rabbit", "raccoon", "race", "rack", "radar", "radio", + "rail", "rain", "raise", "rally", "ramp", "ranch", "random", "range", "rapid", "rare", "rate", + "rather", "raven", "raw", "razor", "ready", "real", "reason", "rebel", "rebuild", "recall", + "receive", "recipe", "record", "recycle", "reduce", "reflect", "reform", "refuse", "region", + "regret", "regular", "reject", "relax", "release", "relief", "rely", "remain", "remember", + "remind", "remove", "render", "renew", "rent", "reopen", "repair", "repeat", "replace", + "report", "require", "rescue", "resemble", "resist", "resource", "response", "result", + "retire", "retreat", "return", "reunion", "reveal", "review", "reward", "rhythm", "rib", + "ribbon", "rice", "rich", "ride", "ridge", "rifle", "right", "rigid", "ring", "riot", "ripple", + "risk", "ritual", "rival", "river", "road", "roast", "robot", "robust", "rocket", "romance", + "roof", "rookie", "room", "rose", "rotate", "rough", "round", "route", "royal", "rubber", + "rude", "rug", "rule", "run", "runway", "rural", "sad", "saddle", "sadness", "safe", "sail", + "salad", "salmon", "salon", "salt", "salute", "same", "sample", "sand", "satisfy", "satoshi", + "sauce", "sausage", "save", "say", "scale", "scan", "scare", "scatter", "scene", "scheme", + "school", "science", "scissors", "scorpion", "scout", "scrap", "screen", "script", "scrub", + "sea", "search", "season", "seat", "second", "secret", "section", "security", "seed", "seek", + "segment", "select", "sell", "seminar", "senior", "sense", "sentence", "series", "service", + "session", "settle", "setup", "seven", "shadow", "shaft", "shallow", "share", "shed", "shell", + "sheriff", "shield", "shift", "shine", "ship", "shiver", "shock", "shoe", "shoot", "shop", + "short", "shoulder", "shove", "shrimp", "shrug", "shuffle", "shy", "sibling", "sick", "side", + "siege", "sight", "sign", "silent", "silk", "silly", "silver", "similar", "simple", "since", + "sing", "siren", "sister", "situate", "six", "size", "skate", "sketch", "ski", "skill", "skin", + "skirt", "skull", "slab", "slam", "sleep", "slender", "slice", "slide", "slight", "slim", + "slogan", "slot", "slow", "slush", "small", "smart", "smile", "smoke", "smooth", "snack", + "snake", "snap", "sniff", "snow", "soap", "soccer", "social", "sock", "soda", "soft", "solar", + "soldier", "solid", "solution", "solve", "someone", "song", "soon", "sorry", "sort", "soul", + "sound", "soup", "source", "south", "space", "spare", "spatial", "spawn", "speak", "special", + "speed", "spell", "spend", "sphere", "spice", "spider", "spike", "spin", "spirit", "split", + "spoil", "sponsor", "spoon", "sport", "spot", "spray", "spread", "spring", "spy", "square", + "squeeze", "squirrel", "stable", "stadium", "staff", "stage", "stairs", "stamp", "stand", + "start", "state", "stay", "steak", "steel", "stem", "step", "stereo", "stick", "still", + "sting", "stock", "stomach", "stone", "stool", "story", "stove", "strategy", "street", + "strike", "strong", "struggle", "student", "stuff", "stumble", "style", "subject", "submit", + "subway", "success", "such", "sudden", "suffer", "sugar", "suggest", "suit", "summer", "sun", + "sunny", "sunset", "super", "supply", "supreme", "sure", "surface", "surge", "surprise", + "surround", "survey", "suspect", "sustain", "swallow", "swamp", "swap", "swarm", "swear", + "sweet", "swift", "swim", "swing", "switch", "sword", "symbol", "symptom", "syrup", "system", + "table", "tackle", "tag", "tail", "talent", "talk", "tank", "tape", "target", "task", "taste", + "tattoo", "taxi", "teach", "team", "tell", "ten", "tenant", "tennis", "tent", "term", "test", + "text", "thank", "that", "theme", "then", "theory", "there", "they", "thing", "this", + "thought", "three", "thrive", "throw", "thumb", "thunder", "ticket", "tide", "tiger", "tilt", + "timber", "time", "tiny", "tip", "tired", "tissue", "title", "toast", "tobacco", "today", + "toddler", "toe", "together", "toilet", "token", "tomato", "tomorrow", "tone", "tongue", + "tonight", "tool", "tooth", "top", "topic", "topple", "torch", "tornado", "tortoise", "toss", + "total", "tourist", "toward", "tower", "town", "toy", "track", "trade", "traffic", "tragic", + "train", "transfer", "trap", "trash", "travel", "tray", "treat", "tree", "trend", "trial", + "tribe", "trick", "trigger", "trim", "trip", "trophy", "trouble", "truck", "true", "truly", + "trumpet", "trust", "truth", "try", "tube", "tuition", "tumble", "tuna", "tunnel", "turkey", + "turn", "turtle", "twelve", "twenty", "twice", "twin", "twist", "two", "type", "typical", + "ugly", "umbrella", "unable", "unaware", "uncle", "uncover", "under", "undo", "unfair", + "unfold", "unhappy", "uniform", "unique", "unit", "universe", "unknown", "unlock", "until", + "unusual", "unveil", "update", "upgrade", "uphold", "upon", "upper", "upset", "urban", "urge", + "usage", "use", "used", "useful", "useless", "usual", "utility", "vacant", "vacuum", "vague", + "valid", "valley", "valve", "van", "vanish", "vapor", "various", "vast", "vault", "vehicle", + "velvet", "vendor", "venture", "venue", "verb", "verify", "version", "very", "vessel", + "veteran", "viable", "vibrant", "vicious", "victory", "video", "view", "village", "vintage", + "violin", "virtual", "virus", "visa", "visit", "visual", "vital", "vivid", "vocal", "voice", + "void", "volcano", "volume", "vote", "voyage", "wage", "wagon", "wait", "walk", "wall", + "walnut", "want", "warfare", "warm", "warrior", "wash", "wasp", "waste", "water", "wave", + "way", "wealth", "weapon", "wear", "weasel", "weather", "web", "wedding", "weekend", "weird", + "welcome", "west", "wet", "whale", "what", "wheat", "wheel", "when", "where", "whip", + "whisper", "wide", "width", "wife", "wild", "will", "win", "window", "wine", "wing", "wink", + "winner", "winter", "wire", "wisdom", "wise", "wish", "witness", "wolf", "woman", "wonder", + "wood", "wool", "word", "work", "world", "worry", "worth", "wrap", "wreck", "wrestle", "wrist", + "write", "wrong", "yard", "year", "yellow", "you", "young", "youth", "zebra", "zero", "zone", + "zoo", +]; diff --git a/crates/algokit_algo25/src/lib.rs b/crates/algokit_algo25/src/lib.rs new file mode 100644 index 00000000..fe1df8b9 --- /dev/null +++ b/crates/algokit_algo25/src/lib.rs @@ -0,0 +1,244 @@ +mod english; + +use english::ENGLISH; +use sha2::{Digest, Sha512_256}; + +pub const FAIL_TO_DECODE_MNEMONIC_ERROR_MSG: &str = "failed to decode mnemonic"; +pub const NOT_IN_WORDS_LIST_ERROR_MSG: &str = + "the mnemonic contains a word that is not in the wordlist"; + +const SEED_BYTES_LENGTH: usize = 32; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum MnemonicError { + InvalidSeedLength { expected: usize, found: usize }, + NotInWordsList, + FailedToDecodeMnemonic, +} + +impl core::fmt::Display for MnemonicError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + MnemonicError::InvalidSeedLength { expected, .. } => { + write!(f, "Seed length must be {expected}") + } + MnemonicError::NotInWordsList => write!(f, "{NOT_IN_WORDS_LIST_ERROR_MSG}"), + MnemonicError::FailedToDecodeMnemonic => { + write!(f, "{FAIL_TO_DECODE_MNEMONIC_ERROR_MSG}") + } + } + } +} + +impl std::error::Error for MnemonicError {} + +fn to_uint11_array(buffer8: &[u8]) -> Vec { + let mut buffer11 = Vec::new(); + let mut acc: u32 = 0; + let mut acc_bits: u32 = 0; + + for octet in buffer8 { + acc |= u32::from(*octet) << acc_bits; + acc_bits += 8; + + if acc_bits >= 11 { + buffer11.push((acc & 0x7ff) as u16); + acc >>= 11; + acc_bits -= 11; + } + } + + if acc_bits != 0 { + buffer11.push(acc as u16); + } + + buffer11 +} + +fn to_uint8_array(buffer11: &[u16]) -> Vec { + let mut buffer8 = Vec::new(); + let mut acc: u32 = 0; + let mut acc_bits: u32 = 0; + + for ui11 in buffer11 { + acc |= u32::from(*ui11) << acc_bits; + acc_bits += 11; + + while acc_bits >= 8 { + buffer8.push((acc & 0xff) as u8); + acc >>= 8; + acc_bits -= 8; + } + } + + if acc_bits != 0 { + buffer8.push(acc as u8); + } + + buffer8 +} + +fn apply_words(nums: &[u16]) -> Vec<&'static str> { + nums.iter().map(|n| ENGLISH[*n as usize]).collect() +} + +fn english_index(word: &str) -> Option { + ENGLISH.iter().position(|candidate| *candidate == word) +} + +fn compute_checksum(seed: &[u8; SEED_BYTES_LENGTH]) -> &'static str { + let mut hasher = Sha512_256::new(); + hasher.update(seed); + let hash = hasher.finalize(); + + let uint11_hash = to_uint11_array(hash.as_ref()); + let words = apply_words(&uint11_hash); + words[0] +} + +/// Converts a 32-byte key into a 25-word mnemonic including checksum. +pub fn mnemonic_from_seed(seed: &[u8]) -> Result { + if seed.len() != SEED_BYTES_LENGTH { + return Err(MnemonicError::InvalidSeedLength { + expected: SEED_BYTES_LENGTH, + found: seed.len(), + }); + } + + let seed: [u8; SEED_BYTES_LENGTH] = + seed.try_into() + .map_err(|_| MnemonicError::InvalidSeedLength { + expected: SEED_BYTES_LENGTH, + found: seed.len(), + })?; + + let uint11_array = to_uint11_array(&seed); + let words = apply_words(&uint11_array); + let checksum_word = compute_checksum(&seed); + + Ok(format!("{} {checksum_word}", words.join(" "))) +} + +/// Converts a mnemonic generated by this library back to the source 32-byte seed. +pub fn seed_from_mnemonic(mnemonic: &str) -> Result<[u8; SEED_BYTES_LENGTH], MnemonicError> { + let words: Vec<&str> = mnemonic.split_whitespace().collect(); + + // Expect exactly 25 words: 24 data words + 1 checksum word. + if words.len() != 25 { + return Err(MnemonicError::FailedToDecodeMnemonic); + } + + let key_words = &words[..24]; + + for w in key_words { + if english_index(w).is_none() { + return Err(MnemonicError::NotInWordsList); + } + } + + let checksum = words[24]; + let uint11_array: Vec = key_words + .iter() + .map(|word| english_index(word).expect("checked above") as u16) + .collect(); + + let mut uint8_array = to_uint8_array(&uint11_array); + + if uint8_array.len() != 33 { + return Err(MnemonicError::FailedToDecodeMnemonic); + } + + if uint8_array[uint8_array.len() - 1] != 0x0 { + return Err(MnemonicError::FailedToDecodeMnemonic); + } + + uint8_array.pop(); + + let seed: [u8; SEED_BYTES_LENGTH] = uint8_array + .try_into() + .map_err(|_| MnemonicError::FailedToDecodeMnemonic)?; + + if compute_checksum(&seed) == checksum { + return Ok(seed); + } + + Err(MnemonicError::FailedToDecodeMnemonic) +} + +/// Takes an Algorand secret key and returns its associated mnemonic. +pub fn secret_key_to_mnemonic(sk: &[u8]) -> Result { + let seed = sk + .get(..SEED_BYTES_LENGTH) + .ok_or(MnemonicError::InvalidSeedLength { + expected: SEED_BYTES_LENGTH, + found: sk.len(), + })?; + mnemonic_from_seed(seed) +} + +/// Takes a mnemonic and returns the corresponding master derivation key. +pub fn mnemonic_to_master_derivation_key( + mn: &str, +) -> Result<[u8; SEED_BYTES_LENGTH], MnemonicError> { + seed_from_mnemonic(mn) +} + +/// Takes a master derivation key and returns the corresponding mnemonic. +pub fn master_derivation_key_to_mnemonic(mdk: &[u8]) -> Result { + mnemonic_from_seed(mdk) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn seed_round_trip() { + let seed = [7u8; SEED_BYTES_LENGTH]; + let mnemonic = mnemonic_from_seed(&seed).expect("mnemonic should encode"); + let decoded = seed_from_mnemonic(&mnemonic).expect("mnemonic should decode"); + assert_eq!(decoded, seed); + } + + #[test] + fn rejects_non_wordlist_words() { + let seed = [3u8; SEED_BYTES_LENGTH]; + let mnemonic = mnemonic_from_seed(&seed).expect("mnemonic should encode"); + let mut words: Vec<&str> = mnemonic.split(' ').collect(); + words[0] = "notaword"; + let broken = words.join(" "); + + let err = seed_from_mnemonic(&broken).expect_err("should fail"); + assert_eq!(err, MnemonicError::NotInWordsList); + assert_eq!(err.to_string(), NOT_IN_WORDS_LIST_ERROR_MSG); + } + + #[test] + fn rejects_bad_checksum() { + let seed = [11u8; SEED_BYTES_LENGTH]; + let mnemonic = mnemonic_from_seed(&seed).expect("mnemonic should encode"); + let mut words: Vec<&str> = mnemonic.split(' ').collect(); + words[24] = if words[24] == "abandon" { + "ability" + } else { + "abandon" + }; + let broken = words.join(" "); + + let err = seed_from_mnemonic(&broken).expect_err("should fail checksum"); + assert_eq!(err, MnemonicError::FailedToDecodeMnemonic); + assert_eq!(err.to_string(), FAIL_TO_DECODE_MNEMONIC_ERROR_MSG); + } + + #[test] + fn rejects_wrong_seed_length() { + let err = mnemonic_from_seed(&[1u8; 31]).expect_err("length mismatch should fail"); + assert_eq!( + err, + MnemonicError::InvalidSeedLength { + expected: SEED_BYTES_LENGTH, + found: 31 + } + ); + } +} diff --git a/crates/algokit_algo25_ffi/Cargo.toml b/crates/algokit_algo25_ffi/Cargo.toml new file mode 100644 index 00000000..2ba0c0e1 --- /dev/null +++ b/crates/algokit_algo25_ffi/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "algokit_algo25_ffi" +version = "0.1.0" +edition = "2024" + +[lib] +crate-type = ["lib", "cdylib", "staticlib"] + +[features] +default = ["ffi_uniffi"] +ffi_uniffi = ["dep:uniffi"] + +[dependencies] +algokit_algo25 = { path = "../algokit_algo25" } +uniffi = { workspace = true, features = [ + "scaffolding-ffi-buffer-fns", +], optional = true } + +[build-dependencies] +uniffi = { workspace = true, features = [ + "build", + "scaffolding-ffi-buffer-fns", +] } diff --git a/crates/algokit_algo25_ffi/src/lib.rs b/crates/algokit_algo25_ffi/src/lib.rs new file mode 100644 index 00000000..398d482b --- /dev/null +++ b/crates/algokit_algo25_ffi/src/lib.rs @@ -0,0 +1,124 @@ +use algokit_algo25::{ + MnemonicError as RustMnemonicError, + master_derivation_key_to_mnemonic as rust_master_derivation_key_to_mnemonic, + mnemonic_from_seed as rust_mnemonic_from_seed, + mnemonic_to_master_derivation_key as rust_mnemonic_to_master_derivation_key, + secret_key_to_mnemonic as rust_secret_key_to_mnemonic, + seed_from_mnemonic as rust_seed_from_mnemonic, +}; + +#[cfg(feature = "ffi_uniffi")] +uniffi::setup_scaffolding!(); + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ffi_uniffi", derive(uniffi::Enum))] +pub enum MnemonicErrorKind { + InvalidSeedLength, + NotInWordsList, + FailedToDecodeMnemonic, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ffi_uniffi", derive(uniffi::Record))] +pub struct MnemonicError { + pub kind: MnemonicErrorKind, + pub expected: Option, + pub found: Option, +} + +impl From for MnemonicError { + fn from(value: RustMnemonicError) -> Self { + match value { + RustMnemonicError::InvalidSeedLength { expected, found } => Self { + kind: MnemonicErrorKind::InvalidSeedLength, + expected: Some(expected as u64), + found: Some(found as u64), + }, + RustMnemonicError::NotInWordsList => Self { + kind: MnemonicErrorKind::NotInWordsList, + expected: None, + found: None, + }, + RustMnemonicError::FailedToDecodeMnemonic => Self { + kind: MnemonicErrorKind::FailedToDecodeMnemonic, + expected: None, + found: None, + }, + } + } +} + +impl From for RustMnemonicError { + fn from(value: MnemonicError) -> Self { + match value.kind { + MnemonicErrorKind::InvalidSeedLength => RustMnemonicError::InvalidSeedLength { + expected: value.expected.unwrap_or_default() as usize, + found: value.found.unwrap_or_default() as usize, + }, + MnemonicErrorKind::NotInWordsList => RustMnemonicError::NotInWordsList, + MnemonicErrorKind::FailedToDecodeMnemonic => RustMnemonicError::FailedToDecodeMnemonic, + } + } +} + +#[derive(Debug, Clone)] +#[cfg_attr(feature = "ffi_uniffi", derive(uniffi::Error))] +pub enum AlgoKitAlgo25Error { + Error { err_msg: String }, +} + +impl std::fmt::Display for AlgoKitAlgo25Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AlgoKitAlgo25Error::Error { err_msg: message } => write!(f, "{}", message), + } + } +} + +impl From for AlgoKitAlgo25Error { + fn from(value: RustMnemonicError) -> Self { + let ffi_error: MnemonicError = value.into(); + let details = match ffi_error.kind { + MnemonicErrorKind::InvalidSeedLength => format!( + "invalid seed length (expected {}, found {})", + ffi_error.expected.unwrap_or_default(), + ffi_error.found.unwrap_or_default(), + ), + MnemonicErrorKind::NotInWordsList => { + "mnemonic contains a word that is not in the wordlist".to_string() + } + MnemonicErrorKind::FailedToDecodeMnemonic => "failed to decode mnemonic".to_string(), + }; + + Self::Error { err_msg: details } + } +} + +#[uniffi::export] +pub fn mnemonic_from_seed(seed: Vec) -> Result { + rust_mnemonic_from_seed(&seed).map_err(Into::into) +} + +#[uniffi::export] +pub fn seed_from_mnemonic(mnemonic: &str) -> Result, AlgoKitAlgo25Error> { + rust_seed_from_mnemonic(mnemonic) + .map(|seed| seed.to_vec()) + .map_err(Into::into) +} + +#[uniffi::export] +pub fn secret_key_to_mnemonic(secret_key: Vec) -> Result { + rust_secret_key_to_mnemonic(&secret_key).map_err(Into::into) +} + +#[uniffi::export] +pub fn mnemonic_to_master_derivation_key(mnemonic: &str) -> Result, AlgoKitAlgo25Error> { + rust_mnemonic_to_master_derivation_key(mnemonic) + .map(|seed| seed.to_vec()) + .map_err(Into::into) +} + +#[uniffi::export] +pub fn master_derivation_key_to_mnemonic(mdk: Vec) -> Result { + rust_master_derivation_key_to_mnemonic(&mdk).map_err(Into::into) +} diff --git a/crates/algokit_algo25_ffi/uniffi.toml b/crates/algokit_algo25_ffi/uniffi.toml new file mode 100644 index 00000000..f7178a85 --- /dev/null +++ b/crates/algokit_algo25_ffi/uniffi.toml @@ -0,0 +1,2 @@ +[bindings.swift] +module_name = "algokit_algo25" diff --git a/crates/algokit_crypto_ffi/Cargo.toml b/crates/algokit_crypto_ffi/Cargo.toml index 4e0279ec..8ca25c43 100644 --- a/crates/algokit_crypto_ffi/Cargo.toml +++ b/crates/algokit_crypto_ffi/Cargo.toml @@ -18,6 +18,7 @@ uniffi = { workspace = true, features = [ async-trait = "0.1.89" signature = "2.2.0" tokio = { version = "1.49.0", features = ["rt", "time", "net", "io-util", "sync"] } +getrandom = "0.4.1" [dev-dependencies] tokio = { version = "1.49.0", features = ["macros", "rt"] } diff --git a/crates/algokit_crypto_ffi/src/lib.rs b/crates/algokit_crypto_ffi/src/lib.rs index 1451e0ab..bf693bf8 100644 --- a/crates/algokit_crypto_ffi/src/lib.rs +++ b/crates/algokit_crypto_ffi/src/lib.rs @@ -2,6 +2,7 @@ use algokit_crypto::ed25519::{ CryptoxideEd25519Keypair as RustCryptoxideEd25519Keypair, Ed25519Signer as RustEd25519Signer, }; +use signature::Keypair; #[cfg(feature = "ffi_uniffi")] use uniffi::{self}; @@ -53,3 +54,25 @@ pub fn ed25519_raw_sign(secret_key: Vec, data: Vec) -> Result, A Ok(signature.to_vec()) } + +#[uniffi::export] +pub fn ed25519_public_key_from_seed(seed: Vec) -> Result, AlgoKitCryptoError> { + let keypair = + RustCryptoxideEd25519Keypair::try_generate(Some(seed.try_into().map_err(|_| { + AlgoKitCryptoError::from("Secret key must be 32 bytes for Ed25519".to_string()) + })?)) + .map_err(|e| { + AlgoKitCryptoError::from(format!("Failed to generate keypair from secret key: {}", e)) + })?; + + Ok(keypair.verifying_key().to_vec()) +} + +/// Generate random bytes from the operating system's random number generator +#[uniffi::export] +pub fn random_bytes(len: u32) -> Result, AlgoKitCryptoError> { + let mut bytes = vec![0u8; len as usize]; + getrandom::fill(&mut bytes) + .map_err(|e| AlgoKitCryptoError::from(format!("Failed to generate random bytes: {}", e)))?; + Ok(bytes) +} diff --git a/packages/android/algokit_algo25/README.md b/packages/android/algokit_algo25/README.md new file mode 100644 index 00000000..cd33934b --- /dev/null +++ b/packages/android/algokit_algo25/README.md @@ -0,0 +1,43 @@ +# algokit_algo25 + +## Building + +```sh +cargo pkg algo25 kt +``` + +## Testing + +### Host + +```sh +./gradlew test +``` + +### Android + +First you need a connected android device. You can either use a physical device or start an emulator. + +To see available emulators: + +```sh +~/Library/Android/sdk/emulator/emulator -list-avds +``` + +Then start the emulator: + +```sh +~/Library/Android/sdk/emulator/emulator -avd +``` + +Or to start the first one: + +```sh +~/Library/Android/sdk/emulator/emulator -avd `~/Library/Android/sdk/emulator/emulator -list-avds | head -n 1` +``` + +Then run the tests: + +```sh +./gradlew connectedAndroidTest +``` diff --git a/packages/android/algokit_algo25/build.gradle.kts b/packages/android/algokit_algo25/build.gradle.kts new file mode 100644 index 00000000..ae060169 --- /dev/null +++ b/packages/android/algokit_algo25/build.gradle.kts @@ -0,0 +1,53 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This is a general purpose Gradle build. + * Learn more about Gradle by exploring our Samples at https://docs.gradle.org/8.14.2/samples + */ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) +} + +android { + namespace = "com.example.algokit_algo25" + compileSdk = 36 + + defaultConfig { + minSdk = 28 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + } + kotlinOptions { jvmTarget = "21" } + testOptions { + unitTests { + isIncludeAndroidResources = true + all { it.systemProperty("jna.library.path", "src/test/resources") } + } + } +} + +dependencies { + implementation("net.java.dev.jna:jna:5.14.0@aar") + // Use full JNA JAR for unit tests (includes native dispatch libraries) + testImplementation("net.java.dev.jna:jna:5.14.0") + + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.material) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) +} diff --git a/packages/android/algokit_algo25/gradle.properties b/packages/android/algokit_algo25/gradle.properties new file mode 100644 index 00000000..d5be7319 --- /dev/null +++ b/packages/android/algokit_algo25/gradle.properties @@ -0,0 +1,7 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties + +org.gradle.configuration-cache=true +android.useAndroidX=true +android.suppressUnsupportedCompileSdk=36 + diff --git a/packages/android/algokit_algo25/gradle/libs.versions.toml b/packages/android/algokit_algo25/gradle/libs.versions.toml new file mode 100644 index 00000000..ff68a35c --- /dev/null +++ b/packages/android/algokit_algo25/gradle/libs.versions.toml @@ -0,0 +1,24 @@ +[versions] +agp = "8.7.3" +kotlin = "2.0.21" +coreKtx = "1.16.0" +junit = "4.13.2" +junitVersion = "1.2.1" +espressoCore = "3.6.1" +appcompat = "1.7.0" +material = "1.12.0" + +[libraries] +androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } +junit = { group = "junit", name = "junit", version.ref = "junit" } +androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +android-library = { id = "com.android.library", version.ref = "agp" } +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } + + diff --git a/packages/android/algokit_algo25/gradle/wrapper/gradle-wrapper.jar b/packages/android/algokit_algo25/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..1b33c55b Binary files /dev/null and b/packages/android/algokit_algo25/gradle/wrapper/gradle-wrapper.jar differ diff --git a/packages/android/algokit_algo25/gradle/wrapper/gradle-wrapper.properties b/packages/android/algokit_algo25/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..ff23a68d --- /dev/null +++ b/packages/android/algokit_algo25/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/android/algokit_algo25/gradlew b/packages/android/algokit_algo25/gradlew new file mode 100755 index 00000000..23d15a93 --- /dev/null +++ b/packages/android/algokit_algo25/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH="\\\"\\\"" + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/packages/android/algokit_algo25/gradlew.bat b/packages/android/algokit_algo25/gradlew.bat new file mode 100644 index 00000000..db3a6ac2 --- /dev/null +++ b/packages/android/algokit_algo25/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/packages/android/algokit_algo25/settings.gradle.kts b/packages/android/algokit_algo25/settings.gradle.kts new file mode 100644 index 00000000..275315bc --- /dev/null +++ b/packages/android/algokit_algo25/settings.gradle.kts @@ -0,0 +1,30 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * For more detailed information on multi-project builds, please refer to https://docs.gradle.org/8.14.2/userguide/multi_project_builds.html in the Gradle documentation. + */ + +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "algokit_algo25" diff --git a/packages/android/algokit_algo25/src/main/kotlin/uniffi/algokit_algo25_ffi/algokit_algo25_ffi.kt b/packages/android/algokit_algo25/src/main/kotlin/uniffi/algokit_algo25_ffi/algokit_algo25_ffi.kt new file mode 100644 index 00000000..3d322c91 --- /dev/null +++ b/packages/android/algokit_algo25/src/main/kotlin/uniffi/algokit_algo25_ffi/algokit_algo25_ffi.kt @@ -0,0 +1,1330 @@ +// This file was autogenerated by some hot garbage in the `uniffi` crate. +// Trust me, you don't want to mess with it! + +@file:Suppress("NAME_SHADOWING") + +package uniffi.algokit_algo25_ffi + +// Common helper code. +// +// Ideally this would live in a separate .kt file where it can be unittested etc +// in isolation, and perhaps even published as a re-useable package. +// +// However, it's important that the details of how this helper code works (e.g. the +// way that different builtin types are passed across the FFI) exactly match what's +// expected by the Rust code on the other side of the interface. In practice right +// now that means coming from the exact some version of `uniffi` that was used to +// compile the Rust component. The easiest way to ensure this is to bundle the Kotlin +// helpers directly inline like we're doing here. + +import com.sun.jna.Library +import com.sun.jna.IntegerType +import com.sun.jna.Native +import com.sun.jna.Pointer +import com.sun.jna.Structure +import com.sun.jna.Callback +import com.sun.jna.ptr.* +import java.nio.ByteBuffer +import java.nio.ByteOrder +import java.nio.CharBuffer +import java.nio.charset.CodingErrorAction +import java.util.concurrent.atomic.AtomicLong +import java.util.concurrent.ConcurrentHashMap + +// This is a helper for safely working with byte buffers returned from the Rust code. +// A rust-owned buffer is represented by its capacity, its current length, and a +// pointer to the underlying data. + +/** + * @suppress + */ +@Structure.FieldOrder("capacity", "len", "data") +open class RustBuffer : Structure() { + // Note: `capacity` and `len` are actually `ULong` values, but JVM only supports signed values. + // When dealing with these fields, make sure to call `toULong()`. + @JvmField var capacity: Long = 0 + @JvmField var len: Long = 0 + @JvmField var data: Pointer? = null + + class ByValue: RustBuffer(), Structure.ByValue + class ByReference: RustBuffer(), Structure.ByReference + + internal fun setValue(other: RustBuffer) { + capacity = other.capacity + len = other.len + data = other.data + } + + companion object { + internal fun alloc(size: ULong = 0UL) = uniffiRustCall() { status -> + // Note: need to convert the size to a `Long` value to make this work with JVM. + UniffiLib.INSTANCE.ffi_algokit_algo25_ffi_rustbuffer_alloc(size.toLong(), status) + }.also { + if(it.data == null) { + throw RuntimeException("RustBuffer.alloc() returned null data pointer (size=${size})") + } + } + + internal fun create(capacity: ULong, len: ULong, data: Pointer?): RustBuffer.ByValue { + var buf = RustBuffer.ByValue() + buf.capacity = capacity.toLong() + buf.len = len.toLong() + buf.data = data + return buf + } + + internal fun free(buf: RustBuffer.ByValue) = uniffiRustCall() { status -> + UniffiLib.INSTANCE.ffi_algokit_algo25_ffi_rustbuffer_free(buf, status) + } + } + + @Suppress("TooGenericExceptionThrown") + fun asByteBuffer() = + this.data?.getByteBuffer(0, this.len.toLong())?.also { + it.order(ByteOrder.BIG_ENDIAN) + } +} + +/** + * The equivalent of the `*mut RustBuffer` type. + * Required for callbacks taking in an out pointer. + * + * Size is the sum of all values in the struct. + * + * @suppress + */ +class RustBufferByReference : ByReference(16) { + /** + * Set the pointed-to `RustBuffer` to the given value. + */ + fun setValue(value: RustBuffer.ByValue) { + // NOTE: The offsets are as they are in the C-like struct. + val pointer = getPointer() + pointer.setLong(0, value.capacity) + pointer.setLong(8, value.len) + pointer.setPointer(16, value.data) + } + + /** + * Get a `RustBuffer.ByValue` from this reference. + */ + fun getValue(): RustBuffer.ByValue { + val pointer = getPointer() + val value = RustBuffer.ByValue() + value.writeField("capacity", pointer.getLong(0)) + value.writeField("len", pointer.getLong(8)) + value.writeField("data", pointer.getLong(16)) + + return value + } +} + +// This is a helper for safely passing byte references into the rust code. +// It's not actually used at the moment, because there aren't many things that you +// can take a direct pointer to in the JVM, and if we're going to copy something +// then we might as well copy it into a `RustBuffer`. But it's here for API +// completeness. + +@Structure.FieldOrder("len", "data") +internal open class ForeignBytes : Structure() { + @JvmField var len: Int = 0 + @JvmField var data: Pointer? = null + + class ByValue : ForeignBytes(), Structure.ByValue +} +/** + * The FfiConverter interface handles converter types to and from the FFI + * + * All implementing objects should be public to support external types. When a + * type is external we need to import it's FfiConverter. + * + * @suppress + */ +public interface FfiConverter { + // Convert an FFI type to a Kotlin type + fun lift(value: FfiType): KotlinType + + // Convert an Kotlin type to an FFI type + fun lower(value: KotlinType): FfiType + + // Read a Kotlin type from a `ByteBuffer` + fun read(buf: ByteBuffer): KotlinType + + // Calculate bytes to allocate when creating a `RustBuffer` + // + // This must return at least as many bytes as the write() function will + // write. It can return more bytes than needed, for example when writing + // Strings we can't know the exact bytes needed until we the UTF-8 + // encoding, so we pessimistically allocate the largest size possible (3 + // bytes per codepoint). Allocating extra bytes is not really a big deal + // because the `RustBuffer` is short-lived. + fun allocationSize(value: KotlinType): ULong + + // Write a Kotlin type to a `ByteBuffer` + fun write(value: KotlinType, buf: ByteBuffer) + + // Lower a value into a `RustBuffer` + // + // This method lowers a value into a `RustBuffer` rather than the normal + // FfiType. It's used by the callback interface code. Callback interface + // returns are always serialized into a `RustBuffer` regardless of their + // normal FFI type. + fun lowerIntoRustBuffer(value: KotlinType): RustBuffer.ByValue { + val rbuf = RustBuffer.alloc(allocationSize(value)) + try { + val bbuf = rbuf.data!!.getByteBuffer(0, rbuf.capacity).also { + it.order(ByteOrder.BIG_ENDIAN) + } + write(value, bbuf) + rbuf.writeField("len", bbuf.position().toLong()) + return rbuf + } catch (e: Throwable) { + RustBuffer.free(rbuf) + throw e + } + } + + // Lift a value from a `RustBuffer`. + // + // This here mostly because of the symmetry with `lowerIntoRustBuffer()`. + // It's currently only used by the `FfiConverterRustBuffer` class below. + fun liftFromRustBuffer(rbuf: RustBuffer.ByValue): KotlinType { + val byteBuf = rbuf.asByteBuffer()!! + try { + val item = read(byteBuf) + if (byteBuf.hasRemaining()) { + throw RuntimeException("junk remaining in buffer after lifting, something is very wrong!!") + } + return item + } finally { + RustBuffer.free(rbuf) + } + } +} + +/** + * FfiConverter that uses `RustBuffer` as the FfiType + * + * @suppress + */ +public interface FfiConverterRustBuffer: FfiConverter { + override fun lift(value: RustBuffer.ByValue) = liftFromRustBuffer(value) + override fun lower(value: KotlinType) = lowerIntoRustBuffer(value) +} +// A handful of classes and functions to support the generated data structures. +// This would be a good candidate for isolating in its own ffi-support lib. + +internal const val UNIFFI_CALL_SUCCESS = 0.toByte() +internal const val UNIFFI_CALL_ERROR = 1.toByte() +internal const val UNIFFI_CALL_UNEXPECTED_ERROR = 2.toByte() + +@Structure.FieldOrder("code", "error_buf") +internal open class UniffiRustCallStatus : Structure() { + @JvmField var code: Byte = 0 + @JvmField var error_buf: RustBuffer.ByValue = RustBuffer.ByValue() + + class ByValue: UniffiRustCallStatus(), Structure.ByValue + + fun isSuccess(): Boolean { + return code == UNIFFI_CALL_SUCCESS + } + + fun isError(): Boolean { + return code == UNIFFI_CALL_ERROR + } + + fun isPanic(): Boolean { + return code == UNIFFI_CALL_UNEXPECTED_ERROR + } + + companion object { + fun create(code: Byte, errorBuf: RustBuffer.ByValue): UniffiRustCallStatus.ByValue { + val callStatus = UniffiRustCallStatus.ByValue() + callStatus.code = code + callStatus.error_buf = errorBuf + return callStatus + } + } +} + +class InternalException(message: String) : kotlin.Exception(message) + +/** + * Each top-level error class has a companion object that can lift the error from the call status's rust buffer + * + * @suppress + */ +interface UniffiRustCallStatusErrorHandler { + fun lift(error_buf: RustBuffer.ByValue): E; +} + +// Helpers for calling Rust +// In practice we usually need to be synchronized to call this safely, so it doesn't +// synchronize itself + +// Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err +private inline fun uniffiRustCallWithError(errorHandler: UniffiRustCallStatusErrorHandler, callback: (UniffiRustCallStatus) -> U): U { + var status = UniffiRustCallStatus() + val return_value = callback(status) + uniffiCheckCallStatus(errorHandler, status) + return return_value +} + +// Check UniffiRustCallStatus and throw an error if the call wasn't successful +private fun uniffiCheckCallStatus(errorHandler: UniffiRustCallStatusErrorHandler, status: UniffiRustCallStatus) { + if (status.isSuccess()) { + return + } else if (status.isError()) { + throw errorHandler.lift(status.error_buf) + } else if (status.isPanic()) { + // when the rust code sees a panic, it tries to construct a rustbuffer + // with the message. but if that code panics, then it just sends back + // an empty buffer. + if (status.error_buf.len > 0) { + throw InternalException(FfiConverterString.lift(status.error_buf)) + } else { + throw InternalException("Rust panic") + } + } else { + throw InternalException("Unknown rust call status: $status.code") + } +} + +/** + * UniffiRustCallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR + * + * @suppress + */ +object UniffiNullRustCallStatusErrorHandler: UniffiRustCallStatusErrorHandler { + override fun lift(error_buf: RustBuffer.ByValue): InternalException { + RustBuffer.free(error_buf) + return InternalException("Unexpected CALL_ERROR") + } +} + +// Call a rust function that returns a plain value +private inline fun uniffiRustCall(callback: (UniffiRustCallStatus) -> U): U { + return uniffiRustCallWithError(UniffiNullRustCallStatusErrorHandler, callback) +} + +internal inline fun uniffiTraitInterfaceCall( + callStatus: UniffiRustCallStatus, + makeCall: () -> T, + writeReturn: (T) -> Unit, +) { + try { + writeReturn(makeCall()) + } catch(e: kotlin.Exception) { + callStatus.code = UNIFFI_CALL_UNEXPECTED_ERROR + callStatus.error_buf = FfiConverterString.lower(e.toString()) + } +} + +internal inline fun uniffiTraitInterfaceCallWithError( + callStatus: UniffiRustCallStatus, + makeCall: () -> T, + writeReturn: (T) -> Unit, + lowerError: (E) -> RustBuffer.ByValue +) { + try { + writeReturn(makeCall()) + } catch(e: kotlin.Exception) { + if (e is E) { + callStatus.code = UNIFFI_CALL_ERROR + callStatus.error_buf = lowerError(e) + } else { + callStatus.code = UNIFFI_CALL_UNEXPECTED_ERROR + callStatus.error_buf = FfiConverterString.lower(e.toString()) + } + } +} +// Map handles to objects +// +// This is used pass an opaque 64-bit handle representing a foreign object to the Rust code. +internal class UniffiHandleMap { + private val map = ConcurrentHashMap() + private val counter = java.util.concurrent.atomic.AtomicLong(0) + + val size: Int + get() = map.size + + // Insert a new object into the handle map and get a handle for it + fun insert(obj: T): Long { + val handle = counter.getAndAdd(1) + map.put(handle, obj) + return handle + } + + // Get an object from the handle map + fun get(handle: Long): T { + return map.get(handle) ?: throw InternalException("UniffiHandleMap.get: Invalid handle") + } + + // Remove an entry from the handlemap and get the Kotlin object back + fun remove(handle: Long): T { + return map.remove(handle) ?: throw InternalException("UniffiHandleMap: Invalid handle") + } +} + +// Contains loading, initialization code, +// and the FFI Function declarations in a com.sun.jna.Library. +@Synchronized +private fun findLibraryName(componentName: String): String { + val libOverride = System.getProperty("uniffi.component.$componentName.libraryOverride") + if (libOverride != null) { + return libOverride + } + return "algokit_algo25_ffi" +} + +private inline fun loadIndirect( + componentName: String +): Lib { + return Native.load(findLibraryName(componentName), Lib::class.java) +} + +// Define FFI callback types +internal interface UniffiRustFutureContinuationCallback : com.sun.jna.Callback { + fun callback(`data`: Long,`pollResult`: Byte,) +} +internal interface UniffiForeignFutureFree : com.sun.jna.Callback { + fun callback(`handle`: Long,) +} +internal interface UniffiCallbackInterfaceFree : com.sun.jna.Callback { + fun callback(`handle`: Long,) +} +@Structure.FieldOrder("handle", "free") +internal open class UniffiForeignFuture( + @JvmField internal var `handle`: Long = 0.toLong(), + @JvmField internal var `free`: UniffiForeignFutureFree? = null, +) : Structure() { + class UniffiByValue( + `handle`: Long = 0.toLong(), + `free`: UniffiForeignFutureFree? = null, + ): UniffiForeignFuture(`handle`,`free`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFuture) { + `handle` = other.`handle` + `free` = other.`free` + } + +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructU8( + @JvmField internal var `returnValue`: Byte = 0.toByte(), + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Byte = 0.toByte(), + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructU8(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructU8) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteU8 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructU8.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructI8( + @JvmField internal var `returnValue`: Byte = 0.toByte(), + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Byte = 0.toByte(), + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructI8(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructI8) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteI8 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructI8.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructU16( + @JvmField internal var `returnValue`: Short = 0.toShort(), + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Short = 0.toShort(), + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructU16(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructU16) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteU16 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructU16.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructI16( + @JvmField internal var `returnValue`: Short = 0.toShort(), + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Short = 0.toShort(), + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructI16(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructI16) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteI16 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructI16.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructU32( + @JvmField internal var `returnValue`: Int = 0, + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Int = 0, + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructU32(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructU32) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteU32 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructU32.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructI32( + @JvmField internal var `returnValue`: Int = 0, + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Int = 0, + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructI32(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructI32) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteI32 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructI32.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructU64( + @JvmField internal var `returnValue`: Long = 0.toLong(), + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Long = 0.toLong(), + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructU64(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructU64) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteU64 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructU64.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructI64( + @JvmField internal var `returnValue`: Long = 0.toLong(), + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Long = 0.toLong(), + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructI64(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructI64) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteI64 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructI64.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructF32( + @JvmField internal var `returnValue`: Float = 0.0f, + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Float = 0.0f, + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructF32(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructF32) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteF32 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructF32.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructF64( + @JvmField internal var `returnValue`: Double = 0.0, + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Double = 0.0, + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructF64(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructF64) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteF64 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructF64.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructPointer( + @JvmField internal var `returnValue`: Pointer = Pointer.NULL, + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Pointer = Pointer.NULL, + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructPointer(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructPointer) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompletePointer : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructPointer.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructRustBuffer( + @JvmField internal var `returnValue`: RustBuffer.ByValue = RustBuffer.ByValue(), + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: RustBuffer.ByValue = RustBuffer.ByValue(), + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructRustBuffer(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructRustBuffer) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteRustBuffer : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructRustBuffer.UniffiByValue,) +} +@Structure.FieldOrder("callStatus") +internal open class UniffiForeignFutureStructVoid( + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructVoid(`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructVoid) { + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteVoid : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructVoid.UniffiByValue,) +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +// For large crates we prevent `MethodTooLargeException` (see #2340) +// N.B. the name of the extension is very misleading, since it is +// rather `InterfaceTooLargeException`, caused by too many methods +// in the interface for large crates. +// +// By splitting the otherwise huge interface into two parts +// * UniffiLib +// * IntegrityCheckingUniffiLib (this) +// we allow for ~2x as many methods in the UniffiLib interface. +// +// The `ffi_uniffi_contract_version` method and all checksum methods are put +// into `IntegrityCheckingUniffiLib` and these methods are called only once, +// when the library is loaded. +internal interface IntegrityCheckingUniffiLib : Library { + // Integrity check functions only + fun uniffi_algokit_algo25_ffi_checksum_func_master_derivation_key_to_mnemonic( +): Short +fun uniffi_algokit_algo25_ffi_checksum_func_mnemonic_from_seed( +): Short +fun uniffi_algokit_algo25_ffi_checksum_func_mnemonic_to_master_derivation_key( +): Short +fun uniffi_algokit_algo25_ffi_checksum_func_secret_key_to_mnemonic( +): Short +fun uniffi_algokit_algo25_ffi_checksum_func_seed_from_mnemonic( +): Short +fun ffi_algokit_algo25_ffi_uniffi_contract_version( +): Int + +} + +// A JNA Library to expose the extern-C FFI definitions. +// This is an implementation detail which will be called internally by the public API. +internal interface UniffiLib : Library { + companion object { + internal val INSTANCE: UniffiLib by lazy { + val componentName = "algokit_algo25_ffi" + // For large crates we prevent `MethodTooLargeException` (see #2340) + // N.B. the name of the extension is very misleading, since it is + // rather `InterfaceTooLargeException`, caused by too many methods + // in the interface for large crates. + // + // By splitting the otherwise huge interface into two parts + // * UniffiLib (this) + // * IntegrityCheckingUniffiLib + // And all checksum methods are put into `IntegrityCheckingUniffiLib` + // we allow for ~2x as many methods in the UniffiLib interface. + // + // Thus we first load the library with `loadIndirect` as `IntegrityCheckingUniffiLib` + // so that we can (optionally!) call `uniffiCheckApiChecksums`... + loadIndirect(componentName) + .also { lib: IntegrityCheckingUniffiLib -> + uniffiCheckContractApiVersion(lib) + uniffiCheckApiChecksums(lib) + } + // ... and then we load the library as `UniffiLib` + // N.B. we cannot use `loadIndirect` once and then try to cast it to `UniffiLib` + // => results in `java.lang.ClassCastException: com.sun.proxy.$Proxy cannot be cast to ...` + // error. So we must call `loadIndirect` twice. For crates large enough + // to trigger this issue, the performance impact is negligible, running on + // a macOS M1 machine the `loadIndirect` call takes ~50ms. + val lib = loadIndirect(componentName) + // No need to check the contract version and checksums, since + // we already did that with `IntegrityCheckingUniffiLib` above. + // Loading of library with integrity check done. + lib + } + + } + + // FFI functions + fun uniffi_algokit_algo25_ffi_fn_func_master_derivation_key_to_mnemonic(`mdk`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_algokit_algo25_ffi_fn_func_mnemonic_from_seed(`seed`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_algokit_algo25_ffi_fn_func_mnemonic_to_master_derivation_key(`mnemonic`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_algokit_algo25_ffi_fn_func_secret_key_to_mnemonic(`secretKey`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_algokit_algo25_ffi_fn_func_seed_from_mnemonic(`mnemonic`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun ffi_algokit_algo25_ffi_rustbuffer_alloc(`size`: Long,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun ffi_algokit_algo25_ffi_rustbuffer_from_bytes(`bytes`: ForeignBytes.ByValue,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun ffi_algokit_algo25_ffi_rustbuffer_free(`buf`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): Unit +fun ffi_algokit_algo25_ffi_rustbuffer_reserve(`buf`: RustBuffer.ByValue,`additional`: Long,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun ffi_algokit_algo25_ffi_rust_future_poll_u8(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_cancel_u8(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_free_u8(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_complete_u8(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Byte +fun ffi_algokit_algo25_ffi_rust_future_poll_i8(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_cancel_i8(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_free_i8(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_complete_i8(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Byte +fun ffi_algokit_algo25_ffi_rust_future_poll_u16(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_cancel_u16(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_free_u16(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_complete_u16(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Short +fun ffi_algokit_algo25_ffi_rust_future_poll_i16(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_cancel_i16(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_free_i16(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_complete_i16(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Short +fun ffi_algokit_algo25_ffi_rust_future_poll_u32(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_cancel_u32(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_free_u32(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_complete_u32(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Int +fun ffi_algokit_algo25_ffi_rust_future_poll_i32(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_cancel_i32(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_free_i32(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_complete_i32(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Int +fun ffi_algokit_algo25_ffi_rust_future_poll_u64(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_cancel_u64(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_free_u64(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_complete_u64(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Long +fun ffi_algokit_algo25_ffi_rust_future_poll_i64(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_cancel_i64(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_free_i64(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_complete_i64(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Long +fun ffi_algokit_algo25_ffi_rust_future_poll_f32(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_cancel_f32(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_free_f32(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_complete_f32(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Float +fun ffi_algokit_algo25_ffi_rust_future_poll_f64(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_cancel_f64(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_free_f64(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_complete_f64(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Double +fun ffi_algokit_algo25_ffi_rust_future_poll_pointer(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_cancel_pointer(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_free_pointer(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_complete_pointer(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun ffi_algokit_algo25_ffi_rust_future_poll_rust_buffer(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_cancel_rust_buffer(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_free_rust_buffer(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_complete_rust_buffer(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun ffi_algokit_algo25_ffi_rust_future_poll_void(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_cancel_void(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_free_void(`handle`: Long, +): Unit +fun ffi_algokit_algo25_ffi_rust_future_complete_void(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Unit + +} + +private fun uniffiCheckContractApiVersion(lib: IntegrityCheckingUniffiLib) { + // Get the bindings contract version from our ComponentInterface + val bindings_contract_version = 29 + // Get the scaffolding contract version by calling the into the dylib + val scaffolding_contract_version = lib.ffi_algokit_algo25_ffi_uniffi_contract_version() + if (bindings_contract_version != scaffolding_contract_version) { + throw RuntimeException("UniFFI contract version mismatch: try cleaning and rebuilding your project") + } +} +@Suppress("UNUSED_PARAMETER") +private fun uniffiCheckApiChecksums(lib: IntegrityCheckingUniffiLib) { + if (lib.uniffi_algokit_algo25_ffi_checksum_func_master_derivation_key_to_mnemonic() != 32168.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_algokit_algo25_ffi_checksum_func_mnemonic_from_seed() != 8214.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_algokit_algo25_ffi_checksum_func_mnemonic_to_master_derivation_key() != 50816.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_algokit_algo25_ffi_checksum_func_secret_key_to_mnemonic() != 57306.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_algokit_algo25_ffi_checksum_func_seed_from_mnemonic() != 29635.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } +} + +/** + * @suppress + */ +public fun uniffiEnsureInitialized() { + UniffiLib.INSTANCE +} + +// Async support + +// Public interface members begin here. + + +// Interface implemented by anything that can contain an object reference. +// +// Such types expose a `destroy()` method that must be called to cleanly +// dispose of the contained objects. Failure to call this method may result +// in memory leaks. +// +// The easiest way to ensure this method is called is to use the `.use` +// helper method to execute a block and destroy the object at the end. +interface Disposable { + fun destroy() + companion object { + fun destroy(vararg args: Any?) { + for (arg in args) { + when (arg) { + is Disposable -> arg.destroy() + is ArrayList<*> -> { + for (idx in arg.indices) { + val element = arg[idx] + if (element is Disposable) { + element.destroy() + } + } + } + is Map<*, *> -> { + for (element in arg.values) { + if (element is Disposable) { + element.destroy() + } + } + } + is Iterable<*> -> { + for (element in arg) { + if (element is Disposable) { + element.destroy() + } + } + } + } + } + } + } +} + +/** + * @suppress + */ +inline fun T.use(block: (T) -> R) = + try { + block(this) + } finally { + try { + // N.B. our implementation is on the nullable type `Disposable?`. + this?.destroy() + } catch (e: Throwable) { + // swallow + } + } + +/** + * Used to instantiate an interface without an actual pointer, for fakes in tests, mostly. + * + * @suppress + * */ +object NoPointer + +/** + * @suppress + */ +public object FfiConverterULong: FfiConverter { + override fun lift(value: Long): ULong { + return value.toULong() + } + + override fun read(buf: ByteBuffer): ULong { + return lift(buf.getLong()) + } + + override fun lower(value: ULong): Long { + return value.toLong() + } + + override fun allocationSize(value: ULong) = 8UL + + override fun write(value: ULong, buf: ByteBuffer) { + buf.putLong(value.toLong()) + } +} + +/** + * @suppress + */ +public object FfiConverterString: FfiConverter { + // Note: we don't inherit from FfiConverterRustBuffer, because we use a + // special encoding when lowering/lifting. We can use `RustBuffer.len` to + // store our length and avoid writing it out to the buffer. + override fun lift(value: RustBuffer.ByValue): String { + try { + val byteArr = ByteArray(value.len.toInt()) + value.asByteBuffer()!!.get(byteArr) + return byteArr.toString(Charsets.UTF_8) + } finally { + RustBuffer.free(value) + } + } + + override fun read(buf: ByteBuffer): String { + val len = buf.getInt() + val byteArr = ByteArray(len) + buf.get(byteArr) + return byteArr.toString(Charsets.UTF_8) + } + + fun toUtf8(value: String): ByteBuffer { + // Make sure we don't have invalid UTF-16, check for lone surrogates. + return Charsets.UTF_8.newEncoder().run { + onMalformedInput(CodingErrorAction.REPORT) + encode(CharBuffer.wrap(value)) + } + } + + override fun lower(value: String): RustBuffer.ByValue { + val byteBuf = toUtf8(value) + // Ideally we'd pass these bytes to `ffi_bytebuffer_from_bytes`, but doing so would require us + // to copy them into a JNA `Memory`. So we might as well directly copy them into a `RustBuffer`. + val rbuf = RustBuffer.alloc(byteBuf.limit().toULong()) + rbuf.asByteBuffer()!!.put(byteBuf) + return rbuf + } + + // We aren't sure exactly how many bytes our string will be once it's UTF-8 + // encoded. Allocate 3 bytes per UTF-16 code unit which will always be + // enough. + override fun allocationSize(value: String): ULong { + val sizeForLength = 4UL + val sizeForString = value.length.toULong() * 3UL + return sizeForLength + sizeForString + } + + override fun write(value: String, buf: ByteBuffer) { + val byteBuf = toUtf8(value) + buf.putInt(byteBuf.limit()) + buf.put(byteBuf) + } +} + +/** + * @suppress + */ +public object FfiConverterByteArray: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): ByteArray { + val len = buf.getInt() + val byteArr = ByteArray(len) + buf.get(byteArr) + return byteArr + } + override fun allocationSize(value: ByteArray): ULong { + return 4UL + value.size.toULong() + } + override fun write(value: ByteArray, buf: ByteBuffer) { + buf.putInt(value.size) + buf.put(value) + } +} + + + +data class MnemonicError ( + var `kind`: MnemonicErrorKind, + var `expected`: kotlin.ULong?, + var `found`: kotlin.ULong? +) { + + companion object +} + +/** + * @suppress + */ +public object FfiConverterTypeMnemonicError: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): MnemonicError { + return MnemonicError( + FfiConverterTypeMnemonicErrorKind.read(buf), + FfiConverterOptionalULong.read(buf), + FfiConverterOptionalULong.read(buf), + ) + } + + override fun allocationSize(value: MnemonicError) = ( + FfiConverterTypeMnemonicErrorKind.allocationSize(value.`kind`) + + FfiConverterOptionalULong.allocationSize(value.`expected`) + + FfiConverterOptionalULong.allocationSize(value.`found`) + ) + + override fun write(value: MnemonicError, buf: ByteBuffer) { + FfiConverterTypeMnemonicErrorKind.write(value.`kind`, buf) + FfiConverterOptionalULong.write(value.`expected`, buf) + FfiConverterOptionalULong.write(value.`found`, buf) + } +} + + + + + +sealed class AlgoKitAlgo25Exception: kotlin.Exception() { + + class Exception( + + val `errMsg`: kotlin.String + ) : AlgoKitAlgo25Exception() { + override val message + get() = "errMsg=${ `errMsg` }" + } + + + companion object ErrorHandler : UniffiRustCallStatusErrorHandler { + override fun lift(error_buf: RustBuffer.ByValue): AlgoKitAlgo25Exception = FfiConverterTypeAlgoKitAlgo25Error.lift(error_buf) + } + + +} + +/** + * @suppress + */ +public object FfiConverterTypeAlgoKitAlgo25Error : FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): AlgoKitAlgo25Exception { + + + return when(buf.getInt()) { + 1 -> AlgoKitAlgo25Exception.Exception( + FfiConverterString.read(buf), + ) + else -> throw RuntimeException("invalid error enum value, something is very wrong!!") + } + } + + override fun allocationSize(value: AlgoKitAlgo25Exception): ULong { + return when(value) { + is AlgoKitAlgo25Exception.Exception -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + + FfiConverterString.allocationSize(value.`errMsg`) + ) + } + } + + override fun write(value: AlgoKitAlgo25Exception, buf: ByteBuffer) { + when(value) { + is AlgoKitAlgo25Exception.Exception -> { + buf.putInt(1) + FfiConverterString.write(value.`errMsg`, buf) + Unit + } + }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } + } + +} + + + + +enum class MnemonicErrorKind { + + INVALID_SEED_LENGTH, + NOT_IN_WORDS_LIST, + FAILED_TO_DECODE_MNEMONIC; + companion object +} + + +/** + * @suppress + */ +public object FfiConverterTypeMnemonicErrorKind: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer) = try { + MnemonicErrorKind.values()[buf.getInt() - 1] + } catch (e: IndexOutOfBoundsException) { + throw RuntimeException("invalid enum value, something is very wrong!!", e) + } + + override fun allocationSize(value: MnemonicErrorKind) = 4UL + + override fun write(value: MnemonicErrorKind, buf: ByteBuffer) { + buf.putInt(value.ordinal + 1) + } +} + + + + + + +/** + * @suppress + */ +public object FfiConverterOptionalULong: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): kotlin.ULong? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterULong.read(buf) + } + + override fun allocationSize(value: kotlin.ULong?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterULong.allocationSize(value) + } + } + + override fun write(value: kotlin.ULong?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterULong.write(value, buf) + } + } +} + @Throws(AlgoKitAlgo25Exception::class) fun `masterDerivationKeyToMnemonic`(`mdk`: kotlin.ByteArray): kotlin.String { + return FfiConverterString.lift( + uniffiRustCallWithError(AlgoKitAlgo25Exception) { _status -> + UniffiLib.INSTANCE.uniffi_algokit_algo25_ffi_fn_func_master_derivation_key_to_mnemonic( + FfiConverterByteArray.lower(`mdk`),_status) +} + ) + } + + + @Throws(AlgoKitAlgo25Exception::class) fun `mnemonicFromSeed`(`seed`: kotlin.ByteArray): kotlin.String { + return FfiConverterString.lift( + uniffiRustCallWithError(AlgoKitAlgo25Exception) { _status -> + UniffiLib.INSTANCE.uniffi_algokit_algo25_ffi_fn_func_mnemonic_from_seed( + FfiConverterByteArray.lower(`seed`),_status) +} + ) + } + + + @Throws(AlgoKitAlgo25Exception::class) fun `mnemonicToMasterDerivationKey`(`mnemonic`: kotlin.String): kotlin.ByteArray { + return FfiConverterByteArray.lift( + uniffiRustCallWithError(AlgoKitAlgo25Exception) { _status -> + UniffiLib.INSTANCE.uniffi_algokit_algo25_ffi_fn_func_mnemonic_to_master_derivation_key( + FfiConverterString.lower(`mnemonic`),_status) +} + ) + } + + + @Throws(AlgoKitAlgo25Exception::class) fun `secretKeyToMnemonic`(`secretKey`: kotlin.ByteArray): kotlin.String { + return FfiConverterString.lift( + uniffiRustCallWithError(AlgoKitAlgo25Exception) { _status -> + UniffiLib.INSTANCE.uniffi_algokit_algo25_ffi_fn_func_secret_key_to_mnemonic( + FfiConverterByteArray.lower(`secretKey`),_status) +} + ) + } + + + @Throws(AlgoKitAlgo25Exception::class) fun `seedFromMnemonic`(`mnemonic`: kotlin.String): kotlin.ByteArray { + return FfiConverterByteArray.lift( + uniffiRustCallWithError(AlgoKitAlgo25Exception) { _status -> + UniffiLib.INSTANCE.uniffi_algokit_algo25_ffi_fn_func_seed_from_mnemonic( + FfiConverterString.lower(`mnemonic`),_status) +} + ) + } + + + diff --git a/packages/android/algokit_crypto/gradle/libs.versions.toml b/packages/android/algokit_crypto/gradle/libs.versions.toml index 6526d598..b384b8b1 100644 --- a/packages/android/algokit_crypto/gradle/libs.versions.toml +++ b/packages/android/algokit_crypto/gradle/libs.versions.toml @@ -7,10 +7,6 @@ junitVersion = "1.2.1" espressoCore = "3.6.1" appcompat = "1.7.0" material = "1.12.0" -compose-bom = "2025.05.00" -compose-compiler = "2.0.21" -activity-compose = "1.10.1" -lifecycle-runtime-compose = "2.9.0" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -20,21 +16,7 @@ androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-co androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } -# Compose dependencies -androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" } -androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" } -androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } -androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } -androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" } -androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } -androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } -androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity-compose" } -androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle-runtime-compose" } - [plugins] android-application = { id = "com.android.application", version.ref = "agp" } android-library = { id = "com.android.library", version.ref = "agp" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } -compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } - - diff --git a/packages/android/algokit_crypto/src/main/kotlin/uniffi/algokit_crypto_ffi/algokit_crypto_ffi.kt b/packages/android/algokit_crypto/src/main/kotlin/uniffi/algokit_crypto_ffi/algokit_crypto_ffi.kt index 5ffc4763..f4a4ab0a 100644 --- a/packages/android/algokit_crypto/src/main/kotlin/uniffi/algokit_crypto_ffi/algokit_crypto_ffi.kt +++ b/packages/android/algokit_crypto/src/main/kotlin/uniffi/algokit_crypto_ffi/algokit_crypto_ffi.kt @@ -30,7 +30,6 @@ import java.nio.CharBuffer import java.nio.charset.CodingErrorAction import java.util.concurrent.atomic.AtomicLong import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.atomic.AtomicBoolean // This is a helper for safely working with byte buffers returned from the Rust code. // A rust-owned buffer is represented by its capacity, its current length, and a @@ -654,66 +653,6 @@ internal open class UniffiForeignFutureStructVoid( internal interface UniffiForeignFutureCompleteVoid : com.sun.jna.Callback { fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructVoid.UniffiByValue,) } -internal interface UniffiCallbackInterfaceEd25519KeyAndSignerMethod0 : com.sun.jna.Callback { - fun callback(`uniffiHandle`: Long,`msg`: RustBuffer.ByValue,`uniffiOutReturn`: RustBuffer,uniffiCallStatus: UniffiRustCallStatus,) -} -internal interface UniffiCallbackInterfaceEd25519KeyAndSignerMethod1 : com.sun.jna.Callback { - fun callback(`uniffiHandle`: Long,`uniffiOutReturn`: RustBuffer,uniffiCallStatus: UniffiRustCallStatus,) -} -internal interface UniffiCallbackInterfaceEd25519SignerMethod0 : com.sun.jna.Callback { - fun callback(`uniffiHandle`: Long,`msg`: RustBuffer.ByValue,`uniffiOutReturn`: RustBuffer,uniffiCallStatus: UniffiRustCallStatus,) -} -@Structure.FieldOrder("trySign", "verifyingKey", "uniffiFree") -internal open class UniffiVTableCallbackInterfaceEd25519KeyAndSigner( - @JvmField internal var `trySign`: UniffiCallbackInterfaceEd25519KeyAndSignerMethod0? = null, - @JvmField internal var `verifyingKey`: UniffiCallbackInterfaceEd25519KeyAndSignerMethod1? = null, - @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, -) : Structure() { - class UniffiByValue( - `trySign`: UniffiCallbackInterfaceEd25519KeyAndSignerMethod0? = null, - `verifyingKey`: UniffiCallbackInterfaceEd25519KeyAndSignerMethod1? = null, - `uniffiFree`: UniffiCallbackInterfaceFree? = null, - ): UniffiVTableCallbackInterfaceEd25519KeyAndSigner(`trySign`,`verifyingKey`,`uniffiFree`,), Structure.ByValue - - internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceEd25519KeyAndSigner) { - `trySign` = other.`trySign` - `verifyingKey` = other.`verifyingKey` - `uniffiFree` = other.`uniffiFree` - } - -} -@Structure.FieldOrder("trySign", "uniffiFree") -internal open class UniffiVTableCallbackInterfaceEd25519Signer( - @JvmField internal var `trySign`: UniffiCallbackInterfaceEd25519SignerMethod0? = null, - @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, -) : Structure() { - class UniffiByValue( - `trySign`: UniffiCallbackInterfaceEd25519SignerMethod0? = null, - `uniffiFree`: UniffiCallbackInterfaceFree? = null, - ): UniffiVTableCallbackInterfaceEd25519Signer(`trySign`,`uniffiFree`,), Structure.ByValue - - internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceEd25519Signer) { - `trySign` = other.`trySign` - `uniffiFree` = other.`uniffiFree` - } - -} - - - - - - - - - - - - - - - - @@ -791,17 +730,9 @@ internal open class UniffiVTableCallbackInterfaceEd25519Signer( // when the library is loaded. internal interface IntegrityCheckingUniffiLib : Library { // Integrity check functions only - fun uniffi_algokit_crypto_ffi_checksum_method_cryptoxideed25519keypair_try_sign( + fun uniffi_algokit_crypto_ffi_checksum_func_ed25519_public_key_from_seed( ): Short -fun uniffi_algokit_crypto_ffi_checksum_method_cryptoxideed25519keypair_verifying_key( -): Short -fun uniffi_algokit_crypto_ffi_checksum_method_ed25519keyandsigner_try_sign( -): Short -fun uniffi_algokit_crypto_ffi_checksum_method_ed25519keyandsigner_verifying_key( -): Short -fun uniffi_algokit_crypto_ffi_checksum_method_ed25519signer_try_sign( -): Short -fun uniffi_algokit_crypto_ffi_checksum_constructor_cryptoxideed25519keypair_try_generate( +fun uniffi_algokit_crypto_ffi_checksum_func_ed25519_raw_sign( ): Short fun ffi_algokit_crypto_ffi_uniffi_contract_version( ): Int @@ -841,46 +772,16 @@ internal interface UniffiLib : Library { val lib = loadIndirect(componentName) // No need to check the contract version and checksums, since // we already did that with `IntegrityCheckingUniffiLib` above. - uniffiCallbackInterfaceEd25519KeyAndSigner.register(lib) - uniffiCallbackInterfaceEd25519Signer.register(lib) // Loading of library with integrity check done. lib } - // The Cleaner for the whole library - internal val CLEANER: UniffiCleaner by lazy { - UniffiCleaner.create() - } } // FFI functions - fun uniffi_algokit_crypto_ffi_fn_clone_cryptoxideed25519keypair(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_algokit_crypto_ffi_fn_free_cryptoxideed25519keypair(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Unit -fun uniffi_algokit_crypto_ffi_fn_constructor_cryptoxideed25519keypair_try_generate(`seed`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_algokit_crypto_ffi_fn_method_cryptoxideed25519keypair_try_sign(`ptr`: Pointer,`msg`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, + fun uniffi_algokit_crypto_ffi_fn_func_ed25519_public_key_from_seed(`seed`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, ): RustBuffer.ByValue -fun uniffi_algokit_crypto_ffi_fn_method_cryptoxideed25519keypair_verifying_key(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_algokit_crypto_ffi_fn_clone_ed25519keyandsigner(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_algokit_crypto_ffi_fn_free_ed25519keyandsigner(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Unit -fun uniffi_algokit_crypto_ffi_fn_init_callback_vtable_ed25519keyandsigner(`vtable`: UniffiVTableCallbackInterfaceEd25519KeyAndSigner, -): Unit -fun uniffi_algokit_crypto_ffi_fn_method_ed25519keyandsigner_try_sign(`ptr`: Pointer,`msg`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_algokit_crypto_ffi_fn_method_ed25519keyandsigner_verifying_key(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_algokit_crypto_ffi_fn_clone_ed25519signer(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_algokit_crypto_ffi_fn_free_ed25519signer(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Unit -fun uniffi_algokit_crypto_ffi_fn_init_callback_vtable_ed25519signer(`vtable`: UniffiVTableCallbackInterfaceEd25519Signer, -): Unit -fun uniffi_algokit_crypto_ffi_fn_method_ed25519signer_try_sign(`ptr`: Pointer,`msg`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +fun uniffi_algokit_crypto_ffi_fn_func_ed25519_raw_sign(`secretKey`: RustBuffer.ByValue,`data`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, ): RustBuffer.ByValue fun ffi_algokit_crypto_ffi_rustbuffer_alloc(`size`: Long,uniffi_out_err: UniffiRustCallStatus, ): RustBuffer.ByValue @@ -1008,22 +909,10 @@ private fun uniffiCheckContractApiVersion(lib: IntegrityCheckingUniffiLib) { } @Suppress("UNUSED_PARAMETER") private fun uniffiCheckApiChecksums(lib: IntegrityCheckingUniffiLib) { - if (lib.uniffi_algokit_crypto_ffi_checksum_method_cryptoxideed25519keypair_try_sign() != 46579.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_algokit_crypto_ffi_checksum_method_cryptoxideed25519keypair_verifying_key() != 2056.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_algokit_crypto_ffi_checksum_method_ed25519keyandsigner_try_sign() != 14488.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_algokit_crypto_ffi_checksum_method_ed25519keyandsigner_verifying_key() != 42410.toShort()) { + if (lib.uniffi_algokit_crypto_ffi_checksum_func_ed25519_public_key_from_seed() != 13794.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_algokit_crypto_ffi_checksum_method_ed25519signer_try_sign() != 33107.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_algokit_crypto_ffi_checksum_constructor_cryptoxideed25519keypair_try_generate() != 49154.toShort()) { + if (lib.uniffi_algokit_crypto_ffi_checksum_func_ed25519_raw_sign() != 65210.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } } @@ -1103,102 +992,7 @@ inline fun T.use(block: (T) -> R) = * * @suppress * */ -object NoPointer// Magic number for the Rust proxy to call using the same mechanism as every other method, -// to free the callback once it's dropped by Rust. -internal const val IDX_CALLBACK_FREE = 0 -// Callback return codes -internal const val UNIFFI_CALLBACK_SUCCESS = 0 -internal const val UNIFFI_CALLBACK_ERROR = 1 -internal const val UNIFFI_CALLBACK_UNEXPECTED_ERROR = 2 - -/** - * @suppress - */ -public abstract class FfiConverterCallbackInterface: FfiConverter { - internal val handleMap = UniffiHandleMap() - - internal fun drop(handle: Long) { - handleMap.remove(handle) - } - - override fun lift(value: Long): CallbackInterface { - return handleMap.get(value) - } - - override fun read(buf: ByteBuffer) = lift(buf.getLong()) - - override fun lower(value: CallbackInterface) = handleMap.insert(value) - - override fun allocationSize(value: CallbackInterface) = 8UL - - override fun write(value: CallbackInterface, buf: ByteBuffer) { - buf.putLong(lower(value)) - } -} -/** - * The cleaner interface for Object finalization code to run. - * This is the entry point to any implementation that we're using. - * - * The cleaner registers objects and returns cleanables, so now we are - * defining a `UniffiCleaner` with a `UniffiClenaer.Cleanable` to abstract the - * different implmentations available at compile time. - * - * @suppress - */ -interface UniffiCleaner { - interface Cleanable { - fun clean() - } - - fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable - - companion object -} - -// The fallback Jna cleaner, which is available for both Android, and the JVM. -private class UniffiJnaCleaner : UniffiCleaner { - private val cleaner = com.sun.jna.internal.Cleaner.getCleaner() - - override fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable = - UniffiJnaCleanable(cleaner.register(value, cleanUpTask)) -} - -private class UniffiJnaCleanable( - private val cleanable: com.sun.jna.internal.Cleaner.Cleanable, -) : UniffiCleaner.Cleanable { - override fun clean() = cleanable.clean() -} - - -// We decide at uniffi binding generation time whether we were -// using Android or not. -// There are further runtime checks to chose the correct implementation -// of the cleaner. -private fun UniffiCleaner.Companion.create(): UniffiCleaner = - try { - // For safety's sake: if the library hasn't been run in android_cleaner = true - // mode, but is being run on Android, then we still need to think about - // Android API versions. - // So we check if java.lang.ref.Cleaner is there, and use that… - java.lang.Class.forName("java.lang.ref.Cleaner") - JavaLangRefCleaner() - } catch (e: ClassNotFoundException) { - // … otherwise, fallback to the JNA cleaner. - UniffiJnaCleaner() - } - -private class JavaLangRefCleaner : UniffiCleaner { - val cleaner = java.lang.ref.Cleaner.create() - - override fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable = - JavaLangRefCleanable(cleaner.register(value, cleanUpTask)) -} - -private class JavaLangRefCleanable( - val cleanable: java.lang.ref.Cleaner.Cleanable -) : UniffiCleaner.Cleanable { - override fun clean() = cleanable.clean() -} +object NoPointer /** * @suppress @@ -1277,903 +1071,6 @@ public object FfiConverterByteArray: FfiConverterRustBuffer { } -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - - -/** - * FFI-compatible wrapper for CryptoxideEd25519Keypair - * - * This struct wraps the Rust implementation and exposes it via FFI. - */ -public interface CryptoxideEd25519KeypairInterface { - - /** - * Sign a message asynchronously - */ - fun `trySign`(`msg`: kotlin.ByteArray): kotlin.ByteArray - - /** - * Get the verifying key (public key) as a byte vector - */ - fun `verifyingKey`(): kotlin.ByteArray - - companion object -} - -/** - * FFI-compatible wrapper for CryptoxideEd25519Keypair - * - * This struct wraps the Rust implementation and exposes it via FFI. - */ -open class CryptoxideEd25519Keypair: Disposable, AutoCloseable, CryptoxideEd25519KeypairInterface -{ - - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (! this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_algokit_crypto_ffi_fn_free_cryptoxideed25519keypair(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer { - return uniffiRustCall() { status -> - UniffiLib.INSTANCE.uniffi_algokit_crypto_ffi_fn_clone_cryptoxideed25519keypair(pointer!!, status) - } - } - - - /** - * Sign a message asynchronously - */ - @Throws(AlgoKitCryptoException::class)override fun `trySign`(`msg`: kotlin.ByteArray): kotlin.ByteArray { - return FfiConverterByteArray.lift( - callWithPointer { - uniffiRustCallWithError(AlgoKitCryptoException) { _status -> - UniffiLib.INSTANCE.uniffi_algokit_crypto_ffi_fn_method_cryptoxideed25519keypair_try_sign( - it, FfiConverterByteArray.lower(`msg`),_status) -} - } - ) - } - - - - /** - * Get the verifying key (public key) as a byte vector - */override fun `verifyingKey`(): kotlin.ByteArray { - return FfiConverterByteArray.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_algokit_crypto_ffi_fn_method_cryptoxideed25519keypair_verifying_key( - it, _status) -} - } - ) - } - - - - - - companion object { - - /** - * Generate a new keypair from an optional seed. - * If no seed is provided, a random seed is generated using the system's CSPRNG. - */ - @Throws(AlgoKitCryptoException::class) fun `tryGenerate`(`seed`: kotlin.ByteArray?): CryptoxideEd25519Keypair { - return FfiConverterTypeCryptoxideEd25519Keypair.lift( - uniffiRustCallWithError(AlgoKitCryptoException) { _status -> - UniffiLib.INSTANCE.uniffi_algokit_crypto_ffi_fn_constructor_cryptoxideed25519keypair_try_generate( - FfiConverterOptionalByteArray.lower(`seed`),_status) -} - ) - } - - - - } - -} - -/** - * @suppress - */ -public object FfiConverterTypeCryptoxideEd25519Keypair: FfiConverter { - - override fun lower(value: CryptoxideEd25519Keypair): Pointer { - return value.uniffiClonePointer() - } - - override fun lift(value: Pointer): CryptoxideEd25519Keypair { - return CryptoxideEd25519Keypair(value) - } - - override fun read(buf: ByteBuffer): CryptoxideEd25519Keypair { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: CryptoxideEd25519Keypair) = 8UL - - override fun write(value: CryptoxideEd25519Keypair, buf: ByteBuffer) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - - -/** - * FFI-compatible trait that combines signing and keypair operations for Ed25519 - * - * This trait is exported with `with_foreign` to allow foreign languages to implement it. - * Note: We don't use supertrait relationship with Ed25519SignerFfi because UniFFI's - * with_foreign doesn't support trait inheritance properly. Instead, we duplicate the - * try_sign method. - */ -public interface Ed25519KeyAndSigner { - - fun `trySign`(`msg`: kotlin.ByteArray): kotlin.ByteArray - - fun `verifyingKey`(): kotlin.ByteArray - - companion object -} - -/** - * FFI-compatible trait that combines signing and keypair operations for Ed25519 - * - * This trait is exported with `with_foreign` to allow foreign languages to implement it. - * Note: We don't use supertrait relationship with Ed25519SignerFfi because UniFFI's - * with_foreign doesn't support trait inheritance properly. Instead, we duplicate the - * try_sign method. - */ -open class Ed25519KeyAndSignerImpl: Disposable, AutoCloseable, Ed25519KeyAndSigner -{ - - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (! this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_algokit_crypto_ffi_fn_free_ed25519keyandsigner(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer { - return uniffiRustCall() { status -> - UniffiLib.INSTANCE.uniffi_algokit_crypto_ffi_fn_clone_ed25519keyandsigner(pointer!!, status) - } - } - - - @Throws(AlgoKitCryptoException::class)override fun `trySign`(`msg`: kotlin.ByteArray): kotlin.ByteArray { - return FfiConverterByteArray.lift( - callWithPointer { - uniffiRustCallWithError(AlgoKitCryptoException) { _status -> - UniffiLib.INSTANCE.uniffi_algokit_crypto_ffi_fn_method_ed25519keyandsigner_try_sign( - it, FfiConverterByteArray.lower(`msg`),_status) -} - } - ) - } - - - override fun `verifyingKey`(): kotlin.ByteArray { - return FfiConverterByteArray.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_algokit_crypto_ffi_fn_method_ed25519keyandsigner_verifying_key( - it, _status) -} - } - ) - } - - - - - - - companion object - -} - - -// Put the implementation in an object so we don't pollute the top-level namespace -internal object uniffiCallbackInterfaceEd25519KeyAndSigner { - internal object `trySign`: UniffiCallbackInterfaceEd25519KeyAndSignerMethod0 { - override fun callback(`uniffiHandle`: Long,`msg`: RustBuffer.ByValue,`uniffiOutReturn`: RustBuffer,uniffiCallStatus: UniffiRustCallStatus,) { - val uniffiObj = FfiConverterTypeEd25519KeyAndSigner.handleMap.get(uniffiHandle) - val makeCall = { -> - uniffiObj.`trySign`( - FfiConverterByteArray.lift(`msg`), - ) - } - val writeReturn = { value: kotlin.ByteArray -> uniffiOutReturn.setValue(FfiConverterByteArray.lower(value)) } - uniffiTraitInterfaceCallWithError( - uniffiCallStatus, - makeCall, - writeReturn, - { e: AlgoKitCryptoException -> FfiConverterTypeAlgoKitCryptoError.lower(e) } - ) - } - } - internal object `verifyingKey`: UniffiCallbackInterfaceEd25519KeyAndSignerMethod1 { - override fun callback(`uniffiHandle`: Long,`uniffiOutReturn`: RustBuffer,uniffiCallStatus: UniffiRustCallStatus,) { - val uniffiObj = FfiConverterTypeEd25519KeyAndSigner.handleMap.get(uniffiHandle) - val makeCall = { -> - uniffiObj.`verifyingKey`( - ) - } - val writeReturn = { value: kotlin.ByteArray -> uniffiOutReturn.setValue(FfiConverterByteArray.lower(value)) } - uniffiTraitInterfaceCall(uniffiCallStatus, makeCall, writeReturn) - } - } - - internal object uniffiFree: UniffiCallbackInterfaceFree { - override fun callback(handle: Long) { - FfiConverterTypeEd25519KeyAndSigner.handleMap.remove(handle) - } - } - - internal var vtable = UniffiVTableCallbackInterfaceEd25519KeyAndSigner.UniffiByValue( - `trySign`, - `verifyingKey`, - uniffiFree, - ) - - // Registers the foreign callback with the Rust side. - // This method is generated for each callback interface. - internal fun register(lib: UniffiLib) { - lib.uniffi_algokit_crypto_ffi_fn_init_callback_vtable_ed25519keyandsigner(vtable) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeEd25519KeyAndSigner: FfiConverter { - internal val handleMap = UniffiHandleMap() - - override fun lower(value: Ed25519KeyAndSigner): Pointer { - return Pointer(handleMap.insert(value)) - } - - override fun lift(value: Pointer): Ed25519KeyAndSigner { - return Ed25519KeyAndSignerImpl(value) - } - - override fun read(buf: ByteBuffer): Ed25519KeyAndSigner { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Ed25519KeyAndSigner) = 8UL - - override fun write(value: Ed25519KeyAndSigner, buf: ByteBuffer) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - - -/** - * FFI-compatible trait for Ed25519 signing operations - * - * This trait is exported with `with_foreign` to allow foreign languages (Python, Swift, Kotlin, etc.) - * to implement it and provide custom signing logic. - */ -public interface Ed25519Signer { - - fun `trySign`(`msg`: kotlin.ByteArray): kotlin.ByteArray - - companion object -} - -/** - * FFI-compatible trait for Ed25519 signing operations - * - * This trait is exported with `with_foreign` to allow foreign languages (Python, Swift, Kotlin, etc.) - * to implement it and provide custom signing logic. - */ -open class Ed25519SignerImpl: Disposable, AutoCloseable, Ed25519Signer -{ - - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (! this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_algokit_crypto_ffi_fn_free_ed25519signer(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer { - return uniffiRustCall() { status -> - UniffiLib.INSTANCE.uniffi_algokit_crypto_ffi_fn_clone_ed25519signer(pointer!!, status) - } - } - - - @Throws(AlgoKitCryptoException::class)override fun `trySign`(`msg`: kotlin.ByteArray): kotlin.ByteArray { - return FfiConverterByteArray.lift( - callWithPointer { - uniffiRustCallWithError(AlgoKitCryptoException) { _status -> - UniffiLib.INSTANCE.uniffi_algokit_crypto_ffi_fn_method_ed25519signer_try_sign( - it, FfiConverterByteArray.lower(`msg`),_status) -} - } - ) - } - - - - - - - companion object - -} - - -// Put the implementation in an object so we don't pollute the top-level namespace -internal object uniffiCallbackInterfaceEd25519Signer { - internal object `trySign`: UniffiCallbackInterfaceEd25519SignerMethod0 { - override fun callback(`uniffiHandle`: Long,`msg`: RustBuffer.ByValue,`uniffiOutReturn`: RustBuffer,uniffiCallStatus: UniffiRustCallStatus,) { - val uniffiObj = FfiConverterTypeEd25519Signer.handleMap.get(uniffiHandle) - val makeCall = { -> - uniffiObj.`trySign`( - FfiConverterByteArray.lift(`msg`), - ) - } - val writeReturn = { value: kotlin.ByteArray -> uniffiOutReturn.setValue(FfiConverterByteArray.lower(value)) } - uniffiTraitInterfaceCallWithError( - uniffiCallStatus, - makeCall, - writeReturn, - { e: AlgoKitCryptoException -> FfiConverterTypeAlgoKitCryptoError.lower(e) } - ) - } - } - - internal object uniffiFree: UniffiCallbackInterfaceFree { - override fun callback(handle: Long) { - FfiConverterTypeEd25519Signer.handleMap.remove(handle) - } - } - - internal var vtable = UniffiVTableCallbackInterfaceEd25519Signer.UniffiByValue( - `trySign`, - uniffiFree, - ) - - // Registers the foreign callback with the Rust side. - // This method is generated for each callback interface. - internal fun register(lib: UniffiLib) { - lib.uniffi_algokit_crypto_ffi_fn_init_callback_vtable_ed25519signer(vtable) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeEd25519Signer: FfiConverter { - internal val handleMap = UniffiHandleMap() - - override fun lower(value: Ed25519Signer): Pointer { - return Pointer(handleMap.insert(value)) - } - - override fun lift(value: Pointer): Ed25519Signer { - return Ed25519SignerImpl(value) - } - - override fun read(buf: ByteBuffer): Ed25519Signer { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Ed25519Signer) = 8UL - - override fun write(value: Ed25519Signer, buf: ByteBuffer) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - - @@ -2234,36 +1131,24 @@ public object FfiConverterTypeAlgoKitCryptoError : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): kotlin.ByteArray? { - if (buf.get().toInt() == 0) { - return null - } - return FfiConverterByteArray.read(buf) + @Throws(AlgoKitCryptoException::class) fun `ed25519PublicKeyFromSeed`(`seed`: kotlin.ByteArray): kotlin.ByteArray { + return FfiConverterByteArray.lift( + uniffiRustCallWithError(AlgoKitCryptoException) { _status -> + UniffiLib.INSTANCE.uniffi_algokit_crypto_ffi_fn_func_ed25519_public_key_from_seed( + FfiConverterByteArray.lower(`seed`),_status) +} + ) } + - override fun allocationSize(value: kotlin.ByteArray?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterByteArray.allocationSize(value) - } + @Throws(AlgoKitCryptoException::class) fun `ed25519RawSign`(`secretKey`: kotlin.ByteArray, `data`: kotlin.ByteArray): kotlin.ByteArray { + return FfiConverterByteArray.lift( + uniffiRustCallWithError(AlgoKitCryptoException) { _status -> + UniffiLib.INSTANCE.uniffi_algokit_crypto_ffi_fn_func_ed25519_raw_sign( + FfiConverterByteArray.lower(`secretKey`),FfiConverterByteArray.lower(`data`),_status) +} + ) } + - override fun write(value: kotlin.ByteArray?, buf: ByteBuffer) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterByteArray.write(value, buf) - } - } -} diff --git a/packages/android/algokit_transact/gradle/libs.versions.toml b/packages/android/algokit_transact/gradle/libs.versions.toml index 6526d598..ff68a35c 100644 --- a/packages/android/algokit_transact/gradle/libs.versions.toml +++ b/packages/android/algokit_transact/gradle/libs.versions.toml @@ -7,10 +7,6 @@ junitVersion = "1.2.1" espressoCore = "3.6.1" appcompat = "1.7.0" material = "1.12.0" -compose-bom = "2025.05.00" -compose-compiler = "2.0.21" -activity-compose = "1.10.1" -lifecycle-runtime-compose = "2.9.0" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -20,21 +16,9 @@ androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-co androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } -# Compose dependencies -androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" } -androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" } -androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } -androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } -androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" } -androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } -androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } -androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity-compose" } -androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle-runtime-compose" } - [plugins] android-application = { id = "com.android.application", version.ref = "agp" } android-library = { id = "com.android.library", version.ref = "agp" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } -compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } diff --git a/packages/swift/AlgoKitAlgo25/.gitignore b/packages/swift/AlgoKitAlgo25/.gitignore new file mode 100644 index 00000000..0023a534 --- /dev/null +++ b/packages/swift/AlgoKitAlgo25/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/packages/swift/AlgoKitAlgo25/Package.swift b/packages/swift/AlgoKitAlgo25/Package.swift new file mode 100644 index 00000000..7d9f197c --- /dev/null +++ b/packages/swift/AlgoKitAlgo25/Package.swift @@ -0,0 +1,33 @@ +// swift-tools-version: 6.0 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "AlgoKitAlgo25", + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "AlgoKitAlgo25", + targets: ["AlgoKitAlgo25"]) + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .binaryTarget( + name: "algokit_algo25FFI", + path: "Frameworks/algokit_algo25.xcframework" + ), + .target( + name: "AlgoKitAlgo25", + dependencies: ["algokit_algo25FFI"], + path: "Sources/AlgoKitAlgo25" + ), + .testTarget( + name: "AlgoKitAlgo25Tests", + dependencies: [ + "AlgoKitAlgo25" + ], + ), + ] +) diff --git a/packages/swift/AlgoKitAlgo25/Sources/AlgoKitAlgo25/AlgokitAlgo25.swift b/packages/swift/AlgoKitAlgo25/Sources/AlgoKitAlgo25/AlgokitAlgo25.swift new file mode 100644 index 00000000..0f5b9b13 --- /dev/null +++ b/packages/swift/AlgoKitAlgo25/Sources/AlgoKitAlgo25/AlgokitAlgo25.swift @@ -0,0 +1,812 @@ +// This file was autogenerated by some hot garbage in the `uniffi` crate. +// Trust me, you don't want to mess with it! + +// swiftlint:disable all +import Foundation + +// Depending on the consumer's build setup, the low-level FFI code +// might be in a separate module, or it might be compiled inline into +// this module. This is a bit of light hackery to work with both. +#if canImport(algokit_algo25FFI) +import algokit_algo25FFI +#endif + +fileprivate extension RustBuffer { + // Allocate a new buffer, copying the contents of a `UInt8` array. + init(bytes: [UInt8]) { + let rbuf = bytes.withUnsafeBufferPointer { ptr in + RustBuffer.from(ptr) + } + self.init(capacity: rbuf.capacity, len: rbuf.len, data: rbuf.data) + } + + static func empty() -> RustBuffer { + RustBuffer(capacity: 0, len:0, data: nil) + } + + static func from(_ ptr: UnsafeBufferPointer) -> RustBuffer { + try! rustCall { ffi_algokit_algo25_ffi_rustbuffer_from_bytes(ForeignBytes(bufferPointer: ptr), $0) } + } + + // Frees the buffer in place. + // The buffer must not be used after this is called. + func deallocate() { + try! rustCall { ffi_algokit_algo25_ffi_rustbuffer_free(self, $0) } + } +} + +fileprivate extension ForeignBytes { + init(bufferPointer: UnsafeBufferPointer) { + self.init(len: Int32(bufferPointer.count), data: bufferPointer.baseAddress) + } +} + +// For every type used in the interface, we provide helper methods for conveniently +// lifting and lowering that type from C-compatible data, and for reading and writing +// values of that type in a buffer. + +// Helper classes/extensions that don't change. +// Someday, this will be in a library of its own. + +fileprivate extension Data { + init(rustBuffer: RustBuffer) { + self.init( + bytesNoCopy: rustBuffer.data!, + count: Int(rustBuffer.len), + deallocator: .none + ) + } +} + +// Define reader functionality. Normally this would be defined in a class or +// struct, but we use standalone functions instead in order to make external +// types work. +// +// With external types, one swift source file needs to be able to call the read +// method on another source file's FfiConverter, but then what visibility +// should Reader have? +// - If Reader is fileprivate, then this means the read() must also +// be fileprivate, which doesn't work with external types. +// - If Reader is internal/public, we'll get compile errors since both source +// files will try define the same type. +// +// Instead, the read() method and these helper functions input a tuple of data + +fileprivate func createReader(data: Data) -> (data: Data, offset: Data.Index) { + (data: data, offset: 0) +} + +// Reads an integer at the current offset, in big-endian order, and advances +// the offset on success. Throws if reading the integer would move the +// offset past the end of the buffer. +fileprivate func readInt(_ reader: inout (data: Data, offset: Data.Index)) throws -> T { + let range = reader.offset...size + guard reader.data.count >= range.upperBound else { + throw UniffiInternalError.bufferOverflow + } + if T.self == UInt8.self { + let value = reader.data[reader.offset] + reader.offset += 1 + return value as! T + } + var value: T = 0 + let _ = withUnsafeMutableBytes(of: &value, { reader.data.copyBytes(to: $0, from: range)}) + reader.offset = range.upperBound + return value.bigEndian +} + +// Reads an arbitrary number of bytes, to be used to read +// raw bytes, this is useful when lifting strings +fileprivate func readBytes(_ reader: inout (data: Data, offset: Data.Index), count: Int) throws -> Array { + let range = reader.offset..<(reader.offset+count) + guard reader.data.count >= range.upperBound else { + throw UniffiInternalError.bufferOverflow + } + var value = [UInt8](repeating: 0, count: count) + value.withUnsafeMutableBufferPointer({ buffer in + reader.data.copyBytes(to: buffer, from: range) + }) + reader.offset = range.upperBound + return value +} + +// Reads a float at the current offset. +fileprivate func readFloat(_ reader: inout (data: Data, offset: Data.Index)) throws -> Float { + return Float(bitPattern: try readInt(&reader)) +} + +// Reads a float at the current offset. +fileprivate func readDouble(_ reader: inout (data: Data, offset: Data.Index)) throws -> Double { + return Double(bitPattern: try readInt(&reader)) +} + +// Indicates if the offset has reached the end of the buffer. +fileprivate func hasRemaining(_ reader: (data: Data, offset: Data.Index)) -> Bool { + return reader.offset < reader.data.count +} + +// Define writer functionality. Normally this would be defined in a class or +// struct, but we use standalone functions instead in order to make external +// types work. See the above discussion on Readers for details. + +fileprivate func createWriter() -> [UInt8] { + return [] +} + +fileprivate func writeBytes(_ writer: inout [UInt8], _ byteArr: S) where S: Sequence, S.Element == UInt8 { + writer.append(contentsOf: byteArr) +} + +// Writes an integer in big-endian order. +// +// Warning: make sure what you are trying to write +// is in the correct type! +fileprivate func writeInt(_ writer: inout [UInt8], _ value: T) { + var value = value.bigEndian + withUnsafeBytes(of: &value) { writer.append(contentsOf: $0) } +} + +fileprivate func writeFloat(_ writer: inout [UInt8], _ value: Float) { + writeInt(&writer, value.bitPattern) +} + +fileprivate func writeDouble(_ writer: inout [UInt8], _ value: Double) { + writeInt(&writer, value.bitPattern) +} + +// Protocol for types that transfer other types across the FFI. This is +// analogous to the Rust trait of the same name. +fileprivate protocol FfiConverter { + associatedtype FfiType + associatedtype SwiftType + + static func lift(_ value: FfiType) throws -> SwiftType + static func lower(_ value: SwiftType) -> FfiType + static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType + static func write(_ value: SwiftType, into buf: inout [UInt8]) +} + +// Types conforming to `Primitive` pass themselves directly over the FFI. +fileprivate protocol FfiConverterPrimitive: FfiConverter where FfiType == SwiftType { } + +extension FfiConverterPrimitive { +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public static func lift(_ value: FfiType) throws -> SwiftType { + return value + } + +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public static func lower(_ value: SwiftType) -> FfiType { + return value + } +} + +// Types conforming to `FfiConverterRustBuffer` lift and lower into a `RustBuffer`. +// Used for complex types where it's hard to write a custom lift/lower. +fileprivate protocol FfiConverterRustBuffer: FfiConverter where FfiType == RustBuffer {} + +extension FfiConverterRustBuffer { +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public static func lift(_ buf: RustBuffer) throws -> SwiftType { + var reader = createReader(data: Data(rustBuffer: buf)) + let value = try read(from: &reader) + if hasRemaining(reader) { + throw UniffiInternalError.incompleteData + } + buf.deallocate() + return value + } + +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public static func lower(_ value: SwiftType) -> RustBuffer { + var writer = createWriter() + write(value, into: &writer) + return RustBuffer(bytes: writer) + } +} +// An error type for FFI errors. These errors occur at the UniFFI level, not +// the library level. +fileprivate enum UniffiInternalError: LocalizedError { + case bufferOverflow + case incompleteData + case unexpectedOptionalTag + case unexpectedEnumCase + case unexpectedNullPointer + case unexpectedRustCallStatusCode + case unexpectedRustCallError + case unexpectedStaleHandle + case rustPanic(_ message: String) + + public var errorDescription: String? { + switch self { + case .bufferOverflow: return "Reading the requested value would read past the end of the buffer" + case .incompleteData: return "The buffer still has data after lifting its containing value" + case .unexpectedOptionalTag: return "Unexpected optional tag; should be 0 or 1" + case .unexpectedEnumCase: return "Raw enum value doesn't match any cases" + case .unexpectedNullPointer: return "Raw pointer value was null" + case .unexpectedRustCallStatusCode: return "Unexpected RustCallStatus code" + case .unexpectedRustCallError: return "CALL_ERROR but no errorClass specified" + case .unexpectedStaleHandle: return "The object in the handle map has been dropped already" + case let .rustPanic(message): return message + } + } +} + +fileprivate extension NSLock { + func withLock(f: () throws -> T) rethrows -> T { + self.lock() + defer { self.unlock() } + return try f() + } +} + +fileprivate let CALL_SUCCESS: Int8 = 0 +fileprivate let CALL_ERROR: Int8 = 1 +fileprivate let CALL_UNEXPECTED_ERROR: Int8 = 2 +fileprivate let CALL_CANCELLED: Int8 = 3 + +fileprivate extension RustCallStatus { + init() { + self.init( + code: CALL_SUCCESS, + errorBuf: RustBuffer.init( + capacity: 0, + len: 0, + data: nil + ) + ) + } +} + +private func rustCall(_ callback: (UnsafeMutablePointer) -> T) throws -> T { + let neverThrow: ((RustBuffer) throws -> Never)? = nil + return try makeRustCall(callback, errorHandler: neverThrow) +} + +private func rustCallWithError( + _ errorHandler: @escaping (RustBuffer) throws -> E, + _ callback: (UnsafeMutablePointer) -> T) throws -> T { + try makeRustCall(callback, errorHandler: errorHandler) +} + +private func makeRustCall( + _ callback: (UnsafeMutablePointer) -> T, + errorHandler: ((RustBuffer) throws -> E)? +) throws -> T { + uniffiEnsureAlgokitAlgo25FfiInitialized() + var callStatus = RustCallStatus.init() + let returnedVal = callback(&callStatus) + try uniffiCheckCallStatus(callStatus: callStatus, errorHandler: errorHandler) + return returnedVal +} + +private func uniffiCheckCallStatus( + callStatus: RustCallStatus, + errorHandler: ((RustBuffer) throws -> E)? +) throws { + switch callStatus.code { + case CALL_SUCCESS: + return + + case CALL_ERROR: + if let errorHandler = errorHandler { + throw try errorHandler(callStatus.errorBuf) + } else { + callStatus.errorBuf.deallocate() + throw UniffiInternalError.unexpectedRustCallError + } + + case CALL_UNEXPECTED_ERROR: + // When the rust code sees a panic, it tries to construct a RustBuffer + // with the message. But if that code panics, then it just sends back + // an empty buffer. + if callStatus.errorBuf.len > 0 { + throw UniffiInternalError.rustPanic(try FfiConverterString.lift(callStatus.errorBuf)) + } else { + callStatus.errorBuf.deallocate() + throw UniffiInternalError.rustPanic("Rust panic") + } + + case CALL_CANCELLED: + fatalError("Cancellation not supported yet") + + default: + throw UniffiInternalError.unexpectedRustCallStatusCode + } +} + +private func uniffiTraitInterfaceCall( + callStatus: UnsafeMutablePointer, + makeCall: () throws -> T, + writeReturn: (T) -> () +) { + do { + try writeReturn(makeCall()) + } catch let error { + callStatus.pointee.code = CALL_UNEXPECTED_ERROR + callStatus.pointee.errorBuf = FfiConverterString.lower(String(describing: error)) + } +} + +private func uniffiTraitInterfaceCallWithError( + callStatus: UnsafeMutablePointer, + makeCall: () throws -> T, + writeReturn: (T) -> (), + lowerError: (E) -> RustBuffer +) { + do { + try writeReturn(makeCall()) + } catch let error as E { + callStatus.pointee.code = CALL_ERROR + callStatus.pointee.errorBuf = lowerError(error) + } catch { + callStatus.pointee.code = CALL_UNEXPECTED_ERROR + callStatus.pointee.errorBuf = FfiConverterString.lower(String(describing: error)) + } +} +fileprivate final class UniffiHandleMap: @unchecked Sendable { + // All mutation happens with this lock held, which is why we implement @unchecked Sendable. + private let lock = NSLock() + private var map: [UInt64: T] = [:] + private var currentHandle: UInt64 = 1 + + func insert(obj: T) -> UInt64 { + lock.withLock { + let handle = currentHandle + currentHandle += 1 + map[handle] = obj + return handle + } + } + + func get(handle: UInt64) throws -> T { + try lock.withLock { + guard let obj = map[handle] else { + throw UniffiInternalError.unexpectedStaleHandle + } + return obj + } + } + + @discardableResult + func remove(handle: UInt64) throws -> T { + try lock.withLock { + guard let obj = map.removeValue(forKey: handle) else { + throw UniffiInternalError.unexpectedStaleHandle + } + return obj + } + } + + var count: Int { + get { + map.count + } + } +} + + +// Public interface members begin here. + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterUInt64: FfiConverterPrimitive { + typealias FfiType = UInt64 + typealias SwiftType = UInt64 + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt64 { + return try lift(readInt(&buf)) + } + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterString: FfiConverter { + typealias SwiftType = String + typealias FfiType = RustBuffer + + public static func lift(_ value: RustBuffer) throws -> String { + defer { + value.deallocate() + } + if value.data == nil { + return String() + } + let bytes = UnsafeBufferPointer(start: value.data!, count: Int(value.len)) + return String(bytes: bytes, encoding: String.Encoding.utf8)! + } + + public static func lower(_ value: String) -> RustBuffer { + return value.utf8CString.withUnsafeBufferPointer { ptr in + // The swift string gives us int8_t, we want uint8_t. + ptr.withMemoryRebound(to: UInt8.self) { ptr in + // The swift string gives us a trailing null byte, we don't want it. + let buf = UnsafeBufferPointer(rebasing: ptr.prefix(upTo: ptr.count - 1)) + return RustBuffer.from(buf) + } + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> String { + let len: Int32 = try readInt(&buf) + return String(bytes: try readBytes(&buf, count: Int(len)), encoding: String.Encoding.utf8)! + } + + public static func write(_ value: String, into buf: inout [UInt8]) { + let len = Int32(value.utf8.count) + writeInt(&buf, len) + writeBytes(&buf, value.utf8) + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterData: FfiConverterRustBuffer { + typealias SwiftType = Data + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Data { + let len: Int32 = try readInt(&buf) + return Data(try readBytes(&buf, count: Int(len))) + } + + public static func write(_ value: Data, into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + writeBytes(&buf, value) + } +} + + +public struct MnemonicError { + public var kind: MnemonicErrorKind + public var expected: UInt64? + public var found: UInt64? + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(kind: MnemonicErrorKind, expected: UInt64?, found: UInt64?) { + self.kind = kind + self.expected = expected + self.found = found + } +} + +#if compiler(>=6) +extension MnemonicError: Sendable {} +#endif + + +extension MnemonicError: Equatable, Hashable { + public static func ==(lhs: MnemonicError, rhs: MnemonicError) -> Bool { + if lhs.kind != rhs.kind { + return false + } + if lhs.expected != rhs.expected { + return false + } + if lhs.found != rhs.found { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(kind) + hasher.combine(expected) + hasher.combine(found) + } +} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeMnemonicError: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MnemonicError { + return + try MnemonicError( + kind: FfiConverterTypeMnemonicErrorKind.read(from: &buf), + expected: FfiConverterOptionUInt64.read(from: &buf), + found: FfiConverterOptionUInt64.read(from: &buf) + ) + } + + public static func write(_ value: MnemonicError, into buf: inout [UInt8]) { + FfiConverterTypeMnemonicErrorKind.write(value.kind, into: &buf) + FfiConverterOptionUInt64.write(value.expected, into: &buf) + FfiConverterOptionUInt64.write(value.found, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeMnemonicError_lift(_ buf: RustBuffer) throws -> MnemonicError { + return try FfiConverterTypeMnemonicError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeMnemonicError_lower(_ value: MnemonicError) -> RustBuffer { + return FfiConverterTypeMnemonicError.lower(value) +} + + +public enum AlgoKitAlgo25Error: Swift.Error { + + + + case Error(errMsg: String + ) +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeAlgoKitAlgo25Error: FfiConverterRustBuffer { + typealias SwiftType = AlgoKitAlgo25Error + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AlgoKitAlgo25Error { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .Error( + errMsg: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: AlgoKitAlgo25Error, into buf: inout [UInt8]) { + switch value { + + + + + + case let .Error(errMsg): + writeInt(&buf, Int32(1)) + FfiConverterString.write(errMsg, into: &buf) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeAlgoKitAlgo25Error_lift(_ buf: RustBuffer) throws -> AlgoKitAlgo25Error { + return try FfiConverterTypeAlgoKitAlgo25Error.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeAlgoKitAlgo25Error_lower(_ value: AlgoKitAlgo25Error) -> RustBuffer { + return FfiConverterTypeAlgoKitAlgo25Error.lower(value) +} + + +extension AlgoKitAlgo25Error: Equatable, Hashable {} + + + + +extension AlgoKitAlgo25Error: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum MnemonicErrorKind { + + case invalidSeedLength + case notInWordsList + case failedToDecodeMnemonic +} + + +#if compiler(>=6) +extension MnemonicErrorKind: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeMnemonicErrorKind: FfiConverterRustBuffer { + typealias SwiftType = MnemonicErrorKind + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MnemonicErrorKind { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .invalidSeedLength + + case 2: return .notInWordsList + + case 3: return .failedToDecodeMnemonic + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: MnemonicErrorKind, into buf: inout [UInt8]) { + switch value { + + + case .invalidSeedLength: + writeInt(&buf, Int32(1)) + + + case .notInWordsList: + writeInt(&buf, Int32(2)) + + + case .failedToDecodeMnemonic: + writeInt(&buf, Int32(3)) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeMnemonicErrorKind_lift(_ buf: RustBuffer) throws -> MnemonicErrorKind { + return try FfiConverterTypeMnemonicErrorKind.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeMnemonicErrorKind_lower(_ value: MnemonicErrorKind) -> RustBuffer { + return FfiConverterTypeMnemonicErrorKind.lower(value) +} + + +extension MnemonicErrorKind: Equatable, Hashable {} + + + + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterOptionUInt64: FfiConverterRustBuffer { + typealias SwiftType = UInt64? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterUInt64.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterUInt64.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} +public func masterDerivationKeyToMnemonic(mdk: Data)throws -> String { + return try FfiConverterString.lift(try rustCallWithError(FfiConverterTypeAlgoKitAlgo25Error_lift) { + uniffi_algokit_algo25_ffi_fn_func_master_derivation_key_to_mnemonic( + FfiConverterData.lower(mdk),$0 + ) +}) +} +public func mnemonicFromSeed(seed: Data)throws -> String { + return try FfiConverterString.lift(try rustCallWithError(FfiConverterTypeAlgoKitAlgo25Error_lift) { + uniffi_algokit_algo25_ffi_fn_func_mnemonic_from_seed( + FfiConverterData.lower(seed),$0 + ) +}) +} +public func mnemonicToMasterDerivationKey(mnemonic: String)throws -> Data { + return try FfiConverterData.lift(try rustCallWithError(FfiConverterTypeAlgoKitAlgo25Error_lift) { + uniffi_algokit_algo25_ffi_fn_func_mnemonic_to_master_derivation_key( + FfiConverterString.lower(mnemonic),$0 + ) +}) +} +public func secretKeyToMnemonic(secretKey: Data)throws -> String { + return try FfiConverterString.lift(try rustCallWithError(FfiConverterTypeAlgoKitAlgo25Error_lift) { + uniffi_algokit_algo25_ffi_fn_func_secret_key_to_mnemonic( + FfiConverterData.lower(secretKey),$0 + ) +}) +} +public func seedFromMnemonic(mnemonic: String)throws -> Data { + return try FfiConverterData.lift(try rustCallWithError(FfiConverterTypeAlgoKitAlgo25Error_lift) { + uniffi_algokit_algo25_ffi_fn_func_seed_from_mnemonic( + FfiConverterString.lower(mnemonic),$0 + ) +}) +} + +private enum InitializationResult { + case ok + case contractVersionMismatch + case apiChecksumMismatch +} +// Use a global variable to perform the versioning checks. Swift ensures that +// the code inside is only computed once. +private let initializationResult: InitializationResult = { + // Get the bindings contract version from our ComponentInterface + let bindings_contract_version = 29 + // Get the scaffolding contract version by calling the into the dylib + let scaffolding_contract_version = ffi_algokit_algo25_ffi_uniffi_contract_version() + if bindings_contract_version != scaffolding_contract_version { + return InitializationResult.contractVersionMismatch + } + if (uniffi_algokit_algo25_ffi_checksum_func_master_derivation_key_to_mnemonic() != 32168) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_algokit_algo25_ffi_checksum_func_mnemonic_from_seed() != 8214) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_algokit_algo25_ffi_checksum_func_mnemonic_to_master_derivation_key() != 50816) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_algokit_algo25_ffi_checksum_func_secret_key_to_mnemonic() != 57306) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_algokit_algo25_ffi_checksum_func_seed_from_mnemonic() != 29635) { + return InitializationResult.apiChecksumMismatch + } + + return InitializationResult.ok +}() + +// Make the ensure init function public so that other modules which have external type references to +// our types can call it. +public func uniffiEnsureAlgokitAlgo25FfiInitialized() { + switch initializationResult { + case .ok: + break + case .contractVersionMismatch: + fatalError("UniFFI contract version mismatch: try cleaning and rebuilding your project") + case .apiChecksumMismatch: + fatalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } +} + +// swiftlint:enable all \ No newline at end of file diff --git a/packages/swift/AlgoKitAlgo25/Tests/AlgoKitAlgo25Tests/AlgoKitAlgo25Tests.swift b/packages/swift/AlgoKitAlgo25/Tests/AlgoKitAlgo25Tests/AlgoKitAlgo25Tests.swift new file mode 100644 index 00000000..66d3ab0d --- /dev/null +++ b/packages/swift/AlgoKitAlgo25/Tests/AlgoKitAlgo25Tests/AlgoKitAlgo25Tests.swift @@ -0,0 +1,6 @@ +import Testing +@testable import AlgoKitAlgo25 + +@Test func example() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. +} diff --git a/packages/swift/AlgoKitCrypto/Sources/AlgoKitCrypto/AlgoKitCrypto.swift b/packages/swift/AlgoKitCrypto/Sources/AlgoKitCrypto/AlgoKitCrypto.swift index 9f17be30..d5a387f6 100644 --- a/packages/swift/AlgoKitCrypto/Sources/AlgoKitCrypto/AlgoKitCrypto.swift +++ b/packages/swift/AlgoKitCrypto/Sources/AlgoKitCrypto/AlgoKitCrypto.swift @@ -395,13 +395,7 @@ fileprivate final class UniffiHandleMap: @unchecked Sendable { // Public interface members begin here. -// Magic number for the Rust proxy to call using the same mechanism as every other method, -// to free the callback once it's dropped by Rust. -private let IDX_CALLBACK_FREE: Int32 = 0 -// Callback return codes -private let UNIFFI_CALLBACK_SUCCESS: Int32 = 0 -private let UNIFFI_CALLBACK_ERROR: Int32 = 1 -private let UNIFFI_CALLBACK_UNEXPECTED_ERROR: Int32 = 2 + #if swift(>=5.8) @_documentation(visibility: private) @@ -463,581 +457,6 @@ fileprivate struct FfiConverterData: FfiConverterRustBuffer { } - - -/** - * FFI-compatible wrapper for CryptoxideEd25519Keypair - * - * This struct wraps the Rust implementation and exposes it via FFI. - */ -public protocol CryptoxideEd25519KeypairProtocol: AnyObject, Sendable { - - /** - * Sign a message asynchronously - */ - func trySign(msg: Data) throws -> Data - - /** - * Get the verifying key (public key) as a byte vector - */ - func verifyingKey() -> Data - -} -/** - * FFI-compatible wrapper for CryptoxideEd25519Keypair - * - * This struct wraps the Rust implementation and exposes it via FFI. - */ -open class CryptoxideEd25519Keypair: CryptoxideEd25519KeypairProtocol, @unchecked Sendable { - fileprivate let pointer: UnsafeMutableRawPointer! - - /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. -#if swift(>=5.8) - @_documentation(visibility: private) -#endif - public struct NoPointer { - public init() {} - } - - // TODO: We'd like this to be `private` but for Swifty reasons, - // we can't implement `FfiConverter` without making this `required` and we can't - // make it `required` without making it `public`. -#if swift(>=5.8) - @_documentation(visibility: private) -#endif - required public init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { - self.pointer = pointer - } - - // This constructor can be used to instantiate a fake object. - // - Parameter noPointer: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject]. - // - // - Warning: - // Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing [Pointer] the FFI lower functions will crash. -#if swift(>=5.8) - @_documentation(visibility: private) -#endif - public init(noPointer: NoPointer) { - self.pointer = nil - } - -#if swift(>=5.8) - @_documentation(visibility: private) -#endif - public func uniffiClonePointer() -> UnsafeMutableRawPointer { - return try! rustCall { uniffi_algokit_crypto_ffi_fn_clone_cryptoxideed25519keypair(self.pointer, $0) } - } - // No primary constructor declared for this class. - - deinit { - guard let pointer = pointer else { - return - } - - try! rustCall { uniffi_algokit_crypto_ffi_fn_free_cryptoxideed25519keypair(pointer, $0) } - } - - - /** - * Generate a new keypair from an optional seed. - * If no seed is provided, a random seed is generated using the system's CSPRNG. - */ -public static func tryGenerate(seed: Data?)throws -> CryptoxideEd25519Keypair { - return try FfiConverterTypeCryptoxideEd25519Keypair_lift(try rustCallWithError(FfiConverterTypeAlgoKitCryptoError_lift) { - uniffi_algokit_crypto_ffi_fn_constructor_cryptoxideed25519keypair_try_generate( - FfiConverterOptionData.lower(seed),$0 - ) -}) -} - - - - /** - * Sign a message asynchronously - */ -open func trySign(msg: Data)throws -> Data { - return try FfiConverterData.lift(try rustCallWithError(FfiConverterTypeAlgoKitCryptoError_lift) { - uniffi_algokit_crypto_ffi_fn_method_cryptoxideed25519keypair_try_sign(self.uniffiClonePointer(), - FfiConverterData.lower(msg),$0 - ) -}) -} - - /** - * Get the verifying key (public key) as a byte vector - */ -open func verifyingKey() -> Data { - return try! FfiConverterData.lift(try! rustCall() { - uniffi_algokit_crypto_ffi_fn_method_cryptoxideed25519keypair_verifying_key(self.uniffiClonePointer(),$0 - ) -}) -} - - -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeCryptoxideEd25519Keypair: FfiConverter { - - typealias FfiType = UnsafeMutableRawPointer - typealias SwiftType = CryptoxideEd25519Keypair - - public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> CryptoxideEd25519Keypair { - return CryptoxideEd25519Keypair(unsafeFromRawPointer: pointer) - } - - public static func lower(_ value: CryptoxideEd25519Keypair) -> UnsafeMutableRawPointer { - return value.uniffiClonePointer() - } - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CryptoxideEd25519Keypair { - let v: UInt64 = try readInt(&buf) - // The Rust code won't compile if a pointer won't fit in a UInt64. - // We have to go via `UInt` because that's the thing that's the size of a pointer. - let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v)) - if (ptr == nil) { - throw UniffiInternalError.unexpectedNullPointer - } - return try lift(ptr!) - } - - public static func write(_ value: CryptoxideEd25519Keypair, into buf: inout [UInt8]) { - // This fiddling is because `Int` is the thing that's the same size as a pointer. - // The Rust code won't compile if a pointer won't fit in a `UInt64`. - writeInt(&buf, UInt64(bitPattern: Int64(Int(bitPattern: lower(value))))) - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeCryptoxideEd25519Keypair_lift(_ pointer: UnsafeMutableRawPointer) throws -> CryptoxideEd25519Keypair { - return try FfiConverterTypeCryptoxideEd25519Keypair.lift(pointer) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeCryptoxideEd25519Keypair_lower(_ value: CryptoxideEd25519Keypair) -> UnsafeMutableRawPointer { - return FfiConverterTypeCryptoxideEd25519Keypair.lower(value) -} - - - - - - -/** - * FFI-compatible trait that combines signing and keypair operations for Ed25519 - * - * This trait is exported with `with_foreign` to allow foreign languages to implement it. - * Note: We don't use supertrait relationship with Ed25519SignerFfi because UniFFI's - * with_foreign doesn't support trait inheritance properly. Instead, we duplicate the - * try_sign method. - */ -public protocol Ed25519KeyAndSigner: AnyObject, Sendable { - - func trySign(msg: Data) throws -> Data - - func verifyingKey() -> Data - -} -/** - * FFI-compatible trait that combines signing and keypair operations for Ed25519 - * - * This trait is exported with `with_foreign` to allow foreign languages to implement it. - * Note: We don't use supertrait relationship with Ed25519SignerFfi because UniFFI's - * with_foreign doesn't support trait inheritance properly. Instead, we duplicate the - * try_sign method. - */ -open class Ed25519KeyAndSignerImpl: Ed25519KeyAndSigner, @unchecked Sendable { - fileprivate let pointer: UnsafeMutableRawPointer! - - /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. -#if swift(>=5.8) - @_documentation(visibility: private) -#endif - public struct NoPointer { - public init() {} - } - - // TODO: We'd like this to be `private` but for Swifty reasons, - // we can't implement `FfiConverter` without making this `required` and we can't - // make it `required` without making it `public`. -#if swift(>=5.8) - @_documentation(visibility: private) -#endif - required public init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { - self.pointer = pointer - } - - // This constructor can be used to instantiate a fake object. - // - Parameter noPointer: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject]. - // - // - Warning: - // Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing [Pointer] the FFI lower functions will crash. -#if swift(>=5.8) - @_documentation(visibility: private) -#endif - public init(noPointer: NoPointer) { - self.pointer = nil - } - -#if swift(>=5.8) - @_documentation(visibility: private) -#endif - public func uniffiClonePointer() -> UnsafeMutableRawPointer { - return try! rustCall { uniffi_algokit_crypto_ffi_fn_clone_ed25519keyandsigner(self.pointer, $0) } - } - // No primary constructor declared for this class. - - deinit { - guard let pointer = pointer else { - return - } - - try! rustCall { uniffi_algokit_crypto_ffi_fn_free_ed25519keyandsigner(pointer, $0) } - } - - - - -open func trySign(msg: Data)throws -> Data { - return try FfiConverterData.lift(try rustCallWithError(FfiConverterTypeAlgoKitCryptoError_lift) { - uniffi_algokit_crypto_ffi_fn_method_ed25519keyandsigner_try_sign(self.uniffiClonePointer(), - FfiConverterData.lower(msg),$0 - ) -}) -} - -open func verifyingKey() -> Data { - return try! FfiConverterData.lift(try! rustCall() { - uniffi_algokit_crypto_ffi_fn_method_ed25519keyandsigner_verifying_key(self.uniffiClonePointer(),$0 - ) -}) -} - - -} - - -// Put the implementation in a struct so we don't pollute the top-level namespace -fileprivate struct UniffiCallbackInterfaceEd25519KeyAndSigner { - - // Create the VTable using a series of closures. - // Swift automatically converts these into C callback functions. - // - // This creates 1-element array, since this seems to be the only way to construct a const - // pointer that we can pass to the Rust code. - static let vtable: [UniffiVTableCallbackInterfaceEd25519KeyAndSigner] = [UniffiVTableCallbackInterfaceEd25519KeyAndSigner( - trySign: { ( - uniffiHandle: UInt64, - msg: RustBuffer, - uniffiOutReturn: UnsafeMutablePointer, - uniffiCallStatus: UnsafeMutablePointer - ) in - let makeCall = { - () throws -> Data in - guard let uniffiObj = try? FfiConverterTypeEd25519KeyAndSigner.handleMap.get(handle: uniffiHandle) else { - throw UniffiInternalError.unexpectedStaleHandle - } - return try uniffiObj.trySign( - msg: try FfiConverterData.lift(msg) - ) - } - - - let writeReturn = { uniffiOutReturn.pointee = FfiConverterData.lower($0) } - uniffiTraitInterfaceCallWithError( - callStatus: uniffiCallStatus, - makeCall: makeCall, - writeReturn: writeReturn, - lowerError: FfiConverterTypeAlgoKitCryptoError_lower - ) - }, - verifyingKey: { ( - uniffiHandle: UInt64, - uniffiOutReturn: UnsafeMutablePointer, - uniffiCallStatus: UnsafeMutablePointer - ) in - let makeCall = { - () throws -> Data in - guard let uniffiObj = try? FfiConverterTypeEd25519KeyAndSigner.handleMap.get(handle: uniffiHandle) else { - throw UniffiInternalError.unexpectedStaleHandle - } - return uniffiObj.verifyingKey( - ) - } - - - let writeReturn = { uniffiOutReturn.pointee = FfiConverterData.lower($0) } - uniffiTraitInterfaceCall( - callStatus: uniffiCallStatus, - makeCall: makeCall, - writeReturn: writeReturn - ) - }, - uniffiFree: { (uniffiHandle: UInt64) -> () in - let result = try? FfiConverterTypeEd25519KeyAndSigner.handleMap.remove(handle: uniffiHandle) - if result == nil { - print("Uniffi callback interface Ed25519KeyAndSigner: handle missing in uniffiFree") - } - } - )] -} - -private func uniffiCallbackInitEd25519KeyAndSigner() { - uniffi_algokit_crypto_ffi_fn_init_callback_vtable_ed25519keyandsigner(UniffiCallbackInterfaceEd25519KeyAndSigner.vtable) -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeEd25519KeyAndSigner: FfiConverter { - fileprivate static let handleMap = UniffiHandleMap() - - typealias FfiType = UnsafeMutableRawPointer - typealias SwiftType = Ed25519KeyAndSigner - - public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> Ed25519KeyAndSigner { - return Ed25519KeyAndSignerImpl(unsafeFromRawPointer: pointer) - } - - public static func lower(_ value: Ed25519KeyAndSigner) -> UnsafeMutableRawPointer { - guard let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: handleMap.insert(obj: value))) else { - fatalError("Cast to UnsafeMutableRawPointer failed") - } - return ptr - } - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Ed25519KeyAndSigner { - let v: UInt64 = try readInt(&buf) - // The Rust code won't compile if a pointer won't fit in a UInt64. - // We have to go via `UInt` because that's the thing that's the size of a pointer. - let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v)) - if (ptr == nil) { - throw UniffiInternalError.unexpectedNullPointer - } - return try lift(ptr!) - } - - public static func write(_ value: Ed25519KeyAndSigner, into buf: inout [UInt8]) { - // This fiddling is because `Int` is the thing that's the same size as a pointer. - // The Rust code won't compile if a pointer won't fit in a `UInt64`. - writeInt(&buf, UInt64(bitPattern: Int64(Int(bitPattern: lower(value))))) - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeEd25519KeyAndSigner_lift(_ pointer: UnsafeMutableRawPointer) throws -> Ed25519KeyAndSigner { - return try FfiConverterTypeEd25519KeyAndSigner.lift(pointer) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeEd25519KeyAndSigner_lower(_ value: Ed25519KeyAndSigner) -> UnsafeMutableRawPointer { - return FfiConverterTypeEd25519KeyAndSigner.lower(value) -} - - - - - - -/** - * FFI-compatible trait for Ed25519 signing operations - * - * This trait is exported with `with_foreign` to allow foreign languages (Python, Swift, Kotlin, etc.) - * to implement it and provide custom signing logic. - */ -public protocol Ed25519Signer: AnyObject, Sendable { - - func trySign(msg: Data) throws -> Data - -} -/** - * FFI-compatible trait for Ed25519 signing operations - * - * This trait is exported with `with_foreign` to allow foreign languages (Python, Swift, Kotlin, etc.) - * to implement it and provide custom signing logic. - */ -open class Ed25519SignerImpl: Ed25519Signer, @unchecked Sendable { - fileprivate let pointer: UnsafeMutableRawPointer! - - /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. -#if swift(>=5.8) - @_documentation(visibility: private) -#endif - public struct NoPointer { - public init() {} - } - - // TODO: We'd like this to be `private` but for Swifty reasons, - // we can't implement `FfiConverter` without making this `required` and we can't - // make it `required` without making it `public`. -#if swift(>=5.8) - @_documentation(visibility: private) -#endif - required public init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { - self.pointer = pointer - } - - // This constructor can be used to instantiate a fake object. - // - Parameter noPointer: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject]. - // - // - Warning: - // Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing [Pointer] the FFI lower functions will crash. -#if swift(>=5.8) - @_documentation(visibility: private) -#endif - public init(noPointer: NoPointer) { - self.pointer = nil - } - -#if swift(>=5.8) - @_documentation(visibility: private) -#endif - public func uniffiClonePointer() -> UnsafeMutableRawPointer { - return try! rustCall { uniffi_algokit_crypto_ffi_fn_clone_ed25519signer(self.pointer, $0) } - } - // No primary constructor declared for this class. - - deinit { - guard let pointer = pointer else { - return - } - - try! rustCall { uniffi_algokit_crypto_ffi_fn_free_ed25519signer(pointer, $0) } - } - - - - -open func trySign(msg: Data)throws -> Data { - return try FfiConverterData.lift(try rustCallWithError(FfiConverterTypeAlgoKitCryptoError_lift) { - uniffi_algokit_crypto_ffi_fn_method_ed25519signer_try_sign(self.uniffiClonePointer(), - FfiConverterData.lower(msg),$0 - ) -}) -} - - -} - - -// Put the implementation in a struct so we don't pollute the top-level namespace -fileprivate struct UniffiCallbackInterfaceEd25519Signer { - - // Create the VTable using a series of closures. - // Swift automatically converts these into C callback functions. - // - // This creates 1-element array, since this seems to be the only way to construct a const - // pointer that we can pass to the Rust code. - static let vtable: [UniffiVTableCallbackInterfaceEd25519Signer] = [UniffiVTableCallbackInterfaceEd25519Signer( - trySign: { ( - uniffiHandle: UInt64, - msg: RustBuffer, - uniffiOutReturn: UnsafeMutablePointer, - uniffiCallStatus: UnsafeMutablePointer - ) in - let makeCall = { - () throws -> Data in - guard let uniffiObj = try? FfiConverterTypeEd25519Signer.handleMap.get(handle: uniffiHandle) else { - throw UniffiInternalError.unexpectedStaleHandle - } - return try uniffiObj.trySign( - msg: try FfiConverterData.lift(msg) - ) - } - - - let writeReturn = { uniffiOutReturn.pointee = FfiConverterData.lower($0) } - uniffiTraitInterfaceCallWithError( - callStatus: uniffiCallStatus, - makeCall: makeCall, - writeReturn: writeReturn, - lowerError: FfiConverterTypeAlgoKitCryptoError_lower - ) - }, - uniffiFree: { (uniffiHandle: UInt64) -> () in - let result = try? FfiConverterTypeEd25519Signer.handleMap.remove(handle: uniffiHandle) - if result == nil { - print("Uniffi callback interface Ed25519Signer: handle missing in uniffiFree") - } - } - )] -} - -private func uniffiCallbackInitEd25519Signer() { - uniffi_algokit_crypto_ffi_fn_init_callback_vtable_ed25519signer(UniffiCallbackInterfaceEd25519Signer.vtable) -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeEd25519Signer: FfiConverter { - fileprivate static let handleMap = UniffiHandleMap() - - typealias FfiType = UnsafeMutableRawPointer - typealias SwiftType = Ed25519Signer - - public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> Ed25519Signer { - return Ed25519SignerImpl(unsafeFromRawPointer: pointer) - } - - public static func lower(_ value: Ed25519Signer) -> UnsafeMutableRawPointer { - guard let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: handleMap.insert(obj: value))) else { - fatalError("Cast to UnsafeMutableRawPointer failed") - } - return ptr - } - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Ed25519Signer { - let v: UInt64 = try readInt(&buf) - // The Rust code won't compile if a pointer won't fit in a UInt64. - // We have to go via `UInt` because that's the thing that's the size of a pointer. - let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v)) - if (ptr == nil) { - throw UniffiInternalError.unexpectedNullPointer - } - return try lift(ptr!) - } - - public static func write(_ value: Ed25519Signer, into buf: inout [UInt8]) { - // This fiddling is because `Int` is the thing that's the same size as a pointer. - // The Rust code won't compile if a pointer won't fit in a `UInt64`. - writeInt(&buf, UInt64(bitPattern: Int64(Int(bitPattern: lower(value))))) - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeEd25519Signer_lift(_ pointer: UnsafeMutableRawPointer) throws -> Ed25519Signer { - return try FfiConverterTypeEd25519Signer.lift(pointer) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeEd25519Signer_lower(_ value: Ed25519Signer) -> UnsafeMutableRawPointer { - return FfiConverterTypeEd25519Signer.lower(value) -} - - - - /** * FFI-compatible error type for crypto operations */ @@ -1045,7 +464,7 @@ public enum AlgoKitCryptoError: Swift.Error { - case Error(message: String + case Error(errMsg: String ) } @@ -1064,7 +483,7 @@ public struct FfiConverterTypeAlgoKitCryptoError: FfiConverterRustBuffer { case 1: return .Error( - message: try FfiConverterString.read(from: &buf) + errMsg: try FfiConverterString.read(from: &buf) ) default: throw UniffiInternalError.unexpectedEnumCase @@ -1078,9 +497,9 @@ public struct FfiConverterTypeAlgoKitCryptoError: FfiConverterRustBuffer { - case let .Error(message): + case let .Error(errMsg): writeInt(&buf, Int32(1)) - FfiConverterString.write(message, into: &buf) + FfiConverterString.write(errMsg, into: &buf) } } @@ -1115,29 +534,20 @@ extension AlgoKitCryptoError: Foundation.LocalizedError { - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -fileprivate struct FfiConverterOptionData: FfiConverterRustBuffer { - typealias SwiftType = Data? - - public static func write(_ value: SwiftType, into buf: inout [UInt8]) { - guard let value = value else { - writeInt(&buf, Int8(0)) - return - } - writeInt(&buf, Int8(1)) - FfiConverterData.write(value, into: &buf) - } - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { - switch try readInt(&buf) as Int8 { - case 0: return nil - case 1: return try FfiConverterData.read(from: &buf) - default: throw UniffiInternalError.unexpectedOptionalTag - } - } +public func ed25519PublicKeyFromSeed(seed: Data)throws -> Data { + return try FfiConverterData.lift(try rustCallWithError(FfiConverterTypeAlgoKitCryptoError_lift) { + uniffi_algokit_crypto_ffi_fn_func_ed25519_public_key_from_seed( + FfiConverterData.lower(seed),$0 + ) +}) +} +public func ed25519RawSign(secretKey: Data, data: Data)throws -> Data { + return try FfiConverterData.lift(try rustCallWithError(FfiConverterTypeAlgoKitCryptoError_lift) { + uniffi_algokit_crypto_ffi_fn_func_ed25519_raw_sign( + FfiConverterData.lower(secretKey), + FfiConverterData.lower(data),$0 + ) +}) } private enum InitializationResult { @@ -1155,27 +565,13 @@ private let initializationResult: InitializationResult = { if bindings_contract_version != scaffolding_contract_version { return InitializationResult.contractVersionMismatch } - if (uniffi_algokit_crypto_ffi_checksum_method_cryptoxideed25519keypair_try_sign() != 46579) { - return InitializationResult.apiChecksumMismatch - } - if (uniffi_algokit_crypto_ffi_checksum_method_cryptoxideed25519keypair_verifying_key() != 2056) { - return InitializationResult.apiChecksumMismatch - } - if (uniffi_algokit_crypto_ffi_checksum_method_ed25519keyandsigner_try_sign() != 14488) { - return InitializationResult.apiChecksumMismatch - } - if (uniffi_algokit_crypto_ffi_checksum_method_ed25519keyandsigner_verifying_key() != 42410) { - return InitializationResult.apiChecksumMismatch - } - if (uniffi_algokit_crypto_ffi_checksum_method_ed25519signer_try_sign() != 33107) { + if (uniffi_algokit_crypto_ffi_checksum_func_ed25519_public_key_from_seed() != 13794) { return InitializationResult.apiChecksumMismatch } - if (uniffi_algokit_crypto_ffi_checksum_constructor_cryptoxideed25519keypair_try_generate() != 49154) { + if (uniffi_algokit_crypto_ffi_checksum_func_ed25519_raw_sign() != 65210) { return InitializationResult.apiChecksumMismatch } - uniffiCallbackInitEd25519KeyAndSigner() - uniffiCallbackInitEd25519Signer() return InitializationResult.ok }() diff --git a/tools/build_pkgs/src/main.rs b/tools/build_pkgs/src/main.rs index 97473143..9c4d5eae 100644 --- a/tools/build_pkgs/src/main.rs +++ b/tools/build_pkgs/src/main.rs @@ -51,6 +51,8 @@ enum Package { Transact, #[value(alias = "algokit_crypto")] Crypto, + #[value(alias = "algokit_algo25")] + Algo25, } impl Display for Package { @@ -58,6 +60,7 @@ impl Display for Package { match self { Package::Transact => f.write_str("algokit_transact"), Package::Crypto => f.write_str("algokit_crypto"), + Package::Algo25 => f.write_str("algokit_algo25"), } } } @@ -67,6 +70,7 @@ impl Package { match self { Self::Transact => "algokit_transact_ffi", Self::Crypto => "algokit_crypto_ffi", + Self::Algo25 => "algokit_algo25_ffi", } .to_string() }