chia_consensus/
opcodes.rs1use clvmr::allocator::{Allocator, NodePtr, SExp};
2use clvmr::cost::Cost;
3
4pub type ConditionOpcode = u16;
5
6pub const AGG_SIG_PARENT: ConditionOpcode = 43;
8pub const AGG_SIG_PUZZLE: ConditionOpcode = 44;
9pub const AGG_SIG_AMOUNT: ConditionOpcode = 45;
10pub const AGG_SIG_PUZZLE_AMOUNT: ConditionOpcode = 46;
11pub const AGG_SIG_PARENT_AMOUNT: ConditionOpcode = 47;
12pub const AGG_SIG_PARENT_PUZZLE: ConditionOpcode = 48;
13pub const AGG_SIG_UNSAFE: ConditionOpcode = 49;
14pub const AGG_SIG_ME: ConditionOpcode = 50;
15
16pub const CREATE_COIN: ConditionOpcode = 51;
19pub const RESERVE_FEE: ConditionOpcode = 52;
20
21pub const CREATE_COIN_ANNOUNCEMENT: ConditionOpcode = 60;
23pub const ASSERT_COIN_ANNOUNCEMENT: ConditionOpcode = 61;
24pub const CREATE_PUZZLE_ANNOUNCEMENT: ConditionOpcode = 62;
25pub const ASSERT_PUZZLE_ANNOUNCEMENT: ConditionOpcode = 63;
26pub const ASSERT_CONCURRENT_SPEND: ConditionOpcode = 64;
27pub const ASSERT_CONCURRENT_PUZZLE: ConditionOpcode = 65;
28
29pub const SEND_MESSAGE: ConditionOpcode = 66;
30pub const RECEIVE_MESSAGE: ConditionOpcode = 67;
31
32pub const ASSERT_MY_COIN_ID: ConditionOpcode = 70;
34pub const ASSERT_MY_PARENT_ID: ConditionOpcode = 71;
35pub const ASSERT_MY_PUZZLEHASH: ConditionOpcode = 72;
36pub const ASSERT_MY_AMOUNT: ConditionOpcode = 73;
37pub const ASSERT_MY_BIRTH_SECONDS: ConditionOpcode = 74;
38pub const ASSERT_MY_BIRTH_HEIGHT: ConditionOpcode = 75;
39pub const ASSERT_EPHEMERAL: ConditionOpcode = 76;
40
41pub const ASSERT_SECONDS_RELATIVE: ConditionOpcode = 80;
44pub const ASSERT_SECONDS_ABSOLUTE: ConditionOpcode = 81;
45
46pub const ASSERT_HEIGHT_RELATIVE: ConditionOpcode = 82;
48pub const ASSERT_HEIGHT_ABSOLUTE: ConditionOpcode = 83;
49
50pub const ASSERT_BEFORE_SECONDS_RELATIVE: ConditionOpcode = 84;
52pub const ASSERT_BEFORE_SECONDS_ABSOLUTE: ConditionOpcode = 85;
53
54pub const ASSERT_BEFORE_HEIGHT_RELATIVE: ConditionOpcode = 86;
56pub const ASSERT_BEFORE_HEIGHT_ABSOLUTE: ConditionOpcode = 87;
57
58pub const REMARK: ConditionOpcode = 1;
60
61pub const SOFTFORK: ConditionOpcode = 90;
64
65pub const CREATE_COIN_COST: Cost = 1_800_000;
66pub const AGG_SIG_COST: Cost = 1_200_000;
67
68pub const GENERIC_CONDITION_COST: Cost = 500;
69pub const FREE_CONDITIONS: usize = 100;
70
71const fn calculate_cost_table() -> [u64; 256] {
76 let (a, b) = (17, 16);
77 let mut s = [0; 256];
78 let (mut num, mut den) = (100_u64, 1_u64);
79 let max = 1 << 59;
80 let mut idx = 0;
81 while idx < 256 {
82 let v = num / den;
83 let mut power_of_ten = 1000;
84 while power_of_ten < v {
85 power_of_ten *= 10;
86 }
87 power_of_ten /= 1000;
88 s[idx] = (v / power_of_ten) * power_of_ten;
89 num *= a;
90 den *= b;
91 while num > max {
92 num >>= 5;
93 den >>= 5;
94 }
95 idx += 1;
96 }
97 s
98}
99
100const COSTS: [Cost; 256] = calculate_cost_table();
101
102pub fn compute_unknown_condition_cost(op: ConditionOpcode) -> Cost {
103 if op < 256 {
104 0
105 } else {
106 COSTS[(op & 0xff) as usize]
107 }
108}
109
110pub fn parse_opcode(
111 a: &Allocator,
112 op: NodePtr,
113 _flags: crate::flags::ConsensusFlags,
114) -> Option<ConditionOpcode> {
115 let buf = match a.sexp(op) {
116 SExp::Atom => a.atom(op),
117 SExp::Pair(..) => return None,
118 };
119 let buf = buf.as_ref();
120 if buf.len() == 2 {
121 if buf[0] == 0 {
122 None
124 } else {
125 Some(ConditionOpcode::from_be_bytes(buf.try_into().unwrap()))
127 }
128 } else if buf.len() == 1 {
129 let b0 = ConditionOpcode::from(buf[0]);
130 match b0 {
131 AGG_SIG_UNSAFE
132 | AGG_SIG_ME
133 | CREATE_COIN
134 | RESERVE_FEE
135 | CREATE_COIN_ANNOUNCEMENT
136 | ASSERT_COIN_ANNOUNCEMENT
137 | CREATE_PUZZLE_ANNOUNCEMENT
138 | ASSERT_PUZZLE_ANNOUNCEMENT
139 | ASSERT_MY_COIN_ID
140 | ASSERT_MY_PARENT_ID
141 | ASSERT_MY_PUZZLEHASH
142 | ASSERT_MY_AMOUNT
143 | ASSERT_SECONDS_RELATIVE
144 | ASSERT_SECONDS_ABSOLUTE
145 | ASSERT_HEIGHT_RELATIVE
146 | ASSERT_HEIGHT_ABSOLUTE
147 | REMARK
148 | ASSERT_BEFORE_SECONDS_RELATIVE
149 | ASSERT_BEFORE_SECONDS_ABSOLUTE
150 | ASSERT_BEFORE_HEIGHT_RELATIVE
151 | ASSERT_BEFORE_HEIGHT_ABSOLUTE
152 | ASSERT_CONCURRENT_SPEND
153 | ASSERT_CONCURRENT_PUZZLE
154 | ASSERT_MY_BIRTH_SECONDS
155 | ASSERT_MY_BIRTH_HEIGHT
156 | ASSERT_EPHEMERAL
157 | SOFTFORK
158 | AGG_SIG_PARENT
159 | AGG_SIG_PUZZLE
160 | AGG_SIG_AMOUNT
161 | AGG_SIG_PUZZLE_AMOUNT
162 | AGG_SIG_PARENT_AMOUNT
163 | AGG_SIG_PARENT_PUZZLE
164 | SEND_MESSAGE
165 | RECEIVE_MESSAGE => Some(b0),
166 _ => None,
167 }
168 } else {
169 None
170 }
171}
172
173#[cfg(test)]
174fn opcode_tester(
175 a: &mut Allocator,
176 val: &[u8],
177 flags: crate::flags::ConsensusFlags,
178) -> Option<ConditionOpcode> {
179 let v = a.new_atom(val).unwrap();
180 parse_opcode(a, v, flags)
181}
182
183#[cfg(test)]
184use rstest::rstest;
185
186#[cfg(test)]
187#[rstest]
188#[case(&[ASSERT_HEIGHT_ABSOLUTE as u8, 0, 0], None)]
190#[case(&[0, ASSERT_HEIGHT_ABSOLUTE as u8], None)]
191#[case(&[0], None)]
192#[case(&[AGG_SIG_UNSAFE as u8], Some(AGG_SIG_UNSAFE))]
194#[case(&[AGG_SIG_ME as u8], Some(AGG_SIG_ME))]
195#[case(&[CREATE_COIN as u8], Some(CREATE_COIN))]
196#[case(&[RESERVE_FEE as u8], Some(RESERVE_FEE))]
197#[case(&[CREATE_COIN_ANNOUNCEMENT as u8], Some(CREATE_COIN_ANNOUNCEMENT))]
198#[case(&[ASSERT_COIN_ANNOUNCEMENT as u8], Some(ASSERT_COIN_ANNOUNCEMENT))]
199#[case(&[CREATE_PUZZLE_ANNOUNCEMENT as u8], Some(CREATE_PUZZLE_ANNOUNCEMENT))]
200#[case(&[ASSERT_PUZZLE_ANNOUNCEMENT as u8], Some(ASSERT_PUZZLE_ANNOUNCEMENT))]
201#[case(&[ASSERT_CONCURRENT_SPEND as u8], Some(ASSERT_CONCURRENT_SPEND))]
202#[case(&[ASSERT_CONCURRENT_PUZZLE as u8], Some(ASSERT_CONCURRENT_PUZZLE))]
203#[case(&[ASSERT_MY_COIN_ID as u8], Some(ASSERT_MY_COIN_ID))]
204#[case(&[ASSERT_MY_PARENT_ID as u8], Some(ASSERT_MY_PARENT_ID))]
205#[case(&[ASSERT_MY_PUZZLEHASH as u8], Some(ASSERT_MY_PUZZLEHASH))]
206#[case(&[ASSERT_MY_AMOUNT as u8], Some(ASSERT_MY_AMOUNT))]
207#[case(&[ASSERT_MY_BIRTH_SECONDS as u8], Some(ASSERT_MY_BIRTH_SECONDS))]
208#[case(&[ASSERT_MY_BIRTH_HEIGHT as u8], Some(ASSERT_MY_BIRTH_HEIGHT))]
209#[case(&[ASSERT_EPHEMERAL as u8], Some(ASSERT_EPHEMERAL))]
210#[case(&[ASSERT_SECONDS_RELATIVE as u8], Some(ASSERT_SECONDS_RELATIVE))]
211#[case(&[ASSERT_SECONDS_ABSOLUTE as u8], Some(ASSERT_SECONDS_ABSOLUTE))]
212#[case(&[ASSERT_HEIGHT_RELATIVE as u8], Some(ASSERT_HEIGHT_RELATIVE))]
213#[case(&[ASSERT_HEIGHT_ABSOLUTE as u8], Some(ASSERT_HEIGHT_ABSOLUTE))]
214#[case(&[ASSERT_BEFORE_SECONDS_RELATIVE as u8], Some(ASSERT_BEFORE_SECONDS_RELATIVE))]
215#[case(&[ASSERT_BEFORE_SECONDS_ABSOLUTE as u8], Some(ASSERT_BEFORE_SECONDS_ABSOLUTE))]
216#[case(&[ASSERT_BEFORE_HEIGHT_RELATIVE as u8], Some(ASSERT_BEFORE_HEIGHT_RELATIVE))]
217#[case(&[ASSERT_BEFORE_HEIGHT_ABSOLUTE as u8], Some(ASSERT_BEFORE_HEIGHT_ABSOLUTE))]
218#[case(&[REMARK as u8], Some(REMARK))]
219#[case(&[SOFTFORK as u8], Some(SOFTFORK))]
220#[case(&[AGG_SIG_PARENT as u8], Some(AGG_SIG_PARENT))]
221#[case(&[AGG_SIG_PUZZLE as u8], Some(AGG_SIG_PUZZLE))]
222#[case(&[AGG_SIG_AMOUNT as u8], Some(AGG_SIG_AMOUNT))]
223#[case(&[AGG_SIG_PUZZLE_AMOUNT as u8], Some(AGG_SIG_PUZZLE_AMOUNT))]
224#[case(&[AGG_SIG_PARENT_AMOUNT as u8], Some(AGG_SIG_PARENT_AMOUNT))]
225#[case(&[AGG_SIG_PARENT_PUZZLE as u8], Some(AGG_SIG_PARENT_PUZZLE))]
226#[case(&[SEND_MESSAGE as u8], Some(SEND_MESSAGE))]
227#[case(&[RECEIVE_MESSAGE as u8], Some(RECEIVE_MESSAGE))]
228fn test_parse_opcode(#[case] input: &[u8], #[case] expected: Option<ConditionOpcode>) {
229 let mut a = Allocator::new();
230 assert_eq!(
231 opcode_tester(&mut a, input, crate::flags::ConsensusFlags::empty()),
232 expected
233 );
234}
235
236#[test]
237fn test_parse_invalid_opcode() {
238 let mut a = Allocator::new();
240 let v1 = a.new_atom(&[0]).unwrap();
241 let v2 = a.new_atom(&[0]).unwrap();
242 let p = a.new_pair(v1, v2).unwrap();
243 assert_eq!(
244 parse_opcode(&a, p, crate::flags::ConsensusFlags::empty()),
245 None
246 );
247}