Skip to content

Commit

Permalink
✨ Pack verifier for depositing
Browse files Browse the repository at this point in the history
  • Loading branch information
doitian committed Feb 9, 2024
1 parent 0bf4483 commit 2e3d78c
Show file tree
Hide file tree
Showing 12 changed files with 243 additions and 23 deletions.
11 changes: 11 additions & 0 deletions bin/use-env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ esac

JOYID_INFO_FILE="$(ls migrations/joyid/*.json | grep -v deployment | head -n 1)"
OMNILOCK_INFO_FILE="$(ls migrations/omnilock/*.json | grep -v deployment | head -n 1)"
DAO_ACTION_VERIFIER_INFO_FILE="$(ls migrations/dao-action-verifier/*.json | grep -v deployment | head -n 1)"

sed -n \
-e 's/,$//' \
Expand All @@ -45,3 +46,13 @@ sed -n \
-e 's/,$//' \
-e 's/^ *"tx_hash": /NEXT_PUBLIC_OMNILOCK_TX_HASH=/p' \
"$OMNILOCK_INFO_FILE" | tail -1

sed -n \
-e 's/,$//' \
-e 's/^ *"type_id": "/NEXT_PUBLIC_DAO_ACTION_VERIFIER_CODE_HASH="/p' \
"$DAO_ACTION_VERIFIER_INFO_FILE" | head -1

sed -n \
-e 's/,$//' \
-e 's/^ *"tx_hash": /NEXT_PUBLIC_DAO_ACTION_VERIFIER_TX_HASH=/p' \
"$DAO_ACTION_VERIFIER_INFO_FILE" | tail -1
6 changes: 5 additions & 1 deletion src/actions/deposit.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
"use server";

import { parseUnit } from "@ckb-lumos/bi";
import { depositDao } from "@/lib/cobuild/publishers";
import { getConfig } from "@/lib/config";
import { prepareLockActions } from "@/lib/cobuild/lock-actions";
import { payFee } from "@/lib/cobuild/fee-manager";
import { prepareVerifier } from "@/lib/papps/dao/verifier";

export default async function deposit(_prevState, formData, config) {
config = config ?? getConfig();

const from = formData.get("from");
const shouldPackVerifier = formData.get("packVerifier") !== undefined;

try {
let buildingPacket = await depositDao(config)(formData);
if (shouldPackVerifier) {
buildingPacket = await prepareVerifier(buildingPacket, from, config);
}
buildingPacket = await payFee(
buildingPacket,
[{ address: from, feeRate: 1200 }],
Expand Down
15 changes: 11 additions & 4 deletions src/app/u/[wallet]/[connection]/deposit/form.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
"use client";

import { Alert, Checkbox, Label, Popover, TextInput } from "flowbite-react";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import { useFormState } from "react-dom";
import { useRouter } from "next/navigation";
import { Label, TextInput, Alert } from "flowbite-react";

import deposit from "@/actions/deposit";
import { fetchAssetsWithCache } from "@/actions/fetch-assets";
import Capacity from "@/components/capacity";
import PackingVerifierHelpText from "@/components/packing-verifier-help-text";
import SubmitButton from "@/components/submit-button";
import { fetchAssetsWithCache } from "@/actions/fetch-assets";
import deposit from "@/actions/deposit";
import Loading from "../loading";
import SignForm from "../sign-form";
import SubmitBuildingPacket from "../submit-building-packet";
Expand Down Expand Up @@ -52,6 +53,12 @@ export function TransactionForm({ formAction, formState, address }) {
}
/>
</div>
<div>
<Checkbox className="mr-2" id="packVerifier" name="packVerifier" />
<Label htmlFor="packVerifier">
Pack Verifier (<PackingVerifierHelpText />)
</Label>
</div>
<SubmitButton>Deposit</SubmitButton>
</form>
);
Expand Down
5 changes: 4 additions & 1 deletion src/app/u/[wallet]/[connection]/sign-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ export default function SignForm({
ckbChainConfig,
);
onSubmit(
finalizeWitnesses(applyLockAction(buildingPacket, lockAction, seal)),
finalizeWitnesses(
applyLockAction(buildingPacket, lockAction, seal),
ckbChainConfig,
),
);
} catch (err) {
console.error(err.stack);
Expand Down
11 changes: 11 additions & 0 deletions src/components/packing-verifier-help-text.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default function PackingVerifierHelpText() {
return (
<a
target="_blank"
className="hover:cursor-pointer"
href="https://github.com/cryptape/ckb-dao-cobuild-poc/blob/develop/docs/packing-verifier.md"
>
What's This?
</a>
);
}
15 changes: 13 additions & 2 deletions src/lib/cobuild/fee-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export async function payFee(buildingPacket, feePayments, config) {
),
buildingPacket,
),
config.ckbChainConfig,
);

const buildingPacketHavePaidFee = await payFeeWithBuildingPacket(
Expand Down Expand Up @@ -55,7 +56,12 @@ function storeWitnessForFeeEstimation(
return generalLockActions.storeWitnessForFeeEstimation(
buildingPacket,
scriptHash,
inputIndices,
{
type: "WitnessArgsStore",
value: {
inputIndices,
},
},
// Variable length, but 500 is usually enough.
() => bytes.hexify(new Uint8Array(500)),
);
Expand All @@ -65,7 +71,12 @@ function storeWitnessForFeeEstimation(
return generalLockActions.storeWitnessForFeeEstimation(
buildingPacket,
scriptHash,
inputIndices,
{
type: "WitnessArgsStore",
value: {
inputIndices,
},
},
// 85 = 65 signature in OmnilockWitnessLock
() => `0x${"0".repeat(85 * 2)}`,
);
Expand Down
6 changes: 3 additions & 3 deletions src/lib/cobuild/general-lock-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ export function prepareLockActionWithWitnessStore(
export function storeWitnessForFeeEstimation(
buildingPacket,
scriptHash,
inputIndices,
witnessStore,
createSealPlaceHolder,
) {
buildingPacket = prepareLockAction(
buildingPacket = prepareLockActionWithWitnessStore(
buildingPacket,
scriptHash,
inputIndices,
witnessStore,
createSealPlaceHolder,
);
const lockAction = buildingPacket.value.lockActions.find(
Expand Down
49 changes: 47 additions & 2 deletions src/lib/cobuild/lock-actions.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { utils as lumosBaseUtils } from "@ckb-lumos/base";
import { bytes } from "@ckb-lumos/codec";

import * as generalLockActions from "./general-lock-actions";
import { parseWitnessType } from "./types";
import { groupByLock } from "./script-group";
import { parseWitnessType, WitnessLayout } from "./types";

const { computeScriptHash } = lumosBaseUtils;

// Generate lockActions.
//
// Do not set any witness in payload to use the Cobuild layout.
export function prepareLockActions(buildingPacket, ckbChainConfig) {
// Workaround to pack Cobuild message as the extra witness.
//
// Before the next action, there's no actual witness set yet. So `finalizeWitnesses` will assume there's no SighashAll witness in the tx and will pack the message as the extra witness. This assumption is currect because this PoC uses WitnessArgs layout for lock actions and DAO type script.
buildingPacket = finalizeWitnesses(buildingPacket, ckbChainConfig);

const groups = groupByLock(buildingPacket.value.resolvedInputs.outputs);
return Object.entries(groups).reduce(
(acc, [scriptHash, inputs]) =>
Expand All @@ -23,20 +29,39 @@ export function prepareLockActions(buildingPacket, ckbChainConfig) {
);
}

export function finalizeWitnesses(buildingPacket) {
function hasVerifierCell(buildingPacket, ckbChainConfig) {
const template = ckbChainConfig.SCRIPTS.DAO_ACTION_VERIFIER;
for (const output of buildingPacket.value.payload.outputs) {
if (
output.type !== undefined &&
output.type !== null &&
output.type.codeHash === template.CODE_HASH
) {
return true;
}
}

return false;
}

export function finalizeWitnesses(buildingPacket, ckbChainConfig) {
// fill holes
const witnesses = Array.from(buildingPacket.value.payload.witnesses).map(
(w) => w ?? "0x",
);

let hasSighashAll = false;
// If there's no SighashAll before SighashAllOnly, replace the first SighashAllOnly with SighashAll
for (const i of witnesses.keys()) {
if (witnesses[i] === "0x") {
continue;
}
const witnessType = parseWitnessType(witnesses[i]);
if (witnessType === "SighashAll") {
hasSighashAll = true;
break;
} else if (witnessType === "SighashAllOnly") {
hasSighashAll = true;
const witness = WitnessLayout.unpack(witnesses[i]);
witnesses[i] = bytes.hexify(
WitnessLayout.pack({
Expand All @@ -50,6 +75,26 @@ export function finalizeWitnesses(buildingPacket) {
}
}

// if there's a verifier cell, pack the Cobuild message into witness
if (!hasSighashAll && hasVerifierCell(buildingPacket, ckbChainConfig)) {
for (
let i = witnesses.length;
i < buildingPacket.value.payload.inputs.length;
++i
) {
witnesses[i] = "0x";
}
witnesses[buildingPacket.value.payload.inputs.length] = bytes.hexify(
WitnessLayout.pack({
type: "SighashAll",
value: {
seal: "0x",
message: buildingPacket.value.message,
},
}),
);
}

return {
type: buildingPacket.type,
value: {
Expand Down
6 changes: 3 additions & 3 deletions src/lib/cobuild/react/building-packet-review.js
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ export function TxSection({

return (
<dl className="divide-y divide-gray-100">
{process.env.DEBUG !== undefined ? (
{process.env.NODE_ENV === "development" ? (
<div className="py-3 sm:grid sm:grid-cols-3">
<dt className="leading-6 text-gray-900">Hash</dt>
<dd className="text-gray-700 sm:col-span-2 sm:mt-0 break-all">
Expand Down Expand Up @@ -567,7 +567,7 @@ function collectAssets(
} else if (isNoneDaoTypedCell(cellOutput, cellData, ckbChainConfig)) {
assets.destroyedTypedCells.push({
cellOutput,
outPoint: buildingPacket.value.packet.inputs[i].previousOutput,
outPoint: buildingPacket.value.payload.inputs[i].previousOutput,
data: cellData,
});
}
Expand All @@ -582,7 +582,7 @@ function collectAssets(
assets.daoDeposited = assets.daoDeposited.add(cellCapacity);
} else if (isNoneDaoTypedCell(cellOutput, cellData, ckbChainConfig)) {
assets.createdTypedCells.push({
outPoint: buildingPacket.value.packet.inputs[i].previousOutput,
outPoint: buildingPacket.value.payload.outputs[i].previousOutput,
data: cellData,
});
}
Expand Down
20 changes: 20 additions & 0 deletions src/lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ const CKB_CHAINS_CONFIGS = {
INDEX: "0x0",
DEP_TYPE: "code",
},

DAO_ACTION_VERIFIER: {
CODE_HASH:
"0xbdca5b74e5d0c913fed19d8482a99af1ef8a639541438b2e00189f5e18907ef9",
HASH_TYPE: "type",
TX_HASH:
"0x9157bcc278176ba9e823a50d72631be9e9b964e7a5ca11db2782c059c4c788ad",
INDEX: "0x0",
DEP_TYPE: "code",
},
},
},
};
Expand Down Expand Up @@ -66,6 +76,15 @@ function buildCkbChainConfig(ckbChain) {
TX_HASH: presence(process.env.NEXT_PUBLIC_OMNILOCK_TX_HASH),
},
);
const DAO_ACTION_VERIFIER = assign(
{ ...template.SCRIPTS.DAO_ACTION_VERIFIER },
{
CODE_HASH: presence(
process.env.NEXT_PUBLIC_DAO_ACTION_VERIFIER_CODE_HASH,
),
TX_HASH: presence(process.env.NEXT_PUBLIC_DAO_ACTION_VERIFIER_TX_HASH),
},
);

const tx0 =
presence(process.env.NEXT_PUBLIC_CKB_GENESIS_TX_0) ??
Expand All @@ -80,6 +99,7 @@ function buildCkbChainConfig(ckbChain) {
SCRIPTS: {
JOYID,
OMNILOCK_CUSTOM,
DAO_ACTION_VERIFIER,
DAO: {
...template.SCRIPTS.DAO,
TX_HASH: tx0,
Expand Down
7 changes: 0 additions & 7 deletions src/lib/papps/dao/action-creators.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,6 @@ function addressToScriptOpt(address, lumosOptions) {
return undefined;
}

function buildSingleOperation(operation) {
return {
type: "SingleOperation",
value: operation,
};
}

export function depositWithFormData(config, formData) {
const lumosOptions = { config: config.ckbChainConfig };

Expand Down
Loading

0 comments on commit 2e3d78c

Please sign in to comment.