Skip to content

Commit

Permalink
Lucidless signing
Browse files Browse the repository at this point in the history
  • Loading branch information
Quantumplation committed Aug 13, 2024
1 parent fa305b7 commit b62931b
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 57 deletions.
73 changes: 27 additions & 46 deletions src/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ import { appendTx, session, updateUI } from "./stats";
import * as ed25519 from "@noble/ed25519";
import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
import { blake2b } from "@noble/hashes/blake2b";
import { sha512 } from "@noble/hashes/sha512";

ed25519.etc.sha512Sync = (...m) => sha512(ed25519.etc.concatBytes(...m));

let gameServerUrl = process.env.SERVER_URL;
if (!gameServerUrl) {
Expand All @@ -38,9 +41,9 @@ if (!gameServerUrl) {
);
}
let lucid = await Lucid.new(undefined, "Preprod");
let { sessionKey: privateKey } = keys;
let { sessionKey, privateKey, sessionPk } = keys;
const address = await lucid
.selectWalletFromPrivateKey(privateKey)
.selectWalletFromPrivateKey(sessionKey)
.wallet.address();
const pkh = lucid.utils.getAddressDetails(address).paymentCredential?.hash!;
console.log(`Using session key with address: ${address}`);
Expand Down Expand Up @@ -134,9 +137,6 @@ export async function fetchNewGame(region: string) {
// so we return it from the newGameResponse and set it manually here
latestUTxO.datum = newGameResponse.player_utxo_datum_hex;

lucid = await Lucid.new(hydra, "Preprod");
lucid.selectWalletFromPrivateKey(privateKey);

// This is temporary, the initial game state is stored in a UTxO created by the control plane.
// We need to add the ability to parse game state from the datum here.
gameData = initialGameData(pkh, player_pkh!);
Expand Down Expand Up @@ -250,15 +250,15 @@ export async function hydraSend(
redeemerQueue.push(cmd);

if (frameNumber % 1 == 0) {
const [newUtxo, tx] = await buildTx(
const [newUtxo, tx] = buildTx(
latestUTxO!,
encodeRedeemer(redeemerQueue),
buildDatum(gameData),
collateralUTxO!,
);

sessionStats.transactions++;
sessionStats.bytes += tx.txSigned.to_bytes().length;
sessionStats.bytes += tx.length / 2;
sessionStats.total_kills = gameData.player.totalStats.killCount;
sessionStats.total_items = gameData.player.totalStats.itemCount;
sessionStats.total_secrets = gameData.player.totalStats.secretCount;
Expand All @@ -268,7 +268,7 @@ export async function hydraSend(
);
updateUI(session, sessionStats);

await hydra.submitTx(tx.toString());
await hydra.submitTx(tx);
latestUTxO = newUtxo;
redeemerQueue = [];
// console.log(`submitted ${tx.toHash()}, took ${performance.now() - hydraSendStart}ms`);
Expand Down Expand Up @@ -363,12 +363,12 @@ const decodeRedeemer = (redeemer: string): Cmd[] => {
}) as Cmd,
);
};
const buildTx = async (
const buildTx = (
inputUtxo: UTxO,
redeemer: string,
datum: string,
collateralUtxo: UTxO,
): Promise<[UTxO, TxSigned]> => {
): [UTxO, string] => {
// Hand-roll transaction creation for more performance
// NOTE: Redeemer is always using max ex units
const redeemerBlock = `81840000${redeemer}821a00d59f801b00000002540be400`;
Expand All @@ -387,45 +387,26 @@ const buildTx = async (
`0d81825820${collateralUtxo.txHash}0${collateralUtxo.outputIndex}` + // Collatteral Input
`0e81581c${pkh}` + // Required Signers
`1281825820${scriptRef!.split("#")[0]}0${scriptRef!.split("#")[1]}`; // Reference inputs
const witnessSetByHand = `a105${redeemerBlock}`; // a single redeemer in witness set

const txId = bytesToHex(
blake2b(hexToBytes(txBodyByHand), { dkLen: 256 / 8 }),
);
const signature = bytesToHex(ed25519.sign(txId, privateKey));

const witnessSetByHand = `a20081825820${sessionPk}5840${signature}05${redeemerBlock}`; // a single redeemer in witness set
const txByHand = `84${txBodyByHand}${witnessSetByHand}f5f6`;

// Still use lucid for signing with configured key
const tx = lucid.fromTx(txByHand);
const signedTx = await tx.sign().complete();

const body = signedTx.txSigned.body();
const outputs = body.outputs();
body.free();
let newUtxo: UTxO | null = null;
for (let i = 0; i < outputs.len(); i++) {
const output = outputs.get(i);
const address = output.address();
if (address.to_bech32("addr_test") === scriptAddress) {
const amount = output.amount();
const datum = output.datum()!;
const data = datum.as_data()!;

newUtxo = {
txHash: signedTx.toHash(),
outputIndex: i,
address: address.to_bech32("addr_test"),
assets: valueToAssets(amount),
datumHash: null,
datum: toHex(data.to_bytes()).substring(8),
scriptRef: null,
};
amount.free();
output.free();
data.free();
address.free();
break;
}
outputs.free();
body.free();
}
const newUtxo: UTxO = {
txHash: txId,
outputIndex: 0,
address: scriptAddress,
assets: { lovelace: 0n },
datumHash: null,
datum: datum,
scriptRef: null,
};

return [newUtxo!, signedTx];
return [newUtxo, txByHand];
};

const subtractPlayerStats = (left?: PlayerStats, right?: PlayerStats) => {
Expand Down
26 changes: 17 additions & 9 deletions src/headless.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ <h1>
import * as ed from "https://esm.sh/@noble/[email protected]";
import { blake2b } from "https://esm.sh/@noble/[email protected]/blake2b";
import { sha512 } from "https://esm.sh/@noble/[email protected]/sha512";
import { bech32 } from "https://esm.sh/[email protected]";

import {
bytesToHex,
hexToBytes,
Expand Down Expand Up @@ -95,6 +97,10 @@ <h1>
async function requestGame() {
let lucid = await Lucid.new(undefined, "Preprod");
const ephemeralKey = lucid.utils.generatePrivateKey();
const privateKey = new Uint8Array(
bech32.fromWords(bech32.decode(ephemeralKey).words),
);
const publicKey = bytesToHex(ed.getPublicKey(privateKey));
const address = await lucid
.selectWalletFromPrivateKey(ephemeralKey)
.wallet.address();
Expand All @@ -107,7 +113,9 @@ <h1>
await rawResponse.json();
return {
lucid,
ephemeralKey,
ephemeralKey: ephemeralKey,
privateKey,
publicKey: publicKey,
pkh,
ip,
player_utxo,
Expand Down Expand Up @@ -259,7 +267,8 @@ <h1>

async function buildTx(gameId) {
const {
ephemeralKey,
privateKey,
publicKey,
pkh,
script_ref,
player_utxo,
Expand All @@ -285,14 +294,13 @@ <h1>
`0d81825820${collateralUtxo.split("#")[0]}0${collateralUtxo.split("#")[1]}` + // Collatteral Input
`0e81581c${pkh}` + // Required Signers
`1281825820${script_ref.split("#")[0]}0${script_ref.split("#")[1]}`; // Reference inputs
const witnessSetByHand = `a105${redeemerBlock}`; // a single redeemer in witness set
const txIdRaw = blake2b(hexToBytes(txBodyByHand), { dkLen: 256 / 8 });
const txId = bytesToHex(txIdRaw);
const signature = bytesToHex(ed.sign(txIdRaw, privateKey));

const witnessSetByHand = `a20081825820${publicKey}5840${signature}05${redeemerBlock}`; // a single redeemer in witness set
const txByHand = `84${txBodyByHand}${witnessSetByHand}f5f6`;
const txId = bytesToHex(
blake2b(hexToBytes(txBodyByHand), { dkLen: 256 / 8 }),
);
const tx = games[gameId].lucid.fromTx(txByHand);
const signedTx = await tx.sign().complete();
return { tx: signedTx.toString(), new_utxo: `${txId}#0` };
return { tx: txByHand, new_utxo: `${txId}#0` };
}
</script>
</html>
5 changes: 3 additions & 2 deletions src/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as ed25519 from "@noble/ed25519";
import * as bech32 from "bech32-buffer";
import { encode } from "cbor-x";
import { Lucid } from "lucid-cardano";
import { blake2b } from "@noble/hashes/blake2b"
import { blake2b } from "@noble/hashes/blake2b";
import { sha512 } from "@noble/hashes/sha512";

ed25519.etc.sha512Async = (...m) =>
Expand Down Expand Up @@ -34,14 +34,15 @@ const decodedSessionKey = Array.from(bech32.decode(sessionKey).data)
.map(toHex)
.join("");
const sessionPk = await ed25519.getPublicKeyAsync(decodedSessionKey);
const sessionPkh = blake2b(sessionPk, { dkLen: 224/8 });
const sessionPkh = blake2b(sessionPk, { dkLen: 224 / 8 });

function toHex(i: number) {
return ("0" + i.toString(16)).slice(-2);
}

export const keys = {
sessionKey,
privateKey: decodedSessionKey,
sessionPk: ed25519.etc.bytesToHex(sessionPk),
sessionPkh: ed25519.etc.bytesToHex(sessionPkh),
cabinetKey,
Expand Down

0 comments on commit b62931b

Please sign in to comment.