diff --git a/src/chia_dialect.rs b/src/chia_dialect.rs index 1e48cb24..3bc5b290 100644 --- a/src/chia_dialect.rs +++ b/src/chia_dialect.rs @@ -68,15 +68,13 @@ impl Dialect for ChiaDialect { o: NodePtr, argument_list: NodePtr, max_cost: Cost, - _extension: OperatorSet, + extension: OperatorSet, ) -> Response { let flags = self.flags | match extension { OperatorSet::Default => 0, - OperatorSet::BLS => ENABLE_BLS_OPS_OUTSIDE_GUARD, - OperatorSet::Keccak => { - ENABLE_KECCAK_OPS_OUTSIDE_GUARD | ENABLE_BLS_OPS_OUTSIDE_GUARD - } + OperatorSet::BLS => 0, + OperatorSet::Keccak => ENABLE_KECCAK_OPS_OUTSIDE_GUARD, }; let op_len = allocator.atom_len(o); diff --git a/src/dialect.rs b/src/dialect.rs index 7e662c54..3f024913 100644 --- a/src/dialect.rs +++ b/src/dialect.rs @@ -6,8 +6,8 @@ use crate::reduction::Response; #[derive(Clone, Copy, Eq, PartialEq)] pub enum OperatorSet { Default, - BLS, // BLS and coinid operators - Keccak, // keccak256 operator (as well as BLS) + BLS, + Keccak, // keccak256 operator } pub trait Dialect { diff --git a/src/run_program.rs b/src/run_program.rs index fd5e0f78..c0c03f8e 100644 --- a/src/run_program.rs +++ b/src/run_program.rs @@ -549,14 +549,11 @@ pub fn run_program_with_counters<'a, D: Dialect>( mod tests { use super::*; - use crate::chia_dialect::NO_UNKNOWN_OPS; + use crate::chia_dialect::{ENABLE_KECCAK, ENABLE_KECCAK_OPS_OUTSIDE_GUARD, NO_UNKNOWN_OPS}; use crate::test_ops::parse_exp; use rstest::rstest; - #[cfg(test)] - use crate::chia_dialect::{ENABLE_KECCAK, ENABLE_KECCAK_OPS_OUTSIDE_GUARD, NO_UNKNOWN_OPS}; - struct RunProgramTest<'a> { prg: &'a str, args: &'a str, @@ -1168,9 +1165,9 @@ mod tests { err: "softfork specified cost mismatch", }, - // without the flag to enable the BLS extensions, it's an unknown extension + // without the flag to enable the keccak extensions, it's an unknown extension RunProgramTest { - prg: "(softfork (q . 161) (q . 1) (q . (q . 42)) (q . ()))", + prg: "(softfork (q . 161) (q . 2) (q . (q . 42)) (q . ()))", args: "()", flags: NO_UNKNOWN_OPS, result: None, @@ -1178,6 +1175,57 @@ mod tests { err: "unknown softfork extension", }, + // coinid is also available under softfork extension 1 + RunProgramTest { + prg: "(softfork (q . 1432) (q . 1) (q a (i (= (coinid (q . 0x1234500000000000000000000000000000000000000000000000000000000000) (q . 0x6789abcdef000000000000000000000000000000000000000000000000000000) (q . 123456789)) (q . 0x69bfe81b052bfc6bd7f3fb9167fec61793175b897c16a35827f947d5cc98e4bc)) (q . 0) (q x)) (q . ())) (q . ()))", + args: "()", + flags: ENABLE_KECCAK, + result: Some("()"), + cost: 1513, + err: "", + }, + + // keccak256 is available when the softfork has activated + RunProgramTest { + prg: "(softfork (q . 1134) (q . 1) (q a (i (= (keccak256 (q . \"foobar\")) (q . 0x38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e)) (q . 0) (q x)) (q . ())) (q . ()))", + args: "()", + flags: ENABLE_KECCAK, + result: Some("()"), + cost: 1215, + err: "", + }, + // make sure keccak is actually executed, by comparing with the wrong output + RunProgramTest { + prg: "(softfork (q . 1134) (q . 1) (q a (i (= (keccak256 (q . \"foobar\")) (q . 0x58d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e)) (q . 0) (q x)) (q . ())) (q . ()))", + args: "()", + flags: ENABLE_KECCAK, + result: None, + cost: 1215, + err: "clvm raise", + }, + // keccak is ignored when the softfork has not activated + RunProgramTest { + prg: "(softfork (q . 1134) (q . 1) (q a (i (= (keccak256 (q . \"foobar\")) (q . 0x58d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e)) (q . 0) (q x)) (q . ())) (q . ()))", + args: "()", + flags: 0, + result: Some("()"), + cost: 1215, + err: "", + }, + + // === HARD FORK === + // new operators *outside* the softfork guard + + // keccak256 is available outside the guard with the appropriate flag + RunProgramTest { + prg: "(a (i (= (keccak256 (q . \"foobar\")) (q . 0x38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e)) (q . 0) (q x)) (q . ()))", + args: "()", + flags: ENABLE_KECCAK | ENABLE_KECCAK_OPS_OUTSIDE_GUARD, + result: Some("()"), + cost: 994, + err: "", + }, + // coinid extension // make sure we can execute the coinid operator under softfork 0 // this program raises an exception if the computed coin ID matches the @@ -1302,140 +1350,97 @@ mod tests { run_test_case(t); } } -} -#[cfg(test)] -fn check(res: (NodePtr, &str)) -> NodePtr { - assert_eq!(res.1, ""); - res.0 -} - -#[cfg(test)] -fn run_test_case(t: &RunProgramTest) { - use crate::chia_dialect::ChiaDialect; - use crate::test_ops::node_eq; - let mut allocator = Allocator::new(); - - let program = check(parse_exp(&mut allocator, t.prg)); - let args = check(parse_exp(&mut allocator, t.args)); - let expected_result = &t.result.map(|v| check(parse_exp(&mut allocator, v))); - - let dialect = ChiaDialect::new(t.flags); - println!("prg: {}", t.prg); - match run_program(&mut allocator, &dialect, program, args, t.cost) { - Ok(Reduction(cost, prg_result)) => { - assert!(node_eq(&allocator, prg_result, expected_result.unwrap())); - assert_eq!(cost, t.cost); - - // now, run the same program again but with the cost limit 1 too low, to - // ensure it fails with the correct error - let expected_cost_exceeded = - run_program(&mut allocator, &dialect, program, args, t.cost - 1).unwrap_err(); - assert_eq!(expected_cost_exceeded.1, "cost exceeded"); - } - Err(err) => { - println!("FAILED: {}", err.1); - assert_eq!(err.1, t.err); - assert!(expected_result.is_none()); - } - } -} - -#[test] -fn test_run_program() { - for t in TEST_CASES { - run_test_case(t); - } -} - -#[cfg(test)] -use rstest::rstest; - -// the test cases for this test consists of: -// prg: the program to run inside the softfork guard -// cost: the expected cost of the program (the test adds the apply-operator) -// enabled: the softfork extension number that enables operator in prg -// hard_fork_flag: the flag that enables the program to be run outside the guard -// err: the expected error message, empty string means OK -// The test programs are carefully crafted such that they fail with "clvm raise" -// when run in consensus mode and the operators are unknown. e.g. (coinid ...) -// returns NIL in that case, which compares not equal to the coin ID, which -// raises the exception. -// This property is relied on for the non-mempool and fork-not-activated cases. -#[cfg(test)] -#[rstest] -// make sure we can execute the coinid operator under softfork 0 -// this program raises an exception if the computed coin ID matches the -// expected -#[case::coinid("(i (= (coinid (q . 0x1234500000000000000000000000000000000000000000000000000000000000) (q . 0x6789abcdef000000000000000000000000000000000000000000000000000000) (q . 123456789)) (q . 0x69bfe81b052bfc6bd7f3fb9167fec61793175b897c16a35827f947d5cc98e4bd)) (q . 0) (q x))", - (1432, 0, ENABLE_BLS_OPS_OUTSIDE_GUARD), - "clvm raise")] -// also test the opposite. This program is the same as above but it raises -// if the coin ID is a mismatch -#[case::coinid("(i (= (coinid (q . 0x1234500000000000000000000000000000000000000000000000000000000000) (q . 0x6789abcdef000000000000000000000000000000000000000000000000000000) (q . 123456789)) (q . 0x69bfe81b052bfc6bd7f3fb9167fec61793175b897c16a35827f947d5cc98e4bc)) (q . 0) (q x))", - (1432, 0, ENABLE_BLS_OPS_OUTSIDE_GUARD), - "")] -// modpow -#[case::modpow( - "(i (= (modpow (q . 12345) (q . 6789) (q . 44444444444)) (q . 13456191581)) (q . 0) (q x))", - (18241, 0, 0), - "" -)] -#[case::modpow( - "(i (= (modpow (q . 12345) (q . 6789) (q . 44444444444)) (q . 13456191582)) (q . 0) (q x))", - (18241, 0, 0), - "clvm raise" -)] -// mod -#[case::modulus( - "(i (= (% (q . 80001) (q . 73)) (q . 66)) (q . 0) (q x))", - (1564, 0, 0), - "" -)] -#[case::modulus( - "(i (= (% (q . 80001) (q . 73)) (q . 67)) (q . 0) (q x))", - (1564, 0, 0), - "clvm raise" -)] -// g1_multiply -#[case::g1_mul("(i (= (g1_multiply (q . 0x97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb) (q . 2)) (q . 0xa572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e)) (q . 0) (q x))", - (706634, 0, 0), - "")] -#[case::g1_mul( - "(i (= (g1_multiply (q . 0x97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb) (q . 2)) (q . 0xa572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4f)) (q . 0) (q x))", - (706634, 0, 0), - "clvm raise")] -#[case::g1_neg("(i (= (g1_negate (q . 0xb7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb)) (q . 0xb7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb)) (q . 0) (q x))", (706634, 0, 0), "clvm raise")] -#[case::g1_neg("(i (= (g1_negate (q . 0xb2f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb)) (q . 0xb7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb)) (q . 0) (q x))", - (706634, 0, 0), - "atom is not a valid G1 point")] -#[case::g2_add("(i (= (g2_add (q . 0x93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8) (q . 0x93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8)) (q . 0xaa4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c335771638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053)) (q . 0) (q x))", - (3981700, 0, 0), - "")] -#[case::g2_add("(i (= (g2_add (q . 0x93e12b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8) (q . 0x93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8)) (q . 0xaa4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c335771638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053)) (q . 0) (q x))", - (3981700, 0, 0), - "atom is not a G2 point")] -fn test_softfork( - #[case] prg: &'static str, - #[case] fields: (u64, u8, u32), // cost, enabled, hard_fork_flag - #[case] err: &'static str, - #[values(0)] flags: u32, - #[values(false, true)] mempool: bool, - #[values(0, 1)] test_ext: u8, -) { - let (cost, enabled, hard_fork_flag) = fields; - let softfork_prg = - format!("(softfork (q . {cost}) (q . {test_ext}) (q . (a {prg} (q . 0))) (q . 0))"); + // the test cases for this test consists of: + // prg: the program to run inside the softfork guard + // cost: the expected cost of the program (the test adds the apply-operator) + // enabled: the softfork extension number that enables operator in prg + // hard_fork_flag: the flag that enables the program to be run outside the guard + // err: the expected error message, empty string means OK + // The test programs are carefully crafted such that they fail with "clvm raise" + // when run in consensus mode and the operators are unknown. e.g. (coinid ...) + // returns NIL in that case, which compares not equal to the coin ID, which + // raises the exception. + // This property is relied on for the non-mempool and fork-not-activated cases. + #[rstest] + // make sure we can execute the coinid operator under softfork 0 + // this program raises an exception if the computed coin ID matches the + // expected + #[case::coinid( + "(i (= (coinid (q . 0x1234500000000000000000000000000000000000000000000000000000000000) (q . 0x6789abcdef000000000000000000000000000000000000000000000000000000) (q . 123456789)) (q . 0x69bfe81b052bfc6bd7f3fb9167fec61793175b897c16a35827f947d5cc98e4bd)) (q . 0) (q x))", + (1432, 0, 0), + "clvm raise") + ] + // also test the opposite. This program is the same as above but it raises + // if the coin ID is a mismatch + #[case::coinid( + "(i (= (coinid (q . 0x1234500000000000000000000000000000000000000000000000000000000000) (q . 0x6789abcdef000000000000000000000000000000000000000000000000000000) (q . 123456789)) (q . 0x69bfe81b052bfc6bd7f3fb9167fec61793175b897c16a35827f947d5cc98e4bc)) (q . 0) (q x))", + (1432, 0, 0), + "" + )] + // modpow + #[case::modpow( + "(i (= (modpow (q . 12345) (q . 6789) (q . 44444444444)) (q . 13456191581)) (q . 0) (q x))", + (18241, 0, 0), + "" + )] + #[case::modpow( + "(i (= (modpow (q . 12345) (q . 6789) (q . 44444444444)) (q . 13456191582)) (q . 0) (q x))", + (18241, 0, 0), + "clvm raise" + )] + // mod + #[case::modulus( + "(i (= (% (q . 80001) (q . 73)) (q . 66)) (q . 0) (q x))", + (1564, 0, 0), + "" + )] + #[case::modulus( + "(i (= (% (q . 80001) (q . 73)) (q . 67)) (q . 0) (q x))", + (1564, 0, 0), + "clvm raise" + )] + // g1_multiply + #[case::g1_mul( + "(i (= (g1_multiply (q . 0x97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb) (q . 2)) (q . 0xa572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e)) (q . 0) (q x))", + (706634, 0, 0), + "" + )] + #[case::g1_mul( + "(i (= (g1_multiply (q . 0x97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb) (q . 2)) (q . 0xa572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4f)) (q . 0) (q x))", + (706634, 0, 0), + "clvm raise" + )] + #[case::g1_neg( + "(i (= (g1_negate (q . 0xb7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb)) (q . 0xb7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb)) (q . 0) (q x))", + (706634, 0, 0), + "clvm raise" + )] + #[case::g1_neg( + "(i (= (g1_negate (q . 0xb2f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb)) (q . 0xb7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb)) (q . 0) (q x))", + (706634, 0, 0), + "atom is not a valid G1 point" + )] + #[case::g2_add( + "(i (= (g2_add (q . 0x93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8) (q . 0x93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8)) (q . 0xaa4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c335771638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053)) (q . 0) (q x))", + (3981700, 0, 0), + "" + )] + #[case::g2_add( + "(i (= (g2_add (q . 0x93e12b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8) (q . 0x93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8)) (q . 0xaa4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c335771638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053)) (q . 0) (q x))", + (3981700, 0, 0), + "atom is not a G2 point" + )] #[case::keccak( - "(i (= (keccak256 (q . \"foobar\")) (q . 0x38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e)) (q . 0) (q x))", - (1134, 1, ENABLE_KECCAK_OPS_OUTSIDE_GUARD), - "" -)] + "(i (= (keccak256 (q . \"foobar\")) (q . 0x38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e)) (q . 0) (q x))", + (1134, 1, ENABLE_KECCAK_OPS_OUTSIDE_GUARD), + "" + )] #[case::keccak( - "(i (= (keccak256 (q . \"foobar\")) (q . 0x38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873f)) (q . 0) (q x))", - (1134, 1, ENABLE_KECCAK_OPS_OUTSIDE_GUARD), - "clvm raise" -)] + "(i (= (keccak256 (q . \"foobar\")) (q . 0x38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873f)) (q . 0) (q x))", + (1134, 1, ENABLE_KECCAK_OPS_OUTSIDE_GUARD), + "clvm raise" + )] fn test_softfork( #[case] prg: &'static str, #[case] fields: (u64, u8, u32), // cost, enabled, hard_fork_flag @@ -1522,9 +1527,19 @@ fn test_softfork( prg: outside_guard_prg.as_str(), args: "()", flags, - result: if err.is_empty() { Some("()") } else { None }, + result: if err.is_empty() && hard_fork_flag == 0 { + Some("()") + } else { + None + }, cost: cost - 140, - err, + err: if hard_fork_flag == 0 { + err + } else if mempool { + "unimplemented operator" + } else { + "clvm raise" + }, }; run_test_case(&t);