Skip to content

Commit

Permalink
Added AddressBech32 guard in Blueprint
Browse files Browse the repository at this point in the history
  • Loading branch information
hrajchert committed Feb 13, 2024
1 parent f543af1 commit c09a83e
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 86 deletions.
95 changes: 11 additions & 84 deletions packages/blueprint/src/typed.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { Address } from "@marlowe.io/language-core-v1";
import { MarloweJSON } from "@marlowe.io/adapter/codec";
import * as t from "io-ts/lib/index.js";
import { DateFromEpochMS, StringCodec } from "./codecs.js";
import { Metadata, MetadatumGuard } from "@marlowe.io/runtime-core";
import {
AddressBech32,
AddressBech32Guard,
Metadata,
MetadatumGuard,
} from "@marlowe.io/runtime-core";
import {
BigIntOrNumber,
BigIntOrNumberGuard,
Expand Down Expand Up @@ -45,7 +48,7 @@ type TypeOfParam<Param extends BlueprintParam<any>> = Param extends StringParam<
: Param extends ValueParam<infer Name>
? BigIntOrNumber
: Param extends AddressParam<infer Name>
? Address
? AddressBech32
: Param extends DateParam<infer Name>
? Date
: never;
Expand All @@ -66,7 +69,7 @@ function blueprintParamCodec<Param extends BlueprintParam<any>>(
} else if (param.type === "value") {
return BigIntOrNumberGuard;
} else if (param.type === "address") {
return t.string;
return StringCodec.pipe(AddressBech32Guard);
} else if (param.type === "date") {
return DateFromEpochMS;
} else {
Expand Down Expand Up @@ -96,6 +99,9 @@ class Blueprint<T extends object> {
description?: string;
constructor(args: MkBlueprint<readonly BlueprintParam<any>[]>) {
const blueprintParams = args.params;
this.name = args.name;
this.description = args.description;

const paramListCodec = blueprintParamsCodec(blueprintParams);
const paramObjectCodec = blueprintParamsObjectGuard(blueprintParams);

Expand Down Expand Up @@ -161,7 +167,6 @@ class Blueprint<T extends object> {
(values) => {
// FIXME: Try to type
let valuesAsList: any[] = [];
// iterate over blueprintParams and for each entry.name, look for the values[name] a

blueprintParams.forEach((param, ix) => {
const value = (values as any)[param.name];
Expand All @@ -179,36 +184,6 @@ class Blueprint<T extends object> {
}
)
);

/**
this.blueprintCodec = t.type({
v: t.literal(1),
params: t.unknown,
}).pipe(
// new t.Type<T, Metadata, {v: 1, params: unknown}>(
new t.Type<T, any, any>(
"Blueprint T",
paramObjectCodec.is,
(val, ctx) => {
return null as any;
// throw new Error("Not implemented");
// if (paramObjectCodec.is(val)) {
// return t.success(val);
// } else {
// return t.failure(val, ctx);
// }
},
(values) => {
return {
v: 1,
params: []
} as Metadata;
// throw new Error("Not implemented");
// paramObjectCodec.encode
}
)
)
*/
}

is(value: unknown): value is T {
Expand All @@ -220,7 +195,6 @@ class Blueprint<T extends object> {
if (decoded._tag === "Right") {
return decoded.right;
} else {
console.log(MarloweJSON.stringify(decoded.left, null, 2));
throw new Error("Invalid value");
}
}
Expand Down Expand Up @@ -250,50 +224,3 @@ export function mkBlueprint<T extends readonly BlueprintParam<any>[]>(
): Blueprint<Expand<BlueprintType<T>>> {
return new Blueprint(args);
}

// Example DelayPayment

// const delayPaymentBlueprint = mkBlueprint([
// {
// name: "payFrom",
// description: "Who is making the delayed payment",
// type: "address",
// },
// {
// name: "payTo",
// description: "Who is receiving the payment",
// type: "address",
// },
// {
// name: "amount",
// description: "The amount of lovelaces to be paid",
// type: "value",
// },
// {
// name: "depositDeadline",
// description:
// "The deadline for the payment to be made. If the payment is not made by this date, the contract can be closed",
// type: "date",
// },
// {
// name: "releaseDeadline",
// description:
// "A date after the payment can be released to the receiver. NOTE: An empty transaction must be done to close the contract",
// type: "date",
// },
// ] as const);

// console.log(
// "example 3",
// MarloweJSON.stringify(decoded1, null, 2)
// );

// console.log(decoded1.amount);

// console.log(delayPaymentBlueprint.encode({
// payFrom: {address: "address1"},
// payTo: {address: "address2"},
// amount: 42n,
// depositDeadline: new Date("2024-02-02"),
// releaseDeadline: new Date("2024-02-02"),
// }))
File renamed without changes.
166 changes: 166 additions & 0 deletions packages/blueprint/test/blueprint-delayed-payment.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import { AddressBech32, addressBech32 } from "@marlowe.io/runtime-core";
import { BlueprintOf, mkBlueprint } from "../src/typed.js";
describe("Delayed payment Blueprint", () => {
const delayPaymentBlueprint = mkBlueprint({
name: "Delayed payment",
description:
"In a delay payment, a `payer` transfer an `amount` of ADA to the `payee` which can be redeemed after a `releaseDeadline`. While the payment is held by the contract, it can be staked to the payer, to generate pasive income while the payee has the guarantees that the money will be released.",
params: [
{
name: "payer",
description: "Who is making the payment",
type: "address",
},
{
name: "payee",
description: "Who is receiving the payment",
type: "address",
},
{
name: "amount",
description: "The amount of lovelaces to be paid",
type: "value",
},
{
name: "depositDeadline",
description:
"The deadline for the payment to be made. If the payment is not made by this date, the contract can be closed",
type: "date",
},
{
name: "releaseDeadline",
description:
"A date after the payment can be released to the receiver. NOTE: An empty transaction must be done to close the contract",
type: "date",
},
] as const,
});

/** The inferred type from the Blueprint should be
{
payer: AddressBech32,
payee: AddressBech32,
amount: BigInt,
depositDeadline: Date,
releaseDeadline: Date
}
*/
type DelayedPaymentParams = BlueprintOf<typeof delayPaymentBlueprint>;

const aDepositDate = new Date("2024-01-01T00:00:00.000Z");
const aDepositDateMS = BigInt(aDepositDate.getTime());

const aReleaseDate = new Date("2024-01-02T00:00:00.000Z");
const aReleaseDateMS = BigInt(aReleaseDate.getTime());

it("should guard for correct values", () => {
const a: DelayedPaymentParams = {
payer: addressBech32(
"addr_test1qpcucug827nlrmsv7n66hwdfpemwqtv8nxnjc4azacuu807w6l6hgelwsph7clqmauq7h3y9qhhgs0rwu3mu8uf7m4kqckxkry"
),
payee: addressBech32(
"addr_test1qr5xmx2uk4mdllmcmvvzrv0dgjqdcq6vwrlapkhs53y26frd865gqhw8qxh53gnhnc3gsrgz2nc7gackeneut4ctjgvs207cpv"
),
amount: 42n,
depositDeadline: aDepositDate,
releaseDeadline: aReleaseDate,
};

expect(delayPaymentBlueprint.is(a)).toBe(true);
});

it("should guard for incorrect addresses", () => {
const a: DelayedPaymentParams = {
payer: "invalid_address" as AddressBech32,
payee: "invalid_address" as AddressBech32,
amount: 42n,
depositDeadline: aDepositDate,
releaseDeadline: aReleaseDate,
};

expect(delayPaymentBlueprint.is(a)).toBe(false);
});
it("should encode a valid value", () => {
const a: DelayedPaymentParams = {
payer: addressBech32(
"addr_test1qpcucug827nlrmsv7n66hwdfpemwqtv8nxnjc4azacuu807w6l6hgelwsph7clqmauq7h3y9qhhgs0rwu3mu8uf7m4kqckxkry"
),
payee: addressBech32(
"addr_test1qr5xmx2uk4mdllmcmvvzrv0dgjqdcq6vwrlapkhs53y26frd865gqhw8qxh53gnhnc3gsrgz2nc7gackeneut4ctjgvs207cpv"
),
amount: 42n,
depositDeadline: aDepositDate,
releaseDeadline: aReleaseDate,
};
expect(delayPaymentBlueprint.encode(a)).toEqual({
"9041": {
v: 1n,
params: [
[
"addr_test1qpcucug827nlrmsv7n66hwdfpemwqtv8nxnjc4azacuu807w6l6hge",
"lwsph7clqmauq7h3y9qhhgs0rwu3mu8uf7m4kqckxkry",
],
[
"addr_test1qr5xmx2uk4mdllmcmvvzrv0dgjqdcq6vwrlapkhs53y26frd865gqh",
"w8qxh53gnhnc3gsrgz2nc7gackeneut4ctjgvs207cpv",
],
42n,
aDepositDateMS,
aReleaseDateMS,
],
},
});
});
it("should decode a valid value", () => {
const metadata = {
"9041": {
v: 1n,
params: [
[
"addr_test1qpcucug827nlrmsv7n66hwdfpemwqtv8nxnjc4azacuu807w6l6hge",
"lwsph7clqmauq7h3y9qhhgs0rwu3mu8uf7m4kqckxkry",
],
[
"addr_test1qr5xmx2uk4mdllmcmvvzrv0dgjqdcq6vwrlapkhs53y26frd865gqh",
"w8qxh53gnhnc3gsrgz2nc7gackeneut4ctjgvs207cpv",
],
42n,
aDepositDateMS,
aReleaseDateMS,
],
},
};

expect(delayPaymentBlueprint.decode(metadata)).toEqual({
payer: addressBech32(
"addr_test1qpcucug827nlrmsv7n66hwdfpemwqtv8nxnjc4azacuu807w6l6hgelwsph7clqmauq7h3y9qhhgs0rwu3mu8uf7m4kqckxkry"
),
payee: addressBech32(
"addr_test1qr5xmx2uk4mdllmcmvvzrv0dgjqdcq6vwrlapkhs53y26frd865gqhw8qxh53gnhnc3gsrgz2nc7gackeneut4ctjgvs207cpv"
),
amount: 42n,
depositDeadline: aDepositDate,
releaseDeadline: aReleaseDate,
});
});

it("should fail to decode an invalid address", () => {
const metadata = {
"9041": {
v: 1n,
params: [
["invalid_address"],
[
"addr_test1qr5xmx2uk4mdllmcmvvzrv0dgjqdcq6vwrlapkhs53y26frd865gqh",
"w8qxh53gnhnc3gsrgz2nc7gackeneut4ctjgvs207cpv",
],
42n,
aDepositDateMS,
aReleaseDateMS,
],
},
};
// FIXME: Improve the error message checking
expect(() => delayPaymentBlueprint.decode(metadata)).toThrow();
});
});
2 changes: 1 addition & 1 deletion packages/blueprint/test/codecs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe("DateFromEpochMS", () => {
});
it("should encode a date as a number", () => {
const aDate = DateFromEpochMS.encode(new Date("2024-01-01T00:00:00.000Z"));
expect(aDate).toEqual(1704067200000);
expect(aDate).toEqual(1704067200000n);
});
});

Expand Down
12 changes: 11 additions & 1 deletion packages/runtime/core/src/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,24 @@ import { iso, Newtype } from "newtype-ts";
import { fromNewtype } from "io-ts-types";
import { TxOutRef } from "./tx/outRef.js";
import { unsafeEither } from "@marlowe.io/adapter/fp-ts";
// TODO: Try to use a lighter library than lucid for checking this.
import { C } from "lucid-cardano";

export interface AddressBech32Brand {
readonly AddressBech32: unique symbol;
}

export const AddressBech32Guard = t.brand(
t.string,
(s): s is t.Branded<string, AddressBech32Brand> => true,
(s): s is t.Branded<string, AddressBech32Brand> => {
try {
// TODO: Try to use a lighter library than lucid for checking this.
C.Address.from_bech32(s);
return true;
} catch (e) {
return false;
}
},
"AddressBech32"
);

Expand Down

0 comments on commit c09a83e

Please sign in to comment.