From 694e1918d8542eccc9ffd53171d41d7f7162ce50 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sun, 23 Jun 2024 22:40:16 -0400 Subject: [PATCH 1/2] Calculate coin id, curry tree hash --- crates/rue-compiler/src/error.rs | 2 +- std/stdlib.rue | 77 ++++++++++++++++++++++++++++---- test.clsp | 1 + 3 files changed, 70 insertions(+), 10 deletions(-) create mode 100644 test.clsp diff --git a/crates/rue-compiler/src/error.rs b/crates/rue-compiler/src/error.rs index f656d2c..14b75f0 100644 --- a/crates/rue-compiler/src/error.rs +++ b/crates/rue-compiler/src/error.rs @@ -191,7 +191,7 @@ pub enum ErrorKind { #[error("explicit return is not allowed in expressions")] ExplicitReturnInExpr, - #[error("blocks must either have a an expression value, return statement, or raise an error")] + #[error("block missing return value")] EmptyBlock, #[error("cannot check equality on non-atom type `{0}`")] diff --git a/std/stdlib.rue b/std/stdlib.rue index 95d6684..0b0f01c 100644 --- a/std/stdlib.rue +++ b/std/stdlib.rue @@ -112,6 +112,19 @@ export enum Condition { }, } +export fun concat(a: T[], b: T[]) -> T[] { + if a is (T, T[]) { + return [a.first, ...concat(a.rest, b)]; + } + if b is (T, T[]) { + return [b.first, ...concat(a, b.rest)]; + } + nil +} + +inline const ATOM_PREFIX: Bytes = 1 as Bytes; +inline const PAIR_PREFIX: Bytes = 2 as Bytes; + export fun tree_hash(value: Any) -> Bytes32 { if value is Bytes { tree_hash_atom(value) @@ -121,19 +134,65 @@ export fun tree_hash(value: Any) -> Bytes32 { } export inline fun tree_hash_atom(value: Bytes) -> Bytes32 { - sha256(1 as Bytes + value) + sha256(ATOM_PREFIX + value) } export inline fun tree_hash_pair(first: Bytes32, rest: Bytes32) -> Bytes32 { - sha256(2 as Bytes + first + rest) + sha256(PAIR_PREFIX + first + rest) } -export fun concat(a: T[], b: T[]) -> T[] { - if a is (T, T[]) { - return [a.first, ...concat(a.rest, b)]; - } - if b is (T, T[]) { - return [b.first, ...concat(a, b.rest)]; +inline const OP_Q: Bytes = 1 as Bytes; +inline const OP_A: Bytes = 2 as Bytes; +inline const OP_C: Bytes = 4 as Bytes; + +inline const OP_Q_TREE_HASH: Bytes32 = tree_hash_atom(OP_Q); +inline const OP_A_TREE_HASH: Bytes32 = tree_hash_atom(OP_A); +inline const OP_C_TREE_HASH: Bytes32 = tree_hash_atom(OP_C); +inline const ONE_TREE_HASH: Bytes32 = tree_hash_atom(1 as Bytes); + +const NIL_TREE_HASH: Bytes32 = tree_hash_atom(nil); +const APPLY_PREIMAGE_PREFIX: Bytes = PAIR_PREFIX + OP_A_TREE_HASH; +const CONS_PREIMAGE_PREFIX: Bytes = PAIR_PREFIX + OP_C_TREE_HASH; + +inline fun quote_hash(value: Bytes32) -> Bytes32 { + tree_hash_pair(OP_Q_TREE_HASH, value) +} + +inline fun two_item_list_hash(first: Bytes32, rest: Bytes32) -> Bytes32 { + tree_hash_pair(first, tree_hash_pair(rest, NIL_TREE_HASH)) +} + +inline fun apply_hash(mod_hash: Bytes32, environment_hash: Bytes32) -> Bytes32 { + sha256(APPLY_PREIMAGE_PREFIX + two_item_list_hash(quote_hash(mod_hash), environment_hash)) +} + +inline fun update_hash_with_parameter( + parameter_hash: Bytes32, + environment_hash: Bytes32 +) -> Bytes32 { + sha256(CONS_PREIMAGE_PREFIX + two_item_list_hash(quote_hash(parameter_hash), environment_hash)) +} + +fun curried_params_hash(parameters: Bytes32[]) -> Bytes32 { + if parameters is Nil { + return ONE_TREE_HASH; } - nil + update_hash_with_parameter(parameters.first, curried_params_hash(parameters.rest)) +} + +export inline fun curry_tree_hash( + mod_hash: Bytes32, + ...parameters: Bytes32[] +) -> Bytes32 { + apply_hash(mod_hash, curried_params_hash(parameters)) +} + +export fun calculate_coin_id( + parent_coin_id: Bytes, + puzzle_hash: Bytes, + amount: Int, +) -> Bytes32 { + assert parent_coin_id is Bytes32; + assert puzzle_hash is Bytes32; + sha256(parent_coin_id + puzzle_hash + amount as Bytes) } diff --git a/test.clsp b/test.clsp new file mode 100644 index 0000000..94226da --- /dev/null +++ b/test.clsp @@ -0,0 +1 @@ +2a From 19abddb2e00c591679873027d8a7661f2650eb70 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sun, 23 Jun 2024 22:50:09 -0400 Subject: [PATCH 2/2] Add puzzle examples --- .../rue-compiler/src/database/type_system.rs | 3 +++ examples/multisig.rue | 2 ++ examples/p2_conditions.rue | 9 +++++++ examples/p2_delegated_or_hidden.rue | 25 +++++++++++++++++++ tests.toml | 14 +++++++++++ 5 files changed, 53 insertions(+) create mode 100644 examples/p2_conditions.rue create mode 100644 examples/p2_delegated_or_hidden.rue diff --git a/crates/rue-compiler/src/database/type_system.rs b/crates/rue-compiler/src/database/type_system.rs index 09fbfc2..ab36c6d 100644 --- a/crates/rue-compiler/src/database/type_system.rs +++ b/crates/rue-compiler/src/database/type_system.rs @@ -309,6 +309,9 @@ impl Database { self.compare_type_visitor(*lhs, *rhs, ctx) } (_, Type::Optional(inner)) => self.compare_type_visitor(lhs, *inner, ctx), + (Type::Optional(inner), Type::Bytes) => { + Comparison::Castable & self.compare_type_visitor(*inner, rhs, ctx) + } // TODO: Unions would make this more generalized and useful. // I should add unions back. diff --git a/examples/multisig.rue b/examples/multisig.rue index e399721..9bba1dc 100644 --- a/examples/multisig.rue +++ b/examples/multisig.rue @@ -1,3 +1,5 @@ +// This puzzle has not been audited or tested, and is for example purposes only. + fun main( public_keys: PublicKey[], required: Int, diff --git a/examples/p2_conditions.rue b/examples/p2_conditions.rue new file mode 100644 index 0000000..f017e95 --- /dev/null +++ b/examples/p2_conditions.rue @@ -0,0 +1,9 @@ +// This puzzle has not been audited or tested, and is for example purposes only. + +fun main(public_key: PublicKey, conditions: Condition[]) -> Condition[] { + let agg_sig = Condition::AggSigMe { + public_key: public_key, + message: tree_hash(conditions), + }; + [agg_sig, ...conditions] +} diff --git a/examples/p2_delegated_or_hidden.rue b/examples/p2_delegated_or_hidden.rue new file mode 100644 index 0000000..25ee32d --- /dev/null +++ b/examples/p2_delegated_or_hidden.rue @@ -0,0 +1,25 @@ +// This puzzle has not been audited or tested, and is for example purposes only. + +fun main( + synthetic_pk: PublicKey, + original_pk: PublicKey?, + delegated_puzzle: fun(...Any) -> Condition[], + delegated_solution: Any +) -> Condition[] { + let conditions = delegated_puzzle(...delegated_solution); + let delegated_puzzle_hash = tree_hash(delegated_puzzle); + + if original_pk != nil { + let exponent = sha256(original_pk as Bytes + delegated_puzzle_hash); + let offset_pk = pubkey_for_exp(exponent); + assert synthetic_pk == original_pk + offset_pk; + return conditions; + } + + let agg_sig_me = Condition::AggSigMe { + public_key: synthetic_pk, + message: delegated_puzzle_hash, + }; + + [agg_sig_me, ...conditions] +} diff --git a/tests.toml b/tests.toml index a924ade..c2bb852 100644 --- a/tests.toml +++ b/tests.toml @@ -202,3 +202,17 @@ compiler_errors = [ "unused inline function `some` at 6:12", "unused let binding `inline_fun` at 2:9", ] + +[p2_conditions] +bytes = 167 +cost = 19407 +input = "(0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ((51 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8 1000)))" +output = "((g1_multiply 0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0xc239bbcfc69fb5abacdd8dc174c6b33170c6d902dec3bc1c87662020cf044313) (g1_negate 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8 1000))" +hash = "abc43bf1142122da14e73971da004668ce7b8785ca2b6ac41427b8ef3193f957" + +[p2_delegated_or_hidden] +bytes = 305 +cost = 24974 +input = "(0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 () (q . ((51 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8 1000))) 1)" +output = "((g1_multiply 0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0x3d7e1145b5969c12f4889f4f1f66bde0e4d3ba54b91784cf604294d162b44b69) (g1_negate 0x291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8 1000))" +hash = "670f06e052dedc68788dd17acb7d100203a1533d11f9c679d0f23bd3d555f122"