diff --git a/package.json b/package.json index b7a05ed0..bb99e492 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@helios-lang/compiler", - "version": "0.17.0-83", + "version": "0.17.0-84", "description": "Helios is a Domain Specific Language that compiles to Plutus-Core (i.e. Cardano on-chain validator scripts). Helios is a non-Haskell alternative to Plutus. With this library you can compile Helios scripts and build Cardano transactions, all you need to build 100% client-side dApps for Cardano.", "main": "src/index.js", "types": "types/index.d.ts", diff --git a/src/codegen/makeRawFuncs.js b/src/codegen/makeRawFuncs.js index f8218671..048f2d8e 100644 --- a/src/codegen/makeRawFuncs.js +++ b/src/codegen/makeRawFuncs.js @@ -2046,17 +2046,38 @@ export function makeRawFunctions(simplify, isTestnet) { new RawFunc( "__helios__ratio__is_valid_data", `(data) -> { - __helios__common__test_list_data( - data, + __helios__common__test_list_data( + data, + __helios__common__test_list_head_data( + __helios__int__is_valid_data, __helios__common__test_list_head_data( - __helios__int__is_valid_data, - __helios__common__test_list_head_data( - __helios__int__is_valid_data, - __helios__common__test_list_empty - ) + (bottom_data) -> { + __core__chooseData( + bottom_data, + () -> {false}, + () -> {false}, + () -> {false}, + () -> { + bottom = __core__unIData__safe(bottom_data); + + __core__ifThenElse( + __core__lessThanInteger(0, bottom), + () -> { + true + }, + () -> { + false + } + )() + }, + () -> {false} + )() + }, + __helios__common__test_list_empty ) ) - }` + ) + }` ) ) add( @@ -3562,27 +3583,57 @@ export function makeRawFunctions(simplify, isTestnet) { ) add( new RawFunc( - `__helios__bytearray__is_valid_data_fixed_length`, - `(data, n) -> { - __core__chooseData( - data, - () -> {false}, - () -> {false}, - () -> {false}, - () -> {false}, - () -> { - bytes = __core__unBData__safe(data); - __core__ifThenElse( - __core__equalsInteger(__core__lengthOfByteString(bytes), n), - () -> { - true - }, - () -> { - false - } - )() - } - )() + "__helios__bytearray__is_valid_data_fixed_length", + `(n) -> { + (data) -> { + __core__chooseData( + data, + () -> {false}, + () -> {false}, + () -> {false}, + () -> {false}, + () -> { + bytes = __core__unBData__safe(data); + __core__ifThenElse( + __core__equalsInteger(__core__lengthOfByteString(bytes), n), + () -> { + true + }, + () -> { + false + } + )() + } + )() + } + }` + ) + ) + add( + new RawFunc( + "__helios__bytearray__is_valid_data_max_length", + `(n) -> { + (data) -> { + __core__chooseData( + data, + () -> {false}, + () -> {false}, + () -> {false}, + () -> {false}, + () -> { + bytes = __core__unBData__safe(data); + __core__ifThenElse( + __core__lessThanEqualsInteger(__core__lengthOfByteString(bytes), n), + () -> { + true + }, + () -> { + false + } + )() + } + )() + } }` ) ) @@ -6729,7 +6780,7 @@ export function makeRawFunctions(simplify, isTestnet) { add( new RawFunc( "__helios__scripthash__is_valid_data", - `(data) -> {__helios__bytearray__is_valid_data_fixed_length(data, 28)}` + `__helios__bytearray__is_valid_data_fixed_length(28)` ) ) @@ -6742,14 +6793,6 @@ export function makeRawFunctions(simplify, isTestnet) { ]) { // Hash builtins addByteArrayLikeFuncs(`__helios__${hash}`) - add( - new RawFunc( - `__helios__${hash}__is_valid_data`, - `(data) -> { - __helios__bytearray__is_valid_data_fixed_length(data, ${hash == "datumhash" ? 32 : 28}) - }` - ) - ) add( new RawFunc( `__helios__${hash}__from_script_hash`, @@ -6767,13 +6810,70 @@ export function makeRawFunctions(simplify, isTestnet) { ) ) } + // is_valid_data is different for each hash + add( + new RawFunc( + "__helios__pubkeyhash__is_valid_data", + "__helios__bytearray__is_valid_data_fixed_length(28)" + ) + ) + add( + new RawFunc( + "__helios__validatorhash__is_valid_data", + "__helios__bytearray__is_valid_data_fixed_length(28)" + ) + ) + add( + new RawFunc( + "__helios__stakingvalidatorhash__is_valid_data", + "__helios__bytearray__is_valid_data_fixed_length(28)" + ) + ) + add( + new RawFunc( + "__helios__datumhash__is_valid_data", + "__helios__bytearray__is_valid_data_fixed_length(32)" + ) + ) + + add( + new RawFunc( + "__helios__mintingpolicyhash__is_valid_data", + `(data) -> { + __core__chooseData( + data, + () -> {false}, + () -> {false}, + () -> {false}, + () -> {false}, + () -> { + bytes = __core__unBData__safe(data); + n = __core__lengthOfByteString(bytes); + __core__ifThenElse( + __core__equalsInteger(n, 0), + () -> { + true + }, + () -> { + __core__ifThenElse( + __core__equalsInteger(n, 28), + true, + false + ) + } + )() + } + )() + }` + ) + ) // PubKey builtin addByteArrayLikeFuncs("__helios__pubkey") add( new RawFunc( "__helios__pubkey__is_valid_data", - `(data) -> {__helios__bytearray__is_valid_data_fixed_length(data, 32)}` + `__helios__bytearray__is_valid_data_fixed_length(32)` ) ) add( @@ -8276,7 +8376,7 @@ export function makeRawFunctions(simplify, isTestnet) { __core__chooseList( __core__tailList__safe(fields), () -> { - __helios__bytearray__is_valid_data_fixed_length(__core__headList__safe(fields), 32) + __helios__bytearray__is_valid_data_fixed_length(32)(__core__headList__safe(fields)) }, () -> { false @@ -8639,7 +8739,7 @@ export function makeRawFunctions(simplify, isTestnet) { ) add( new RawFunc( - `__helios__data__as_strict[${FTPP}0]`, + `__helios__data__as_strictly[${FTPP}0]`, `(data) -> { __core__ifThenElse( ${FTPP}0__is_valid_data(data), @@ -9384,7 +9484,7 @@ export function makeRawFunctions(simplify, isTestnet) { add( new RawFunc( "__helios__stakinghash__is_stakekey", - "__helios__spendingcredential__is_stakekey" + "__helios__spendingcredential__is_pubkey" ) ) add( @@ -9446,7 +9546,39 @@ export function makeRawFunctions(simplify, isTestnet) { __core__chooseData( data, () -> { - true + pair = __core__unConstrData__safe(data); + tag = __core__fstPair(pair); + fields = __core__sndPair(pair); + + __core__ifThenElse( + __core__equalsInteger(tag, 0), + () -> { + __helios__common__test_list_head_data( + __helios__stakinghash__is_valid_data, + __helios__common__test_list_empty + )(fields) + }, + () -> { + __core__ifThenElse( + __core__equalsInteger(tag, 1), + () -> { + __helios__common__test_list_head_data( + __helios__int__is_valid_data, + __helios__common__test_list_head_data( + __helios__int__is_valid_data, + __helios__common__test_list_head_data( + __helios__int__is_valid_data, + __helios__common__test_list_empty + ) + ) + )(fields) + }, + () -> { + false + } + )() + } + )() }, () -> {false}, () -> {false}, @@ -9556,16 +9688,87 @@ export function makeRawFunctions(simplify, isTestnet) { new RawFunc( "__helios__timerange__is_valid_data", `(data) -> { - __core__chooseData( + test_inner = (data) -> { + __helios__common__test_constr_data_2( + data, + 0, + (data) -> { + __core__chooseData( + data, + () -> { + pair = __core__unConstrData__safe(data); + tag = __core__fstPair(pair); + fields = __core__sndPair(pair); + __core__ifThenElse( + __core__equalsInteger(tag, 0), + () -> { + __core__chooseList( + fields, + true, + false + ) + }, + () -> { + __core__ifThenElse( + __core__equalsInteger(tag, 2), + () -> { + __core__chooseList( + fields, + true, + false + ) + }, + () -> { + __core__ifThenElse( + __core__equalsInteger(tag, 1), + () -> { + __core__chooseList( + fields, + () -> { + false + }, + () -> { + head = __core__headList__safe(fields); + __core__ifThenElse( + __helios__time__is_valid_data(head), + () -> { + __core__chooseList( + __core__tailList__safe(fields), + true, + false + ) + }, + () -> { + false + } + )() + } + )() + }, + () -> { + false + } + )() + } + )() + } + )() + }, + () -> {false}, + () -> {false}, + () -> {false}, + () -> {false} + )() + }, + __helios__bool__is_valid_data + ) + }; + __helios__common__test_constr_data_2( data, - () -> { - true - }, - () -> {false}, - () -> {false}, - () -> {false}, - () -> {false} - )() + 0, + test_inner, + test_inner + ) }` ) ) @@ -9850,7 +10053,7 @@ export function makeRawFunctions(simplify, isTestnet) { new RawFunc( "__helios__assetclass__is_valid_data", `(data) -> { - __helios__common__test_constr_data_2(data, 0, __helios__mintingpolicyhash__is_valid_data, __helios__bytearray__is_valid_data) + __helios__common__test_constr_data_2(data, 0, __helios__mintingpolicyhash__is_valid_data, __helios__bytearray__is_valid_data_max_length(32)) }` ) ) @@ -10071,6 +10274,7 @@ export function makeRawFunctions(simplify, isTestnet) { head = __core__headList__safe(map); key = __core__fstPair(head); value = __core__sndPair(head); + tail = __core__tailList__safe(map); __core__ifThenElse( __helios__mintingpolicyhash__is_valid_data(key), () -> { @@ -10089,19 +10293,19 @@ export function makeRawFunctions(simplify, isTestnet) { __core__chooseList( inner, () -> { - true + recurse(recurse, tail) }, () -> { head = __core__headList__safe(inner); key = __core__fstPair(head); value = __core__sndPair(head); __core__ifThenElse( - __helios__bytearray__is_valid_data(key), + __helios__bytearray__is_valid_data_max_length(32)(key), () -> { __core__ifThenElse( __helios__int__is_valid_data(value), () -> { - true + recurse(recurse, tail) }, () -> { false diff --git a/src/program/version.js b/src/program/version.js index 576f815d..de82951f 100644 --- a/src/program/version.js +++ b/src/program/version.js @@ -1 +1 @@ -export const VERSION = "0.17.0-83" +export const VERSION = "0.17.0-84" diff --git a/src/typecheck/hashes.js b/src/typecheck/hashes.js index 3a0567c0..a85e72bd 100644 --- a/src/typecheck/hashes.js +++ b/src/typecheck/hashes.js @@ -5,7 +5,8 @@ import { ByteArrayType, genCommonInstanceMembers, genCommonTypeMembers, - genCommonEnumTypeMembers + genCommonEnumTypeMembers, + RawDataType } from "./primitives.js" /** @@ -138,6 +139,7 @@ export const StakingHashType = new GenericType({ genTypeMembers: (self) => ({ StakeKey: StakingHashStakeKeyType, Validator: StakingHashValidatorType, + is_valid_data: new FuncType([RawDataType], BoolType), new_stakekey: new FuncType([PubKeyHashType], StakingHashStakeKeyType), new_validator: new FuncType( [StakingValidatorHashType], diff --git a/src/typecheck/primitives.js b/src/typecheck/primitives.js index 4e559103..5bbb193d 100644 --- a/src/typecheck/primitives.js +++ b/src/typecheck/primitives.js @@ -205,7 +205,7 @@ export const RawDataType = new GenericType({ return new ParametricData([a], a.ref) })(), - as_strict: (() => { + as_strictly: (() => { const a = new Parameter("a", `${FTPP}0`, new DefaultTypeClass()) return new ParametricData([a], a.ref) diff --git a/test/Address.test.js b/test/Address.test.js index 37dd3891..a238e26d 100644 --- a/test/Address.test.js +++ b/test/Address.test.js @@ -1,5 +1,15 @@ -import { describe } from "node:test" -import { compileAndRunMany, True, map, int, False, bytes } from "./utils.js" +import { describe, it } from "node:test" +import { + compileAndRunMany, + True, + map, + int, + False, + bytes, + compileForRun, + list, + constr +} from "./utils.js" describe("Address", () => { const addressFromValidatorScript = `testing address_from_validator @@ -44,4 +54,112 @@ describe("Address", () => { output: True } ]) + + describe("Address::is_valid_data", () => { + const runner = compileForRun(`testing address_is_valid_data + func main(d: Data) -> Bool { + Address::is_valid_data(d) + }`) + + it("returns true for constrData with tag 1 and two fields: one pubkey spending credential, and one empty/none option", () => { + runner( + [constr(0, constr(0, bytes(new Array(28).fill(0))), constr(1))], + True + ) + }) + + it("returns false for constrData with tag 1", () => { + runner( + [constr(1, constr(0, bytes(new Array(28).fill(0))), constr(1))], + False + ) + }) + + it("returns true for constrData with tag 0 and two fields: one pubkey spending credential, and one empty/none option", () => { + runner( + [constr(0, constr(1, bytes(new Array(28).fill(0))), constr(1))], + True + ) + }) + + it("returns true for constrData with tag 0 and two fields: one pubkey spending credential, and one empty/none option", () => { + runner( + [constr(0, constr(1, bytes(new Array(28).fill(0))), constr(1))], + True + ) + }) + + it("returns false for constrData with tag 0 and two fields: one garbage spending credential, and one empty/none option", () => { + runner( + [constr(0, constr(2, bytes(new Array(28).fill(0))), constr(1))], + False + ) + }) + + it("returns true for constrData with tag 0 and two fields: one validator spending credential, and one empty/none option", () => { + runner( + [constr(0, constr(1, bytes(new Array(28).fill(0))), constr(1))], + True + ) + }) + + it("returns false for constrData with tag 0 and two fields: one validator spending credential, and one garbage staking credential option", () => { + runner( + [constr(0, constr(1, bytes(new Array(28).fill(0))), constr(0))], + False + ) + }) + + it("returns true for constrData with tag 0 and two fields: one validator spending credential, and one pubkey credential option", () => { + runner( + [ + constr( + 0, + constr(1, bytes(new Array(28).fill(0))), + constr( + 0, + constr(0, constr(0, bytes(new Array(28).fill(0)))) + ) + ) + ], + True + ) + }) + + it("returns false for constrData with tag 0 and two fields: one validator spending credential, and one pubkey credential option with 29 bytes", () => { + runner( + [ + constr( + 0, + constr(1, bytes(new Array(28).fill(0))), + constr( + 0, + constr(0, constr(0, bytes(new Array(29).fill(0)))) + ) + ) + ], + False + ) + }) + + it("returns false for constrData without fields", () => { + runner([constr(0)], False) + }) + + it("returns false for iData", () => { + runner([int(0)], False) + }) + + it("returns false for bData", () => { + runner([bytes([])], False) + }) + + it("returns false for listData", () => { + runner([list()], False) + }) + + it("returns false for mapData", () => { + runner([map([])], False) + }) + }) }) diff --git a/test/AssetClass.test.js b/test/AssetClass.test.js new file mode 100644 index 00000000..efd6de3c --- /dev/null +++ b/test/AssetClass.test.js @@ -0,0 +1,122 @@ +import { describe, it } from "node:test" +import { + False, + True, + bytes, + compileForRun, + constr, + int, + list, + map +} from "./utils.js" + +describe("AssetClass", () => { + describe("AssetClass::is_valid_data", () => { + const runner = compileForRun(` + testing assetclass_is_valid_data + func main(a: Data) -> Bool { + AssetClass::is_valid_data(a) + }`) + + it("returns true for constrData with tag equal to 0 and two bData fields with 28 bytes", () => { + runner( + [ + constr( + 0, + bytes(new Array(28).fill(0)), + bytes(new Array(28).fill(0)) + ) + ], + True + ) + }) + + it("returns false for constrData with tag equal to 0 and one bData field with 28 bytes and another with 33 bytes", () => { + runner( + [ + constr( + 0, + bytes(new Array(28).fill(0)), + bytes(new Array(33).fill(0)) + ) + ], + False + ) + }) + + it("returns false for constrData with tag equal to 0 and one bData field with 27 bytes and another empty bData field", () => { + runner([constr(0, bytes(new Array(27).fill(0)), bytes([]))], False) + }) + + it("returns true for constrData with tag equal to 0 and one bData field with 28 bytes and another empty bData field", () => { + runner([constr(0, bytes(new Array(28).fill(0)), bytes([]))], True) + }) + + it("returns false for constrData with tag equal to 0 and three bData fields with 28 bytes", () => { + runner( + [ + constr( + 0, + bytes(new Array(28).fill(0)), + bytes(new Array(28).fill(0)), + bytes(new Array(28).fill(0)) + ) + ], + False + ) + }) + + it("returns false for constrData with tag equal to 0 and one bData field with 28 bytes", () => { + runner([constr(0, bytes(new Array(28).fill(0)))], False) + }) + + it("returns false for constrData with tag equal to 0 and one iData field", () => { + runner([constr(0, int(0))], False) + }) + + it("returns false for constrData with tag equal to 0 and no fields", () => { + runner([constr(0)], False) + }) + + it("returns false for constrData with tag equal to 1 and one bData field with 28 bytes", () => { + runner([constr(1, bytes(new Array(28).fill(0)))], False) + }) + + it("returns false for constrData with tag equal to 1 and two bData fields with 28 bytes", () => { + runner( + [ + constr( + 1, + bytes(new Array(28).fill(0)), + bytes(new Array(28).fill(0)) + ) + ], + False + ) + }) + + it("returns false for constrData with tag equal to 1 and one iData field", () => { + runner([constr(1, int(0))], False) + }) + + it("returns false for constrData with tag equal to 1 and no fields", () => { + runner([constr(1)], False) + }) + + it("returns false for bData", () => { + runner([bytes([])], False) + }) + + it("returns false for iData", () => { + runner([int(0)], False) + }) + + it("returns false for mapData", () => { + runner([map([])], False) + }) + + it("returns false for listData", () => { + runner([list()], False) + }) + }) +}) diff --git a/test/Bool.test.js b/test/Bool.test.js index 2a77ec7a..1f7bffd6 100644 --- a/test/Bool.test.js +++ b/test/Bool.test.js @@ -322,47 +322,47 @@ describe("Bool", () => { Bool::is_valid_data(data) }`) - it("ok for False", () => { + it("returns true for False", () => { runner([False], True) }) - it("ok for True", () => { + it("returns true for True", () => { runner([True], True) }) - it("ok for constr 0 []", () => { + it("returns true for constrData 0 []", () => { runner([constr(0)], True) }) - it("ok for constr 1 []", () => { + it("returns true for constrData 1 []", () => { runner([constr(1)], True) }) - it("nok for constr 2 []", () => { + it("returns false for constrData 2 []", () => { runner([constr(2)], False) }) - it("nok for constr 0 []", () => { + it("returns false for constrData 0 []", () => { runner([constr(0, int(0))], False) }) - it("nok for constr -1 []", () => { + it("returns false for constrData -1 []", () => { runner([constr(-1, int(0))], False) }) - it("nok for bData", () => { + it("returns false for bData", () => { runner([bytes("")], False) }) - it("nok for iData", () => { + it("returns false for iData", () => { runner([int(0)], False) }) - it("nok for list", () => { + it("returns false for listData", () => { runner([list()], False) }) - it("nok for map", () => { + it("returns false for mapData", () => { runner([map([])], False) }) }) diff --git a/test/ByteArray.test.js b/test/ByteArray.test.js new file mode 100644 index 00000000..58a670a3 --- /dev/null +++ b/test/ByteArray.test.js @@ -0,0 +1,50 @@ +import { describe, it } from "node:test" +import { + False, + True, + bytes, + compileForRun, + constr, + int, + list, + map +} from "./utils.js" +import { encodeUtf8 } from "@helios-lang/codec-utils" + +describe("ByteArray", () => { + describe("ByteArray::is_valid_data", () => { + const runner = compileForRun(` + testing bytearray_is_valid_data + func main(a: Data) -> Bool { + ByteArray::is_valid_data(a) + }`) + + it("returns true for empty bData", () => { + runner([bytes([])], True) + }) + + it('returns true for ascii "Hello World"', () => { + runner([bytes(encodeUtf8("Hello World"))], True) + }) + + it("returns true for #ffff", () => { + runner([bytes([255, 255])], True) + }) + + it("returns false for iData", () => { + runner([int(0)], False) + }) + + it("returns false for constrData", () => { + runner([constr(0)], False) + }) + + it("returns false for mapData", () => { + runner([map([])], False) + }) + + it("returns false for listData", () => { + runner([list()], False) + }) + }) +}) diff --git a/test/Data.test.js b/test/Data.test.js index df914b6d..24e66757 100644 --- a/test/Data.test.js +++ b/test/Data.test.js @@ -11,11 +11,6 @@ import { } from "./utils.js" describe("Data", () => { - const dataAsScript = `testing data_as - func main(a: Data) -> Int { - a.as[Int] - }` - describe("Data.as", () => { const runner1 = compileForRun(`testing data_as func main(a: Data) -> Int { @@ -100,23 +95,23 @@ describe("Data", () => { Data::is_valid_data(data) }`) - it("ok for int", () => { + it("returns true for iData", () => { runner([int(0)], True) }) - it("ok for bytearray", () => { + it("returns true for bData", () => { runner([bytes("")], True) }) - it("ok for list", () => { + it("returns true for listData", () => { runner([list()], True) }) - it("ok for map", () => { + it("returns true for mapData", () => { runner([map([])], True) }) - it("ok for constr", () => { + it("returns true for constrData", () => { runner([constr(123)], True) }) }) diff --git a/test/Duration.test.js b/test/Duration.test.js new file mode 100644 index 00000000..9af2d1cc --- /dev/null +++ b/test/Duration.test.js @@ -0,0 +1,40 @@ +import { describe, it } from "node:test" +import { + False, + True, + bytes, + compileForRun, + constr, + int, + list, + map +} from "./utils.js" + +describe("Duration", () => { + describe("Duration::is_valid_data", () => { + const runner = compileForRun(`testing duration_is_valid_data + func main(data: Data) -> Bool { + Duration::is_valid_data(data) + }`) + + it("returns true for int", () => { + runner([int(0)], True) + }) + + it("returns false for bytes", () => { + runner([bytes("")], False) + }) + + it("returns false for list", () => { + runner([list()], False) + }) + + it("returns false for constr", () => { + runner([constr(0)], False) + }) + + it("returns false for map", () => { + runner([map([])], False) + }) + }) +}) diff --git a/test/Int.test.js b/test/Int.test.js index 2c3436b1..c68440fb 100644 --- a/test/Int.test.js +++ b/test/Int.test.js @@ -976,23 +976,23 @@ describe("Int", () => { Int::is_valid_data(data) }`) - it("true for int", () => { + it("returns true for int", () => { runner([int(0)], True) }) - it("false for bytes", () => { + it("returns false for bytes", () => { runner([bytes("")], False) }) - it("false for list", () => { + it("returns false for list", () => { runner([list()], False) }) - it("false for constr", () => { + it("returns false for constr", () => { runner([constr(0)], False) }) - it("false for map", () => { + it("returns false for map", () => { runner([map([])], False) }) }) diff --git a/test/List.test.js b/test/List.test.js index 1becf4f9..f03eff18 100644 --- a/test/List.test.js +++ b/test/List.test.js @@ -1,5 +1,15 @@ import { describe, it } from "node:test" -import { False, True, bool, compileForRun, int, list } from "./utils.js" +import { + False, + True, + bool, + bytes, + compileForRun, + constr, + int, + list, + map +} from "./utils.js" describe("List", () => { describe("[]Int::new_const", () => { @@ -205,4 +215,84 @@ describe("List", () => { runner([list(int(0)), int(1)], list(int(0), int(1))) }) }) + + describe("[]Int::is_valid_data", () => { + const runner = compileForRun(` + testing int_list_is_valid_data + func main(a: Data) -> Bool { + []Int::is_valid_data(a) + }`) + + it("returns true for empty listData", () => { + runner([list()], True) + }) + + it("returns true for listData with one iData entry", () => { + runner([list(int(0))], True) + }) + + it("returns true for listData with two iData entries", () => { + runner([list(int(1), int(2))], True) + }) + + it("returns false for listData with one iData entry and another listData entry", () => { + runner([list(int(1), list())], False) + }) + + it("returns false for iData", () => { + runner([int(0)], False) + }) + + it("returns false for bData", () => { + runner([bytes([])], False) + }) + + it("returns false for mapData", () => { + runner([map([])], False) + }) + + it("returns false for constrData", () => { + runner([constr(123)], False) + }) + }) + + describe("[][]Int::is_valid_data", () => { + const runner = compileForRun(` + testing nested_int_list_is_valid_data + func main(a: Data) -> Bool { + [][]Int::is_valid_data(a) + }`) + + it("returns true for empty listData", () => { + runner([list()], True) + }) + + it("returns false for listData with one nested iData entry", () => { + runner([list(list(int(0)))], True) + }) + + it("returns false for listData with two iData entries", () => { + runner([list(int(1), int(2))], False) + }) + + it("returns true for listData with one iData entry in a the first nested list, and another empty listData entry", () => { + runner([list(list(int(1)), list())], True) + }) + + it("returns false for iData", () => { + runner([int(0)], False) + }) + + it("returns false for bData", () => { + runner([bytes([])], False) + }) + + it("returns false for mapData", () => { + runner([map([])], False) + }) + + it("returns false for constrData", () => { + runner([constr(123)], False) + }) + }) }) diff --git a/test/Map.test.js b/test/Map.test.js index 49ecef77..86566dd4 100644 --- a/test/Map.test.js +++ b/test/Map.test.js @@ -1,5 +1,14 @@ import { describe, it } from "node:test" -import { True, map, int, False, list, compileForRun, bytes } from "./utils.js" +import { + True, + map, + int, + False, + list, + compileForRun, + bytes, + constr +} from "./utils.js" describe("Map", () => { describe("Map[Int]Int == Map[Int]Int", () => { @@ -243,17 +252,33 @@ describe("Map", () => { Map[Int]Int::is_valid_data(data) }`) - it("ok for empty map", () => { + it("returns true for empty map", () => { runner([map([])], True) }) - it("ok for map with 1 entry", () => { + it("returns true for map with 1 entry", () => { runner([map([[int(0), int(0)]])], True) }) - it("ok for map with 1 wrong entry", () => { + it("returns false for map with one bData value", () => { runner([map([[int(0), bytes("")]])], False) }) + + it("returns false for iData", () => { + runner([int(0)], False) + }) + + it("returns false for bData", () => { + runner([bytes([])], False) + }) + + it("returns false for constrData", () => { + runner([constr(123)], False) + }) + + it("returns false for listData", () => { + runner([list()], False) + }) }) describe("Map[Int]Int.append", () => { diff --git a/test/MintingPolicyHash.test.js b/test/MintingPolicyHash.test.js index f4eae72f..7399a9e4 100644 --- a/test/MintingPolicyHash.test.js +++ b/test/MintingPolicyHash.test.js @@ -1,5 +1,14 @@ import { describe, it } from "node:test" -import { True, bytes, compileForRun } from "./utils.js" +import { + False, + True, + bytes, + compileForRun, + constr, + int, + list, + map +} from "./utils.js" describe("MintingPolicyHash", () => { it("can be converted to ScriptHash and back", () => { @@ -16,4 +25,44 @@ describe("MintingPolicyHash", () => { True ) }) + + describe("MintingPolicyHash::is_valid_data", () => { + const runner = compileForRun(` + testing mintingpolicyhash_is_valid_data + func main(a: Data) -> Bool { + MintingPolicyHash::is_valid_data(a) + }`) + + it("returns true for empty bData (ADA", () => { + runner([bytes([])], True) + }) + + it("returns false for #ffff", () => { + runner([bytes([255, 255])], False) + }) + + it("returns true for bData with 28 bytes", () => { + runner([bytes(new Array(28).fill(255))], True) + }) + + it("returns false for bData with 29 bytes", () => { + runner([bytes(new Array(29).fill(255))], False) + }) + + it("returns false for iData", () => { + runner([int(0)], False) + }) + + it("returns false for constrData", () => { + runner([constr(0)], False) + }) + + it("returns false for mapData", () => { + runner([map([])], False) + }) + + it("returns false for listData", () => { + runner([list()], False) + }) + }) }) diff --git a/test/Option.test.js b/test/Option.test.js new file mode 100644 index 00000000..f9f005fa --- /dev/null +++ b/test/Option.test.js @@ -0,0 +1,64 @@ +import { describe, it } from "node:test" +import { + False, + True, + bytes, + compileForRun, + constr, + int, + list, + map +} from "./utils.js" + +describe("Option", () => { + describe("Option::is_valid_data", () => { + const runner = compileForRun(`testing option_is_valid_data + func main(data: Data) -> Bool { + Option[Int]::is_valid_data(data) + }`) + + it("returns true for constrData with tag equal to 0 and one iData field", () => { + runner([constr(0, int(0))], True) + }) + + it("returns false for constrData with tag equal to 0 and one bData field", () => { + runner([constr(0, bytes([]))], False) + }) + + it("returns false for constrData with tag equal to 0 and too many iData fields", () => { + runner([constr(0, int(0), int(1))], False) + }) + + it("returns false for constrData with tag equal to 0 and no fields", () => { + runner([constr(0)], False) + }) + + it("returns true for constrData with tag equal to 1 and no fields", () => { + runner([constr(1)], True) + }) + + it("returns false for constrData with tag equal to 1 and one field", () => { + runner([constr(1, int(0))], False) + }) + + it("returns false for constrData with wrong tag", () => { + runner([constr(123)], False) + }) + + it("returns false for iData", () => { + runner([int(0)], False) + }) + + it("returns false for bData", () => { + runner([bytes([])], False) + }) + + it("returns false for listData", () => { + runner([list()], False) + }) + + it("returns false for mapData", () => { + runner([map([])], False) + }) + }) +}) diff --git a/test/PubKey.test.js b/test/PubKey.test.js new file mode 100644 index 00000000..f5970420 --- /dev/null +++ b/test/PubKey.test.js @@ -0,0 +1,53 @@ +import { describe, it } from "node:test" +import { + False, + True, + bytes, + compileForRun, + constr, + int, + list, + map +} from "./utils.js" + +describe("PubKey", () => { + describe("PubKey::is_valid_data", () => { + const runner = compileForRun(` + testing pubkey_is_valid_data + func main(a: Data) -> Bool { + PubKey::is_valid_data(a) + }`) + + it("returns false for empty bData", () => { + runner([bytes([])], False) + }) + + it("returns false for #ffff", () => { + runner([bytes([255, 255])], False) + }) + + it("returns false for bData with 28 bytes", () => { + runner([bytes(new Array(28).fill(255))], False) + }) + + it("returns true for bData with 32 bytes", () => { + runner([bytes(new Array(32).fill(255))], True) + }) + + it("returns false for iData", () => { + runner([int(0)], False) + }) + + it("returns false for constrData", () => { + runner([constr(0)], False) + }) + + it("returns false for mapData", () => { + runner([map([])], False) + }) + + it("returns false for listData", () => { + runner([list()], False) + }) + }) +}) diff --git a/test/PubKeyHash.test.js b/test/PubKeyHash.test.js new file mode 100644 index 00000000..848e1263 --- /dev/null +++ b/test/PubKeyHash.test.js @@ -0,0 +1,53 @@ +import { describe, it } from "node:test" +import { + False, + True, + bytes, + compileForRun, + constr, + int, + list, + map +} from "./utils.js" + +describe("PubKeyHash", () => { + describe("PubKeyHash::is_valid_data", () => { + const runner = compileForRun(` + testing pubkeyhash_is_valid_data + func main(a: Data) -> Bool { + PubKeyHash::is_valid_data(a) + }`) + + it("returns false for empty bData", () => { + runner([bytes([])], False) + }) + + it("returns false for #ffff", () => { + runner([bytes([255, 255])], False) + }) + + it("returns true for bData with 28 bytes", () => { + runner([bytes(new Array(28).fill(255))], True) + }) + + it("returns false for bData with 32 bytes", () => { + runner([bytes(new Array(32).fill(255))], False) + }) + + it("returns false for iData", () => { + runner([int(0)], False) + }) + + it("returns false for constrData", () => { + runner([constr(0)], False) + }) + + it("returns false for mapData", () => { + runner([map([])], False) + }) + + it("returns false for listData", () => { + runner([list()], False) + }) + }) +}) diff --git a/test/Ratio.test.js b/test/Ratio.test.js index eedefbad..ee457987 100644 --- a/test/Ratio.test.js +++ b/test/Ratio.test.js @@ -661,40 +661,56 @@ describe("Ratio", () => { Ratio::is_valid_data(data) }`) - it("true for ratio", () => { + it("returns true for ratio", () => { runner([ratio(1, 1)], True) }) - it("true for list of two ints", () => { + it("returns true for list of two ints", () => { runner([list(int(1), int(1))], True) }) - it("false for int", () => { + it("returns true for list of negative and positive iData", () => { + runner([list(int(-1), int(1))], True) + }) + + it("returns false for iData", () => { runner([int(1)], False) }) - it("false for bytes", () => { + it("returns false for bData", () => { runner([bytes("")], False) }) - it("false for constr", () => { + it("returns false for constrData", () => { runner([constr(0)], False) }) - it("false for map", () => { + it("returns false for mapData", () => { runner([map([])], False) }) - it("false for empty list", () => { + it("returns false for empty listData", () => { runner([list()], False) }) - it("false for list with wrong entry", () => { - runner([list(int(0), bytes(""))], False) + it("returns false for lisData only one iData entry", () => { + runner([list(int(1))], False) + }) + + it("returns false for lisData with zero iData second entry", () => { + runner([list(int(1), int(0))], False) + }) + + it("returns false for lisData with negative iData second entry", () => { + runner([list(int(1), int(-1))], False) + }) + + it("returns false for lisData with bData second entry", () => { + runner([list(int(1), bytes(""))], False) }) - it("false for list with too many entries", () => { - runner([list(int(0), int(0), int(0))], False) + it("returns false for listData with too many entries", () => { + runner([list(int(1), int(1), int(1))], False) }) }) }) diff --git a/test/Real.test.js b/test/Real.test.js index cccb54e6..87ad9305 100644 --- a/test/Real.test.js +++ b/test/Real.test.js @@ -800,23 +800,23 @@ describe("Real", () => { Real::is_valid_data(a) }`) - it("ok for iData", () => { + it("returns true for iData", () => { runner([int(0)], True) }) - it("nok for bData", () => { + it("returns false for bData", () => { runner([bytes("")], False) }) - it("nok for list", () => { + it("returns false for list", () => { runner([list()], False) }) - it("nok for map", () => { + it("returns false for map", () => { runner([map([])], False) }) - it("nok for constr", () => { + it("returns false for constr", () => { runner([constr(0)], False) }) }) diff --git a/test/ScriptHash.test.js b/test/ScriptHash.test.js new file mode 100644 index 00000000..b8094584 --- /dev/null +++ b/test/ScriptHash.test.js @@ -0,0 +1,53 @@ +import { describe, it } from "node:test" +import { + False, + True, + bytes, + compileForRun, + constr, + int, + list, + map +} from "./utils.js" + +describe("ScriptHash", () => { + describe("ScriptHash::is_valid_data", () => { + const runner = compileForRun(` + testing scripthash_is_valid_data + func main(a: Data) -> Bool { + ScriptHash::is_valid_data(a) + }`) + + it("returns false for empty bData", () => { + runner([bytes([])], False) + }) + + it("returns false for #ffff", () => { + runner([bytes([255, 255])], False) + }) + + it("returns true for bData with 28 bytes", () => { + runner([bytes(new Array(28).fill(255))], True) + }) + + it("returns false for bData with 32 bytes", () => { + runner([bytes(new Array(32).fill(255))], False) + }) + + it("returns false for iData", () => { + runner([int(0)], False) + }) + + it("returns false for constrData", () => { + runner([constr(0)], False) + }) + + it("returns false for mapData", () => { + runner([map([])], False) + }) + + it("returns false for listData", () => { + runner([list()], False) + }) + }) +}) diff --git a/test/SpendingCredential.test.js b/test/SpendingCredential.test.js new file mode 100644 index 00000000..896164ea --- /dev/null +++ b/test/SpendingCredential.test.js @@ -0,0 +1,87 @@ +import { describe, it } from "node:test" +import { + False, + True, + bytes, + compileForRun, + constr, + int, + list, + map +} from "./utils.js" + +describe("SpendingCredential", () => { + describe("SpendingCredential::is_valid_data", () => { + const runner = compileForRun(` + testing spendingcredential_is_valid_data + func main(a: Data) -> Bool { + SpendingCredential::is_valid_data(a) + }`) + + it("returns true for constrData with tag equal to 0 and one bData field with 28 bytes", () => { + runner([constr(0, bytes(new Array(28).fill(0)))], True) + }) + + it("returns false for constrData with tag equal to 0 and two bData fields with 28 bytes", () => { + runner( + [ + constr( + 0, + bytes(new Array(28).fill(0)), + bytes(new Array(28).fill(0)) + ) + ], + False + ) + }) + + it("returns false for constrData with tag equal to 0 and one iData field", () => { + runner([constr(0, int(0))], False) + }) + + it("returns false for constrData with tag equal to 0 and no fields", () => { + runner([constr(0)], False) + }) + + it("returns true for constrData with tag equal to 1 and one bData field with 28 bytes", () => { + runner([constr(1, bytes(new Array(28).fill(0)))], True) + }) + + it("returns false for constrData with tag equal to 1 and two bData fields with 28 bytes", () => { + runner( + [ + constr( + 1, + bytes(new Array(28).fill(0)), + bytes(new Array(28).fill(0)) + ) + ], + False + ) + }) + + it("returns false for constrData with tag equal to 1 and one iData field", () => { + runner([constr(1, int(0))], False) + }) + + it("returns false for constrData with tag equal to 1 and no fields", () => { + runner([constr(1)], False) + }) + + it("returns false for bData", () => { + runner([bytes([])], False) + }) + + it("returns false for iData", () => { + runner([int(0)], False) + }) + + it("returns false for mapData", () => { + runner([map([])], False) + }) + + it("returns false for listData", () => { + runner([list()], False) + }) + }) +}) diff --git a/test/StakingCredential.test.js b/test/StakingCredential.test.js index 2d80b3b9..b225d947 100644 --- a/test/StakingCredential.test.js +++ b/test/StakingCredential.test.js @@ -1,22 +1,92 @@ import { describe, it } from "node:test" -import { True, bytes, compileForRun } from "./utils.js" +import { + False, + True, + bytes, + compileForRun, + constr, + int, + list, + map +} from "./utils.js" describe("StakingCredential", () => { - it("can destructure hash from StakingCredential", () => { - const runner = compileForRun(`testing staking_cred_destruct - func main(pkh: PubKeyHash) -> Bool { - h_ = StakingHash::new_stakekey(pkh); - cred = StakingCredential::new_hash(h_); - - cred.switch{ - Hash{h} => h.switch{ - StakeKey{p} => p == pkh, + describe("destructure StakingCredential", () => { + it("can destructure hash from StakingCredential", () => { + const runner = compileForRun(`testing staking_cred_destruct + func main(pkh: PubKeyHash) -> Bool { + h_ = StakingHash::new_stakekey(pkh); + cred = StakingCredential::new_hash(h_); + + cred.switch{ + Hash{h} => h.switch{ + StakeKey{p} => p == pkh, + else => error("unexpected") + }, else => error("unexpected") - }, - else => error("unexpected") - } + } + }`) + + runner([bytes("abcd")], True) + }) + }) + + describe("StakingCredential::is_valid_data", () => { + const runner = compileForRun(`testing staking_cred_is_valid_data + func main(d: Data) -> Bool { + StakingCredential::is_valid_data(d) }`) - runner([bytes("abcd")], True) + it("returns true for constrData with tag 0 and one pubkey staking hash field", () => { + runner([constr(0, constr(0, bytes(new Array(28).fill(0))))], True) + }) + + it("returns false for constrData with tag 0 and one pubkey staking hash field with 27 bytes", () => { + runner([constr(0, constr(0, bytes(new Array(27).fill(0))))], False) + }) + + it("returns false for constrData with tag 0 and one pubkey staking hash field with 29 bytes", () => { + runner([constr(0, constr(0, bytes(new Array(29).fill(0))))], False) + }) + + it("returns false for constrData with tag 1 and one pubkey staking hash field", () => { + runner([constr(1, constr(0, bytes(new Array(28).fill(0))))], False) + }) + + it("returns true for constrData with tag 1 and the staking ptr fields", () => { + runner([constr(1, int(0), int(0), int(0))], True) + }) + + it("returns false for constrData with tag 1 and too few staking ptr fields", () => { + runner([constr(1, int(0), int(0))], False) + }) + + it("returns false for constrData with tag 1 and too many staking ptr fields", () => { + runner([constr(1, int(0), int(0), int(0), int(0))], False) + }) + + it("returns false for constrData with tag 2 and one pubkey staking hash field", () => { + runner([constr(2, constr(0, bytes(new Array(28).fill(0))))], False) + }) + + it("returns false for constrData with tag 0 and no fields", () => { + runner([constr(0)], False) + }) + + it("returns false for iData", () => { + runner([int(0)], False) + }) + + it("returns false for bData", () => { + runner([bytes([])], False) + }) + + it("returns false for listData", () => { + runner([list()], False) + }) + + it("returns false for mapData", () => { + runner([map([])], False) + }) }) }) diff --git a/test/StakingHash.test.js b/test/StakingHash.test.js new file mode 100644 index 00000000..04d95c4a --- /dev/null +++ b/test/StakingHash.test.js @@ -0,0 +1,87 @@ +import { describe, it } from "node:test" +import { + False, + True, + bytes, + compileForRun, + constr, + int, + list, + map +} from "./utils.js" + +describe("StakingHash", () => { + describe("StakingHash::is_valid_data", () => { + const runner = compileForRun(` + testing stakinghash_is_valid_data + func main(a: Data) -> Bool { + StakingHash::is_valid_data(a) + }`) + + it("returns true for constrData with tag equal to 0 and one bData field with 28 bytes", () => { + runner([constr(0, bytes(new Array(28).fill(0)))], True) + }) + + it("returns false for constrData with tag equal to 0 and two bData fields with 28 bytes", () => { + runner( + [ + constr( + 0, + bytes(new Array(28).fill(0)), + bytes(new Array(28).fill(0)) + ) + ], + False + ) + }) + + it("returns false for constrData with tag equal to 0 and one iData field", () => { + runner([constr(0, int(0))], False) + }) + + it("returns false for constrData with tag equal to 0 and no fields", () => { + runner([constr(0)], False) + }) + + it("returns true for constrData with tag equal to 1 and one bData field with 28 bytes", () => { + runner([constr(1, bytes(new Array(28).fill(0)))], True) + }) + + it("returns false for constrData with tag equal to 1 and two bData fields with 28 bytes", () => { + runner( + [ + constr( + 1, + bytes(new Array(28).fill(0)), + bytes(new Array(28).fill(0)) + ) + ], + False + ) + }) + + it("returns false for constrData with tag equal to 1 and one iData field", () => { + runner([constr(1, int(0))], False) + }) + + it("returns false for constrData with tag equal to 1 and no fields", () => { + runner([constr(1)], False) + }) + + it("returns false for bData", () => { + runner([bytes([])], False) + }) + + it("returns false for iData", () => { + runner([int(0)], False) + }) + + it("returns false for mapData", () => { + runner([map([])], False) + }) + + it("returns false for listData", () => { + runner([list()], False) + }) + }) +}) diff --git a/test/StakingValidatorHash.test.js b/test/StakingValidatorHash.test.js new file mode 100644 index 00000000..a5e0ed92 --- /dev/null +++ b/test/StakingValidatorHash.test.js @@ -0,0 +1,49 @@ +import { describe, it } from "node:test" +import { + False, + True, + bytes, + compileForRun, + constr, + int, + list, + map +} from "./utils.js" + +describe("StakingValidatorHash", () => { + describe("StakingValidatorHash::is_valid_data", () => { + const runner = compileForRun(` + testing stakingvalidatorhash_is_valid_data + func main(a: Data) -> Bool { + StakingValidatorHash::is_valid_data(a) + }`) + + it("returns false for empty bData", () => { + runner([bytes([])], False) + }) + + it("returns false for #ffff", () => { + runner([bytes([255, 255])], False) + }) + + it("returns true for bData with 28 bytes", () => { + runner([bytes(new Array(28).fill(255))], True) + }) + + it("returns false for iData", () => { + runner([int(0)], False) + }) + + it("returns false for constrData", () => { + runner([constr(0)], False) + }) + + it("returns false for mapData", () => { + runner([map([])], False) + }) + + it("returns false for listData", () => { + runner([list()], False) + }) + }) +}) diff --git a/test/String.test.js b/test/String.test.js index 22645d18..621f787f 100644 --- a/test/String.test.js +++ b/test/String.test.js @@ -1,5 +1,16 @@ -import { describe, it } from "node:test" -import { False, True, compileForRun, str } from "./utils.js" +import { describe, it, run } from "node:test" +import { + False, + True, + bytes, + compileForRun, + constr, + int, + list, + map, + str +} from "./utils.js" +import { encodeUtf8 } from "@helios-lang/codec-utils" describe("String", () => { describe("String == String", () => { @@ -119,4 +130,40 @@ describe("String", () => { runner1([str(""), str("Hello")], True) }) }) + + describe("String::is_valid_data", () => { + const runner = compileForRun(` + testing string_is_valid_data + func main(a: Data) -> Bool { + String::is_valid_data(a) + }`) + + it("returns true for empty bData", () => { + runner([bytes([])], True) + }) + + it('returns true for ascii "Hello World"', () => { + runner([bytes(encodeUtf8("Hello World"))], True) + }) + + it("returns false for #ffff", () => { + runner([bytes([255, 255])], False) + }) + + it("returns false for iData", () => { + runner([int(0)], False) + }) + + it("returns false for constrData", () => { + runner([constr(0)], False) + }) + + it("returns false for mapData", () => { + runner([map([])], False) + }) + + it("returns false for listData", () => { + runner([list()], False) + }) + }) }) diff --git a/test/Time.test.js b/test/Time.test.js new file mode 100644 index 00000000..f79c7c55 --- /dev/null +++ b/test/Time.test.js @@ -0,0 +1,40 @@ +import { describe, it } from "node:test" +import { + False, + True, + bytes, + compileForRun, + constr, + int, + list, + map +} from "./utils.js" + +describe("Time", () => { + describe("Time::is_valid_data", () => { + const runner = compileForRun(`testing time_is_valid_data + func main(data: Data) -> Bool { + Time::is_valid_data(data) + }`) + + it("returns true for int", () => { + runner([int(0)], True) + }) + + it("returns false for bytes", () => { + runner([bytes("")], False) + }) + + it("returns false for list", () => { + runner([list()], False) + }) + + it("returns false for constr", () => { + runner([constr(0)], False) + }) + + it("returns false for map", () => { + runner([map([])], False) + }) + }) +}) diff --git a/test/TimeRange.test.js b/test/TimeRange.test.js new file mode 100644 index 00000000..41628e88 --- /dev/null +++ b/test/TimeRange.test.js @@ -0,0 +1,149 @@ +import { describe, it } from "node:test" +import { + False, + True, + bytes, + compileForRun, + constr, + int, + list, + map +} from "./utils.js" + +describe("TimeRange", () => { + describe("TimeRange::is_valid_data", () => { + const runner = compileForRun(`testing timerange_is_valid_data + func main(d: Data) -> Bool { + TimeRange::is_valid_data(d) + }`) + + it("returns false for constrData with tag 0 and no fields", () => { + runner([constr(0)], False) + }) + + it("returns false for constrData with tag 0 and two fields which themselves have tag 0 and no fields", () => { + runner([constr(0, constr(0), constr(0))], False) + }) + + it("returns true for constrData with the correct structure", () => { + runner( + [ + constr( + 0, + constr(0, constr(1, int(0)), True), + constr(0, constr(1, int(0)), True) + ) + ], + True + ) + }) + + it("returns false if one the boolean properties isn't constrData", () => { + runner( + [ + constr( + 0, + constr(0, constr(1, int(0)), True), + constr(0, constr(1, int(0)), int(0)) + ) + ], + False + ) + }) + + it("returns false if the constrData has too many fields", () => { + runner( + [ + constr( + 0, + constr(0, constr(1, int(0)), True), + constr(0, constr(1, int(0)), True), + constr(0, constr(1, int(0)), True) + ) + ], + False + ) + }) + + it("returns false if one of the inner constrData has too many fields", () => { + runner( + [ + constr( + 0, + constr(0, constr(1, int(0)), True, int(0)), + constr(0, constr(1, int(0)), True) + ) + ], + False + ) + }) + + it("returns false if one of the innermost constrData has too many fields", () => { + runner( + [ + constr( + 0, + constr(0, constr(1, int(0), int(0)), True), + constr(0, constr(1, int(0)), True) + ) + ], + False + ) + }) + + it("returns false if one of the innermost constrData has an out-of-range tag", () => { + runner( + [ + constr( + 0, + constr(0, constr(3, int(0)), True), + constr(0, constr(1, int(0)), True) + ) + ], + False + ) + }) + + it("returns false if one of the innermost constrData has tag 2 and too many fields", () => { + runner( + [ + constr( + 0, + constr(0, constr(2, int(0)), True), + constr(0, constr(1, int(0)), True) + ) + ], + False + ) + }) + + it("returns false if one of the innermost constrData has tag 0 and too many fields", () => { + runner( + [ + constr( + 0, + constr(0, constr(1, int(0)), True), + constr(0, constr(0, int(0)), True) + ) + ], + False + ) + }) + + it("returns false for iData", () => { + runner([int(0)], False) + }) + + it("returns false for bData", () => { + runner([bytes([])], False) + }) + + it("returns false for mapData", () => { + runner([map([])], False) + }) + + it("returns false for listData", () => { + runner([list()], False) + }) + }) +}) diff --git a/test/TxId.test.js b/test/TxId.test.js new file mode 100644 index 00000000..a1c2d81a --- /dev/null +++ b/test/TxId.test.js @@ -0,0 +1,56 @@ +import { describe, it } from "node:test" +import { + False, + True, + bytes, + compileForRun, + constr, + int, + list, + map +} from "./utils.js" + +describe("TxId", () => { + describe("TxId::is_valid_data", () => { + const runner = compileForRun(`testing txid_is_valid_data + func main(d: Data) -> Bool { + TxId::is_valid_data(d) + }`) + + it("returns true for constrData with tag 0 and a 32 byte txid field", () => { + runner([constr(0, bytes(new Array(32).fill(0)))], True) + }) + + it("returns false for constrData with tag 1 and a 32 byte txid field", () => { + runner([constr(1, bytes(new Array(32).fill(0)))], False) + }) + + it("returns false for constrData with tag -1 and a 32 byte txid field", () => { + runner([constr(-1, bytes(new Array(32).fill(0)))], False) + }) + + it("returns false for constrData with tag 0 and a 31 byte txid field", () => { + runner([constr(0, bytes(new Array(31).fill(0)))], False) + }) + + it("returns false for constrData with tag 0 and a 33 byte txid field", () => { + runner([constr(0, bytes(new Array(33).fill(0)))], False) + }) + + it("returns false for iData", () => { + runner([int(0)], False) + }) + + it("returns false for bData", () => { + runner([bytes([])], False) + }) + + it("returns false for mapData", () => { + runner([map([])], False) + }) + + it("returns false for listData", () => { + runner([list()], False) + }) + }) +}) diff --git a/test/TxOutputId.test.js b/test/TxOutputId.test.js new file mode 100644 index 00000000..ae75eb72 --- /dev/null +++ b/test/TxOutputId.test.js @@ -0,0 +1,82 @@ +import { describe, it } from "node:test" +import { + False, + True, + bytes, + compileForRun, + constr, + int, + list, + map +} from "./utils.js" + +describe("TxOutputId", () => { + describe("TxOutputId::is_valid_data", () => { + const runner = compileForRun(`testing txoutputid_is_valida_data + func main(d: Data) -> Bool { + TxOutputId::is_valid_data(d) + }`) + + it("returns true for constrData with tag 0 and two fields: one txid and one iData", () => { + runner( + [constr(0, constr(0, bytes(new Array(32).fill(0))), int(0))], + True + ) + }) + + it("returns false for constrData with tag 1 and two fields: one txid and one iData", () => { + runner( + [constr(1, constr(0, bytes(new Array(32).fill(0))), int(0))], + False + ) + }) + + it("returns false for constrData with tag 0 and too many", () => { + runner( + [ + constr( + 0, + constr(0, bytes(new Array(32).fill(0))), + int(0), + int(0) + ) + ], + False + ) + }) + + it("returns false txid is 31 bytes long", () => { + runner( + [constr(0, constr(0, bytes(new Array(31).fill(0))), int(0))], + False + ) + }) + + it("returns false txid is 33 bytes long", () => { + runner( + [constr(0, constr(0, bytes(new Array(33).fill(0))), int(0))], + False + ) + }) + + it("returns false for constrData with tag 0 and no fields", () => { + runner([constr(0)], False) + }) + + it("returns false for iData", () => { + runner([int(0)], False) + }) + + it("returns false for bData", () => { + runner([bytes([])], False) + }) + + it("returns false for mapData", () => { + runner([map([])], False) + }) + + it("returns false for listData", () => { + runner([list()], False) + }) + }) +}) diff --git a/test/ValidatorHash.test.js b/test/ValidatorHash.test.js new file mode 100644 index 00000000..786d81d3 --- /dev/null +++ b/test/ValidatorHash.test.js @@ -0,0 +1,49 @@ +import { describe, it } from "node:test" +import { + False, + True, + bytes, + compileForRun, + constr, + int, + list, + map +} from "./utils.js" + +describe("ValidatorHash", () => { + describe("ValidatorHash::is_valid_data", () => { + const runner = compileForRun(` + testing validatorhash_is_valid_data + func main(a: Data) -> Bool { + ValidatorHash::is_valid_data(a) + }`) + + it("returns false for empty bData", () => { + runner([bytes([])], False) + }) + + it("returns false for #ffff", () => { + runner([bytes([255, 255])], False) + }) + + it("returns true for bData with 28 bytes", () => { + runner([bytes(new Array(28).fill(255))], True) + }) + + it("returns false for iData", () => { + runner([int(0)], False) + }) + + it("returns false for constrData", () => { + runner([constr(0)], False) + }) + + it("returns false for mapData", () => { + runner([map([])], False) + }) + + it("returns false for listData", () => { + runner([list()], False) + }) + }) +}) diff --git a/test/value.test.js b/test/Value.test.js similarity index 85% rename from test/value.test.js rename to test/Value.test.js index 72cc562f..dc207f10 100644 --- a/test/value.test.js +++ b/test/Value.test.js @@ -5,7 +5,9 @@ import { assetclass, bytes, compileForRun, + constr, int, + list, map } from "./utils.js" @@ -954,4 +956,193 @@ describe("Value", () => { ) }) }) + + describe("Value::is_valid_data", () => { + const runner = compileForRun(`testing value_is_valid_data + func main(d: Data) -> Bool { + Value::is_valid_data(d) + }`) + + it("returns true for empty mapData", () => { + runner([map([])], True) + }) + + it("returns true for mapData with a nested single token", () => { + runner( + [ + map([ + [ + bytes(new Array(28).fill(0)), + map([[bytes([]), int(0)]]) + ] + ]) + ], + True + ) + }) + + it("returns false if mph key has 29 bytes", () => { + runner( + [ + map([ + [ + bytes(new Array(29).fill(0)), + map([[bytes([]), int(0)]]) + ] + ]) + ], + False + ) + }) + + it("returns false if mph key has 27 bytes", () => { + runner( + [ + map([ + [ + bytes(new Array(27).fill(0)), + map([[bytes([]), int(0)]]) + ] + ]) + ], + False + ) + }) + + it("returns false if tokenName key isn't a bytearray", () => { + runner( + [ + map([ + [bytes(new Array(28).fill(0)), map([[list(), int(0)]])] + ]) + ], + False + ) + }) + + it("returns false if quantity value isn't iData", () => { + runner( + [ + map([ + [ + bytes(new Array(28).fill(0)), + map([[bytes([]), bytes([])]]) + ] + ]) + ], + False + ) + }) + + it("returns false if token name has 33 bytes", () => { + runner( + [ + map([ + [ + bytes(new Array(28).fill(0)), + map([[bytes(new Array(33).fill(0)), int(0)]]) + ] + ]) + ], + False + ) + }) + + it("returns false if second inner map is empty", () => { + runner( + [ + map([ + [ + bytes(new Array(28).fill(0)), + map([[bytes([]), int(0)]]) + ], + [bytes([]), map([])] + ]) + ], + False + ) + }) + + it("returns true if second inner map contains another token", () => { + runner( + [ + map([ + [ + bytes(new Array(28).fill(0)), + map([[bytes([]), int(0)]]) + ], + [bytes([]), map([[bytes([]), int(100)]])] + ]) + ], + True + ) + }) + + it("returns false if second inner map mph has 1 byte", () => { + runner( + [ + map([ + [ + bytes(new Array(28).fill(0)), + map([[bytes([]), int(0)]]) + ], + [bytes([1]), map([[bytes([]), int(100)]])] + ]) + ], + False + ) + }) + + it("returns true if second inner map mph has 28 bytes", () => { + runner( + [ + map([ + [ + bytes(new Array(28).fill(0)), + map([[bytes([]), int(0)]]) + ], + [ + bytes(new Array(28).fill(0)), + map([[bytes([]), int(100)]]) + ] + ]) + ], + True + ) + }) + + it("returns false if second token quantity is not iData", () => { + runner( + [ + map([ + [ + bytes(new Array(28).fill(0)), + map([[bytes([]), int(0)]]) + ], + [ + bytes(new Array(28).fill(0)), + map([[bytes([]), list()]]) + ] + ]) + ], + False + ) + }) + + it("returns false for iData", () => { + runner([int(0)], False) + }) + + it("returns false for bData", () => { + runner([bytes([])], False) + }) + + it("returns false for listData", () => { + runner([list()], False) + }) + + it("returns false for constrData", () => { + runner([constr(0)], False) + }) + }) })