• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tari-project / tari / 24388110195

14 Apr 2026 08:06AM UTC coverage: 61.038% (-0.1%) from 61.164%
24388110195

push

github

SWvheerden
chore: new release v5.3.0-pre.6

70558 of 115597 relevant lines covered (61.04%)

224856.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.2
/infrastructure/tari_script/src/script.rs
1
// Copyright 2020. The Tari Project
2
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
3
// following conditions are met:
4
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
5
// disclaimer.
6
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
7
// following disclaimer in the documentation and/or other materials provided with the distribution.
8
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
9
// products derived from this software without specific prior written permission.
10
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
11
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
12
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
13
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
14
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
15
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
16
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17

18
// pending updates to Dalek/Digest
19
use std::{cmp::Ordering, collections::HashSet, fmt, io, ops::Deref};
20

21
use blake2::Blake2b;
22
use borsh::{BorshDeserialize, BorshSerialize};
23
use digest::{Digest, consts::U32};
24
use integer_encoding::{VarIntReader, VarIntWriter};
25
use sha2::Sha256;
26
use sha3::Sha3_256;
27
use tari_crypto::{
28
    compressed_commitment::CompressedCommitment,
29
    compressed_key::CompressedKey,
30
    ristretto::{RistrettoPublicKey, RistrettoSecretKey},
31
};
32
use tari_max_size::MaxSizeVec;
33
use tari_utilities::{
34
    ByteArray,
35
    hex::{Hex, HexError, from_hex, to_hex},
36
};
37

38
use crate::{
39
    CompressedCheckSigSchnorrSignature,
40
    ExecutionStack,
41
    HashValue,
42
    Opcode,
43
    ScriptContext,
44
    ScriptError,
45
    StackItem,
46
    op_codes::Message,
47
    slice_to_hash,
48
};
49

50
#[macro_export]
51
macro_rules! script {
52
    ($($opcode:ident$(($($var:expr),+))?) +) => {{
53
        use $crate::TariScript;
54
        use $crate::Opcode;
55
        let script = vec![$(Opcode::$opcode $(($($var),+))?),+];
56
        TariScript::new(script)
57
    }}
58
}
59

60
const MAX_MULTISIG_LIMIT: u8 = 32;
61
const MAX_SCRIPT_BYTES: usize = 4096;
62

63
/// The sized vector of opcodes that make up a script
64
pub type ScriptOpcodes = MaxSizeVec<Opcode, 128>;
65

66
#[derive(Clone, Debug, PartialEq, Eq)]
67
pub struct TariScript {
68
    script: ScriptOpcodes,
69
}
70

71
impl BorshSerialize for TariScript {
72
    fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
74,149✔
73
        let bytes = self.to_bytes();
74,149✔
74
        writer.write_varint(bytes.len())?;
74,149✔
75
        for b in &bytes {
5,966,023✔
76
            b.serialize(writer)?;
5,966,023✔
77
        }
78
        Ok(())
74,149✔
79
    }
74,149✔
80
}
81

82
impl BorshDeserialize for TariScript {
83
    fn deserialize_reader<R>(reader: &mut R) -> Result<Self, io::Error>
4✔
84
    where R: io::Read {
4✔
85
        let len = reader.read_varint()?;
4✔
86
        if len > MAX_SCRIPT_BYTES {
4✔
87
            return Err(io::Error::new(
1✔
88
                io::ErrorKind::InvalidInput,
1✔
89
                "Larger than max script bytes".to_string(),
1✔
90
            ));
1✔
91
        }
3✔
92
        let mut data = Vec::with_capacity(len);
3✔
93
        for _ in 0..len {
3✔
94
            data.push(u8::deserialize_reader(reader)?);
177✔
95
        }
96
        let script = TariScript::from_bytes(data.as_slice())
3✔
97
            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))?;
3✔
98
        Ok(script)
3✔
99
    }
4✔
100
}
101

102
impl TariScript {
103
    pub fn new(script: Vec<Opcode>) -> Result<Self, ScriptError> {
2,656✔
104
        let script = ScriptOpcodes::try_from(script)?;
2,656✔
105
        Ok(TariScript { script })
2,656✔
106
    }
2,656✔
107

108
    pub fn iter(&self) -> std::slice::Iter<'_, Opcode> {
×
109
        self.script.iter()
×
110
    }
×
111

112
    /// This pattern matches two scripts ensure they have the same instructions in the opcodes, but not the same values
113
    /// inside example:
114
    /// Script A = {PushPubKey(AA)}, Script B = {PushPubKey(BB)} will pattern match, but doing Script A == Script B will
115
    /// not match Script A = {PushPubKey(AA)}, Script B = {PushPubKey(AA)} will pattern match, doing Script A ==
116
    /// Script B will also match Script A = {PushPubKey(AA)}, Script B = {PushHash(BB)} will not pattern match, and
117
    /// doing Script A == Script B will not match
118
    pub fn pattern_match(&self, script: &TariScript) -> bool {
4✔
119
        for (i, opcode) in self.script.iter().enumerate() {
5✔
120
            if let Some(code) = script.opcode(i) {
5✔
121
                if std::mem::discriminant(opcode) != std::mem::discriminant(code) {
5✔
122
                    return false;
×
123
                }
5✔
124
            } else {
125
                return false;
×
126
            }
127
        }
128
        // We need to ensure they are the same length
129
        script.opcode(self.script.len()).is_none()
4✔
130
    }
4✔
131

132
    /// Retrieve the opcode at the index, returns None if the index does not exist
133
    pub fn opcode(&self, i: usize) -> Option<&Opcode> {
9✔
134
        let opcode = self.script.get(i)?;
9✔
135
        Some(opcode)
6✔
136
    }
9✔
137

138
    /// Executes the script using a default context. If successful, returns the final stack item.
139
    pub fn execute(&self, inputs: &ExecutionStack) -> Result<StackItem, ScriptError> {
120✔
140
        self.execute_with_context(inputs, &ScriptContext::default())
120✔
141
    }
120✔
142

143
    /// Execute the script with the given inputs and the provided context. If successful, returns the final stack item.
144
    pub fn execute_with_context(
349✔
145
        &self,
349✔
146
        inputs: &ExecutionStack,
349✔
147
        context: &ScriptContext,
349✔
148
    ) -> Result<StackItem, ScriptError> {
349✔
149
        // Copy all inputs onto the stack
150
        let mut stack = inputs.clone();
349✔
151
        // Local execution state
152
        let mut state = ExecutionState::default();
349✔
153

154
        for opcode in self.script.iter() {
483✔
155
            if self.should_execute(opcode, &state)? {
483✔
156
                self.execute_opcode(opcode, &mut stack, context, &mut state)?
456✔
157
            } else {
158
                continue;
27✔
159
            }
160
        }
161

162
        // the script has finished but there was an open IfThen or Else!
163
        if !state.if_stack.is_empty() {
296✔
164
            return Err(ScriptError::MissingOpcode);
1✔
165
        }
295✔
166

167
        // After the script completes, it is successful if and only if it has not aborted, and there is exactly a single
168
        // element on the stack. The script fails if the stack is empty, or contains more than one element, or aborts
169
        // early.
170
        if stack.size() == 1 {
295✔
171
            stack.pop().ok_or(ScriptError::NonUnitLengthStack)
294✔
172
        } else {
173
            Err(ScriptError::NonUnitLengthStack)
1✔
174
        }
175
    }
349✔
176

177
    /// Returns the number of script op codes
178
    pub fn size(&self) -> usize {
×
179
        self.script.len()
×
180
    }
×
181

182
    fn should_execute(&self, opcode: &Opcode, state: &ExecutionState) -> Result<bool, ScriptError> {
483✔
183
        use Opcode::{Else, EndIf, IfThen};
184
        match opcode {
483✔
185
            // always execute these, they will update execution state
186
            IfThen | Else | EndIf => Ok(true),
64✔
187
            // otherwise keep calm and carry on
188
            _ => Ok(state.executing),
419✔
189
        }
190
    }
483✔
191

192
    pub fn to_bytes(&self) -> Vec<u8> {
91,291✔
193
        self.script.iter().fold(Vec::new(), |mut bytes, op| {
344,831✔
194
            op.to_bytes(&mut bytes);
344,831✔
195
            bytes
344,831✔
196
        })
344,831✔
197
    }
91,291✔
198

199
    pub fn as_slice(&self) -> &[Opcode] {
1,774✔
200
        self.script.as_ref()
1,774✔
201
    }
1,774✔
202

203
    /// Calculate the hash of the script.
204
    /// `as_hash` returns [ScriptError::InvalidDigest] if the digest function does not produce at least 32 bytes of
205
    /// output.
206
    pub fn as_hash<D: Digest>(&self) -> Result<HashValue, ScriptError> {
×
207
        if <D as Digest>::output_size() < 32 {
×
208
            return Err(ScriptError::InvalidDigest);
×
209
        }
×
210
        let h = D::digest(self.to_bytes());
×
211
        Ok(slice_to_hash(h.as_slice().get(..32).ok_or(ScriptError::InvalidDigest)?))
×
212
    }
×
213

214
    /// Try to deserialise a byte slice into a valid Tari script
215
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, ScriptError> {
25,931✔
216
        let script = ScriptOpcodes::try_from(Opcode::parse(bytes)?)?;
25,931✔
217

218
        Ok(TariScript { script })
25,931✔
219
    }
25,931✔
220

221
    /// Convert the script into an array of opcode strings.
222
    ///
223
    /// # Example
224
    /// ```edition2018
225
    /// use tari_script::TariScript;
226
    /// use tari_utilities::hex::Hex;
227
    ///
228
    /// let hex_script = "71b07aae2337ce44f9ebb6169c863ec168046cb35ab4ef7aa9ed4f5f1f669bb74b09e58170ac276657a418820f34036b20ea615302b373c70ac8feab8d30681a3e0f0960e708";
229
    /// let script = TariScript::from_hex(hex_script).unwrap();
230
    /// let ops = vec![
231
    ///     "Dup",
232
    ///     "HashBlake256",
233
    ///     "PushHash(ae2337ce44f9ebb6169c863ec168046cb35ab4ef7aa9ed4f5f1f669bb74b09e5)",
234
    ///     "EqualVerify",
235
    ///     "Drop",
236
    ///     "CheckSig(276657a418820f34036b20ea615302b373c70ac8feab8d30681a3e0f0960e708)",
237
    /// ]
238
    /// .into_iter()
239
    /// .map(String::from)
240
    /// .collect::<Vec<String>>();
241
    /// assert_eq!(script.to_opcodes(), ops);
242
    /// ```
243
    pub fn to_opcodes(&self) -> Vec<String> {
4✔
244
        self.script.iter().map(|op| op.to_string()).collect()
14✔
245
    }
4✔
246

247
    // pending updates to Dalek/Digest
248
    fn execute_opcode(
456✔
249
        &self,
456✔
250
        opcode: &Opcode,
456✔
251
        stack: &mut ExecutionStack,
456✔
252
        ctx: &ScriptContext,
456✔
253
        state: &mut ExecutionState,
456✔
254
    ) -> Result<(), ScriptError> {
456✔
255
        #[allow(clippy::enum_glob_use)]
256
        use Opcode::*;
257
        use StackItem::{Hash, Number, PublicKey};
258
        match opcode {
456✔
259
            CheckHeightVerify(height) => TariScript::handle_check_height_verify(*height, ctx.block_height()),
10✔
260
            CheckHeight(height) => TariScript::handle_check_height(stack, *height, ctx.block_height()),
20✔
261
            CompareHeightVerify => TariScript::handle_compare_height_verify(stack, ctx.block_height()),
10✔
262
            CompareHeight => TariScript::handle_compare_height(stack, ctx.block_height()),
15✔
263
            Nop => Ok(()),
149✔
264
            PushZero => stack.push(Number(0)),
2✔
265
            PushOne => stack.push(Number(1)),
6✔
266
            PushHash(h) => stack.push(Hash(*h.clone())),
1✔
267
            PushInt(n) => stack.push(Number(*n)),
8✔
268
            PushPubKey(p) => stack.push(PublicKey(*p.clone())),
38✔
269
            Drop => TariScript::handle_drop(stack),
5✔
270
            Dup => TariScript::handle_dup(stack),
8✔
271
            RevRot => stack.push_down(2),
×
272
            GeZero => TariScript::handle_cmp_to_zero(stack, &[Ordering::Greater, Ordering::Equal]),
6✔
273
            GtZero => TariScript::handle_cmp_to_zero(stack, &[Ordering::Greater]),
2✔
274
            LeZero => TariScript::handle_cmp_to_zero(stack, &[Ordering::Less, Ordering::Equal]),
2✔
275
            LtZero => TariScript::handle_cmp_to_zero(stack, &[Ordering::Less]),
2✔
276
            Add => TariScript::handle_op_add(stack),
7✔
277
            Sub => TariScript::handle_op_sub(stack),
2✔
278
            Equal => {
279
                if TariScript::handle_equal(stack)? {
×
280
                    stack.push(Number(1))
×
281
                } else {
282
                    stack.push(Number(0))
×
283
                }
284
            },
285
            EqualVerify => {
286
                if TariScript::handle_equal(stack)? {
3✔
287
                    Ok(())
2✔
288
                } else {
289
                    Err(ScriptError::VerifyFailed)
1✔
290
                }
291
            },
292
            Or(n) => TariScript::handle_or(stack, *n),
7✔
293
            OrVerify(n) => TariScript::handle_or_verify(stack, *n),
6✔
294
            HashBlake256 => TariScript::handle_hash::<Blake2b<U32>>(stack),
1✔
295
            HashSha256 => TariScript::handle_hash::<Sha256>(stack),
2✔
296
            HashSha3 => TariScript::handle_hash::<Sha3_256>(stack),
2✔
297
            CheckSig(msg) => {
9✔
298
                if self.check_sig(stack, *msg.deref())? {
9✔
299
                    stack.push(Number(1))
5✔
300
                } else {
301
                    stack.push(Number(0))
4✔
302
                }
303
            },
304
            CheckSigVerify(msg) => {
2✔
305
                if self.check_sig(stack, *msg.deref())? {
2✔
306
                    Ok(())
1✔
307
                } else {
308
                    Err(ScriptError::VerifyFailed)
1✔
309
                }
310
            },
311
            CheckMultiSig(m, n, public_keys, msg) => {
30✔
312
                if self.check_multisig(stack, *m, *n, public_keys, *msg.deref())?.is_some() {
30✔
313
                    stack.push(Number(1))
14✔
314
                } else {
315
                    stack.push(Number(0))
11✔
316
                }
317
            },
318
            CheckMultiSigVerify(m, n, public_keys, msg) => {
25✔
319
                if self.check_multisig(stack, *m, *n, public_keys, *msg.deref())?.is_some() {
25✔
320
                    Ok(())
13✔
321
                } else {
322
                    Err(ScriptError::VerifyFailed)
8✔
323
                }
324
            },
325
            CheckMultiSigVerifyAggregatePubKey(m, n, public_keys, msg) => {
6✔
326
                if let Some(agg_pub_key) = self.check_multisig(stack, *m, *n, public_keys, *msg.deref())? {
6✔
327
                    stack.push(PublicKey(agg_pub_key))
4✔
328
                } else {
329
                    Err(ScriptError::VerifyFailed)
1✔
330
                }
331
            },
332
            ToRistrettoPoint => self.handle_to_ristretto_point(stack),
4✔
333
            Return => Err(ScriptError::Return),
2✔
334
            IfThen => TariScript::handle_if_then(stack, state),
23✔
335
            Else => TariScript::handle_else(state),
22✔
336
            EndIf => TariScript::handle_end_if(state),
19✔
337
        }
338
    }
456✔
339

340
    fn handle_check_height_verify(height: u64, block_height: u64) -> Result<(), ScriptError> {
10✔
341
        if block_height >= height {
10✔
342
            Ok(())
6✔
343
        } else {
344
            Err(ScriptError::VerifyFailed)
4✔
345
        }
346
    }
10✔
347

348
    fn handle_check_height(stack: &mut ExecutionStack, height: u64, block_height: u64) -> Result<(), ScriptError> {
20✔
349
        let height = i64::try_from(height)?;
20✔
350
        let block_height = i64::try_from(block_height)?;
18✔
351

352
        // Due to the conversion of u64 into i64 which would fail above if they overflowed, these
353
        // numbers should never enter a state where a `sub` could fail. As they'd both be within range and 0 or above.
354
        // This differs from compare_height due to a stack number being used, which can be lower than 0
355
        let item = match block_height.checked_sub(height) {
17✔
356
            Some(num) => StackItem::Number(num),
17✔
357
            None => {
358
                return Err(ScriptError::CompareFailed(
×
359
                    "Subtraction of given height from current block height failed".to_string(),
×
360
                ));
×
361
            },
362
        };
363

364
        stack.push(item)
17✔
365
    }
20✔
366

367
    fn handle_compare_height_verify(stack: &mut ExecutionStack, block_height: u64) -> Result<(), ScriptError> {
10✔
368
        let target_height = stack.pop_into_number::<u64>()?;
10✔
369

370
        if block_height >= target_height {
10✔
371
            Ok(())
6✔
372
        } else {
373
            Err(ScriptError::VerifyFailed)
4✔
374
        }
375
    }
10✔
376

377
    fn handle_compare_height(stack: &mut ExecutionStack, block_height: u64) -> Result<(), ScriptError> {
15✔
378
        let target_height = stack.pop_into_number::<i64>()?;
15✔
379
        let block_height = i64::try_from(block_height)?;
14✔
380

381
        // Here it is possible to underflow because the stack can take lower numbers where check
382
        // height does not use a stack number and it's minimum can't be lower than 0.
383
        let item = match block_height.checked_sub(target_height) {
13✔
384
            Some(num) => StackItem::Number(num),
12✔
385
            None => {
386
                return Err(ScriptError::CompareFailed(
1✔
387
                    "Couldn't subtract the target height from the current block height".to_string(),
1✔
388
                ));
1✔
389
            },
390
        };
391

392
        stack.push(item)
12✔
393
    }
15✔
394

395
    fn handle_cmp_to_zero(stack: &mut ExecutionStack, valid_orderings: &[Ordering]) -> Result<(), ScriptError> {
12✔
396
        let stack_number = stack.pop_into_number::<i64>()?;
12✔
397
        let ordering = &stack_number.cmp(&0);
12✔
398

399
        if valid_orderings.contains(ordering) {
12✔
400
            stack.push(StackItem::Number(1))
8✔
401
        } else {
402
            stack.push(StackItem::Number(0))
4✔
403
        }
404
    }
12✔
405

406
    fn handle_or(stack: &mut ExecutionStack, n: u8) -> Result<(), ScriptError> {
7✔
407
        if stack.pop_n_plus_one_contains(n)? {
7✔
408
            stack.push(StackItem::Number(1))
2✔
409
        } else {
410
            stack.push(StackItem::Number(0))
2✔
411
        }
412
    }
7✔
413

414
    fn handle_or_verify(stack: &mut ExecutionStack, n: u8) -> Result<(), ScriptError> {
6✔
415
        if stack.pop_n_plus_one_contains(n)? {
6✔
416
            Ok(())
4✔
417
        } else {
418
            Err(ScriptError::VerifyFailed)
2✔
419
        }
420
    }
6✔
421

422
    fn handle_if_then(stack: &mut ExecutionStack, state: &mut ExecutionState) -> Result<(), ScriptError> {
23✔
423
        if state.executing {
23✔
424
            let pred = stack.pop().ok_or(ScriptError::StackUnderflow)?;
21✔
425
            match pred {
21✔
426
                StackItem::Number(1) => {
427
                    // continue execution until Else opcode
428
                    state.executing = true;
11✔
429
                    let if_state = IfState {
11✔
430
                        branch: Branch::ExecuteIf,
11✔
431
                        else_expected: true,
11✔
432
                    };
11✔
433
                    state.if_stack.push(if_state);
11✔
434
                    Ok(())
11✔
435
                },
436
                StackItem::Number(0) => {
437
                    // skip execution until Else opcode
438
                    state.executing = false;
10✔
439
                    let if_state = IfState {
10✔
440
                        branch: Branch::ExecuteElse,
10✔
441
                        else_expected: true,
10✔
442
                    };
10✔
443
                    state.if_stack.push(if_state);
10✔
444
                    Ok(())
10✔
445
                },
446
                _ => Err(ScriptError::InvalidInput),
×
447
            }
448
        } else {
449
            let if_state = IfState {
2✔
450
                branch: Branch::NotExecuted,
2✔
451
                else_expected: true,
2✔
452
            };
2✔
453
            state.if_stack.push(if_state);
2✔
454
            Ok(())
2✔
455
        }
456
    }
23✔
457

458
    fn handle_else(state: &mut ExecutionState) -> Result<(), ScriptError> {
22✔
459
        let if_state = state.if_stack.last_mut().ok_or(ScriptError::InvalidOpcode)?;
22✔
460

461
        // check to make sure Else is expected
462
        if !if_state.else_expected {
21✔
463
            return Err(ScriptError::InvalidOpcode);
1✔
464
        }
20✔
465

466
        match if_state.branch {
20✔
467
            Branch::NotExecuted => {
2✔
468
                state.executing = false;
2✔
469
            },
2✔
470
            Branch::ExecuteIf => {
8✔
471
                state.executing = false;
8✔
472
            },
8✔
473
            Branch::ExecuteElse => {
10✔
474
                state.executing = true;
10✔
475
            },
10✔
476
        }
477
        if_state.else_expected = false;
20✔
478
        Ok(())
20✔
479
    }
22✔
480

481
    fn handle_end_if(state: &mut ExecutionState) -> Result<(), ScriptError> {
19✔
482
        // check to make sure EndIf is expected
483
        let if_state = state.if_stack.pop().ok_or(ScriptError::InvalidOpcode)?;
19✔
484

485
        // check if we still expect an Else first
486
        if if_state.else_expected {
17✔
487
            return Err(ScriptError::MissingOpcode);
1✔
488
        }
16✔
489

490
        match if_state.branch {
16✔
491
            Branch::NotExecuted => {
2✔
492
                state.executing = false;
2✔
493
            },
2✔
494
            Branch::ExecuteIf => {
8✔
495
                state.executing = true;
8✔
496
            },
8✔
497
            Branch::ExecuteElse => {
6✔
498
                state.executing = true;
6✔
499
            },
6✔
500
        }
501
        Ok(())
16✔
502
    }
19✔
503

504
    /// Handle opcodes that push a hash to the stack. I'm not doing any length checks right now, so this should be
505
    /// added once other digest functions are provided that don't produce 32 byte hashes
506
    fn handle_hash<D: Digest>(stack: &mut ExecutionStack) -> Result<(), ScriptError> {
5✔
507
        use StackItem::{Commitment, Hash, PublicKey};
508
        let top = stack.pop().ok_or(ScriptError::StackUnderflow)?;
5✔
509
        // use a closure to grab &b while it still exists in the match expression
510
        let to_arr = |b: &[u8]| {
5✔
511
            let mut hash = [0u8; 32];
5✔
512
            hash.copy_from_slice(D::digest(b).as_slice());
5✔
513
            hash
5✔
514
        };
5✔
515
        let hash_value = match top {
5✔
516
            Commitment(c) => to_arr(c.as_bytes()),
2✔
517
            PublicKey(k) => to_arr(k.as_bytes()),
3✔
518
            Hash(h) => to_arr(&h),
×
519
            _ => return Err(ScriptError::IncompatibleTypes),
×
520
        };
521

522
        stack.push(Hash(hash_value))
5✔
523
    }
5✔
524

525
    fn handle_dup(stack: &mut ExecutionStack) -> Result<(), ScriptError> {
8✔
526
        let last = if let Some(last) = stack.peek() {
8✔
527
            last.clone()
8✔
528
        } else {
529
            return Err(ScriptError::StackUnderflow);
×
530
        };
531
        stack.push(last)
8✔
532
    }
8✔
533

534
    fn handle_drop(stack: &mut ExecutionStack) -> Result<(), ScriptError> {
5✔
535
        match stack.pop() {
5✔
536
            Some(_) => Ok(()),
5✔
537
            None => Err(ScriptError::StackUnderflow),
×
538
        }
539
    }
5✔
540

541
    fn handle_op_add(stack: &mut ExecutionStack) -> Result<(), ScriptError> {
7✔
542
        use StackItem::{Commitment, Number, PublicKey};
543
        let top = stack.pop().ok_or(ScriptError::StackUnderflow)?;
7✔
544
        let two = stack.pop().ok_or(ScriptError::StackUnderflow)?;
7✔
545
        match (top, two) {
6✔
546
            (Number(v1), Number(v2)) => stack.push(Number(v1.checked_add(v2).ok_or(ScriptError::ValueExceedsBounds)?)),
5✔
547
            (Commitment(c1), Commitment(c2)) => {
1✔
548
                let com_1 = c1.to_commitment()?;
1✔
549
                let com_2 = c2.to_commitment()?;
1✔
550
                stack.push(Commitment(CompressedCommitment::from_commitment(&com_1 + &com_2)))
1✔
551
            },
552
            (PublicKey(p1), PublicKey(p2)) => {
×
553
                let key1 = p1.to_public_key().map_err(|_| ScriptError::InvalidInput)?;
×
554
                let key2 = p2.to_public_key().map_err(|_| ScriptError::InvalidInput)?;
×
555
                let compressed_key = CompressedKey::new_from_pk(&key1 + &key2);
×
556
                stack.push(PublicKey(compressed_key))
×
557
            },
558
            (_, _) => Err(ScriptError::IncompatibleTypes),
×
559
        }
560
    }
7✔
561

562
    fn handle_op_sub(stack: &mut ExecutionStack) -> Result<(), ScriptError> {
2✔
563
        use StackItem::{Commitment, Number};
564
        let top = stack.pop().ok_or(ScriptError::StackUnderflow)?;
2✔
565
        let two = stack.pop().ok_or(ScriptError::StackUnderflow)?;
2✔
566
        match (top, two) {
2✔
567
            (Number(v1), Number(v2)) => stack.push(Number(v2.checked_sub(v1).ok_or(ScriptError::ValueExceedsBounds)?)),
2✔
568
            (Commitment(c1), Commitment(c2)) => {
×
569
                let com_1 = c1.to_commitment()?;
×
570
                let com_2 = c2.to_commitment()?;
×
571
                stack.push(Commitment(CompressedCommitment::from_commitment(&com_2 - &com_1)))
×
572
            },
573
            (..) => Err(ScriptError::IncompatibleTypes),
×
574
        }
575
    }
2✔
576

577
    fn handle_equal(stack: &mut ExecutionStack) -> Result<bool, ScriptError> {
3✔
578
        use StackItem::{Commitment, Hash, Number, PublicKey, Signature};
579
        let top = stack.pop().ok_or(ScriptError::StackUnderflow)?;
3✔
580
        let two = stack.pop().ok_or(ScriptError::StackUnderflow)?;
3✔
581
        match (top, two) {
3✔
582
            (Number(v1), Number(v2)) => Ok(v1 == v2),
×
583
            (Commitment(c1), Commitment(c2)) => Ok(c1 == c2),
×
584
            (Signature(s1), Signature(s2)) => Ok(s1 == s2),
×
585
            (PublicKey(p1), PublicKey(p2)) => Ok(p1 == p2),
2✔
586
            (Hash(h1), Hash(h2)) => Ok(h1 == h2),
1✔
587
            (..) => Err(ScriptError::IncompatibleTypes),
×
588
        }
589
    }
3✔
590

591
    fn check_sig(&self, stack: &mut ExecutionStack, message: Message) -> Result<bool, ScriptError> {
11✔
592
        use StackItem::{PublicKey, Signature};
593
        let pk = stack.pop().ok_or(ScriptError::StackUnderflow)?;
11✔
594
        let sig = stack.pop().ok_or(ScriptError::StackUnderflow)?;
11✔
595
        match (pk, sig) {
11✔
596
            (PublicKey(p), Signature(s)) => {
11✔
597
                let key = p.to_public_key().map_err(|_| ScriptError::InvalidInput)?;
11✔
598
                let sig = s.to_schnorr_signature()?;
11✔
599
                Ok(sig.verify(&key, message))
11✔
600
            },
601
            (..) => Err(ScriptError::IncompatibleTypes),
×
602
        }
603
    }
11✔
604

605
    /// Validates an m-of-n multisig script
606
    ///
607
    /// This validation broadly proceeds to check if **exactly** _m_ signatures are valid signatures out of a
608
    /// possible _n_ public keys.
609
    ///
610
    /// A successful validation returns `Ok(P)` where _P_ is the sum of the public keys that matched the _m_
611
    /// signatures. If the validation was NOT successful, `check_multisig` returns `Ok(None)`. This is a private
612
    /// function, and callers will interpret these results according to their use cases.
613
    ///
614
    /// Other problems, such as stack underflows, invalid parameters etc return an `Err` as usual.
615
    ///
616
    /// Notes:
617
    /// * The _m_ signatures are expected to be the top _m_ items on the stack.
618
    /// * The ordering of signatures on the stack MUST match the relative ordering of the corresponding public keys.
619
    /// * The list may contain duplicate keys, but each occurrence of a public key may be used AT MOST once.
620
    /// * Every signature MUST be a valid signature using one of the public keys
621
    /// * _m_ and _n_ must be positive AND m <= n AND n <= MAX_MULTISIG_LIMIT (32).
622
    fn check_multisig(
61✔
623
        &self,
61✔
624
        stack: &mut ExecutionStack,
61✔
625

61✔
626
        m: u8,
61✔
627
        n: u8,
61✔
628
        public_keys: &[CompressedKey<RistrettoPublicKey>],
61✔
629
        message: Message,
61✔
630
    ) -> Result<Option<CompressedKey<RistrettoPublicKey>>, ScriptError> {
61✔
631
        if m == 0 || n == 0 || m > n || n > MAX_MULTISIG_LIMIT || public_keys.len() != n as usize {
61✔
632
            return Err(ScriptError::ValueExceedsBounds);
7✔
633
        }
54✔
634
        // pop m sigs
635
        let m = m as usize;
54✔
636
        let signatures = stack
54✔
637
            .pop_num_items(m)?
54✔
638
            .into_iter()
52✔
639
            .map(|item| match item {
103✔
640
                StackItem::Signature(s) => Ok(s),
102✔
641
                _ => Err(ScriptError::IncompatibleTypes),
1✔
642
            })
103✔
643
            .collect::<Result<Vec<CompressedCheckSigSchnorrSignature>, ScriptError>>()?;
52✔
644

645
        // keep a hashset of unique signatures used to prevent someone putting the same signature in more than once.
646
        #[allow(clippy::mutable_key_type)]
647
        let mut sig_set = HashSet::new();
51✔
648

649
        let mut agg_pub_key = RistrettoPublicKey::default();
51✔
650

651
        // Create an iterator that allows each pubkey to only be checked a single time as they are
652
        // removed from the collection when referenced
653
        let mut pub_keys = public_keys.iter();
51✔
654

655
        // Signatures and public keys must be ordered
656
        for s in &signatures {
94✔
657
            if pub_keys.len() == 0 {
94✔
658
                return Ok(None);
8✔
659
            }
86✔
660

661
            if sig_set.contains(s) {
86✔
662
                continue;
2✔
663
            }
84✔
664

665
            for pk in pub_keys.by_ref() {
130✔
666
                let ristretto_key = pk.to_public_key().map_err(|_| ScriptError::InvalidInput)?;
130✔
667
                let sig = s.to_schnorr_signature()?;
130✔
668
                if sig.verify(&ristretto_key, message) {
130✔
669
                    sig_set.insert(s);
74✔
670
                    agg_pub_key = agg_pub_key + ristretto_key;
74✔
671
                    break;
74✔
672
                }
56✔
673
            }
674
            // Make sure the signature matched a public key
675
            if !sig_set.contains(s) {
84✔
676
                return Ok(None);
10✔
677
            }
74✔
678
        }
679
        if sig_set.len() == m {
33✔
680
            let key = CompressedKey::new_from_pk(agg_pub_key);
31✔
681
            Ok(Some(key))
31✔
682
        } else {
683
            Ok(None)
2✔
684
        }
685
    }
61✔
686

687
    fn handle_to_ristretto_point(&self, stack: &mut ExecutionStack) -> Result<(), ScriptError> {
4✔
688
        let item = stack.pop().ok_or(ScriptError::StackUnderflow)?;
4✔
689
        let scalar = match &item {
4✔
690
            StackItem::Hash(hash) => hash.as_slice(),
1✔
691
            StackItem::Scalar(scalar) => scalar.as_slice(),
2✔
692
            _ => return Err(ScriptError::IncompatibleTypes),
1✔
693
        };
694
        let ristretto_sk = RistrettoSecretKey::from_canonical_bytes(scalar).map_err(|_| ScriptError::InvalidInput)?;
3✔
695
        let ristretto_pk = CompressedKey::from_secret_key(&ristretto_sk);
2✔
696
        stack.push(StackItem::PublicKey(ristretto_pk))?;
2✔
697
        Ok(())
2✔
698
    }
4✔
699
}
700

701
impl Iterator for TariScript {
702
    type Item = Opcode;
703

704
    fn next(&mut self) -> Option<Self::Item> {
×
705
        self.script.next()
×
706
    }
×
707
}
708

709
impl<'a> IntoIterator for &'a TariScript {
710
    type IntoIter = std::slice::Iter<'a, Opcode>;
711
    type Item = &'a Opcode;
712

713
    fn into_iter(self) -> Self::IntoIter {
×
714
        self.script.iter()
×
715
    }
×
716
}
717

718
impl Hex for TariScript {
719
    fn from_hex(hex: &str) -> Result<Self, HexError>
7✔
720
    where Self: Sized {
7✔
721
        let bytes = from_hex(hex)?;
7✔
722
        TariScript::from_bytes(&bytes).map_err(|_| HexError::HexConversionError {})
7✔
723
    }
7✔
724

725
    fn to_hex(&self) -> String {
3✔
726
        to_hex(&self.to_bytes())
3✔
727
    }
3✔
728
}
729

730
/// The default Tari script is to push a sender pubkey onto the stack
731
impl Default for TariScript {
732
    fn default() -> Self {
28✔
733
        script!(PushPubKey(Box::default())).expect("default will not fail")
28✔
734
    }
28✔
735
}
736

737
impl fmt::Display for TariScript {
738
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1✔
739
        let s = self.to_opcodes().join(" ");
1✔
740
        f.write_str(&s)
1✔
741
    }
1✔
742
}
743

744
#[derive(Clone, Debug, PartialEq, Eq)]
745
enum Branch {
746
    NotExecuted,
747
    ExecuteIf,
748
    ExecuteElse,
749
}
750

751
#[derive(Clone, Debug, PartialEq, Eq)]
752
struct IfState {
753
    branch: Branch,
754
    else_expected: bool,
755
}
756

757
#[derive(Clone, Debug, PartialEq, Eq)]
758
struct ExecutionState {
759
    executing: bool,
760
    if_stack: Vec<IfState>,
761
}
762

763
impl Default for ExecutionState {
764
    fn default() -> Self {
349✔
765
        Self {
349✔
766
            executing: true,
349✔
767
            if_stack: Vec::new(),
349✔
768
        }
349✔
769
    }
349✔
770
}
771

772
#[cfg(test)]
773
mod test {
774
    #![allow(clippy::indexing_slicing)]
775
    use blake2::Blake2b;
776
    use borsh::{BorshDeserialize, BorshSerialize};
777
    use digest::{Digest, consts::U32};
778
    use sha2::Sha256;
779
    use sha3::Sha3_256 as Sha3;
780
    use tari_crypto::{
781
        compressed_commitment::CompressedCommitment,
782
        compressed_key::CompressedKey,
783
        keys::SecretKey,
784
        ristretto::{RistrettoPublicKey, RistrettoSecretKey, pedersen::CompressedPedersenCommitment},
785
    };
786
    use tari_utilities::{ByteArray, hex::Hex};
787

788
    use crate::{
789
        CheckSigSchnorrSignature,
790
        CompressedCheckSigSchnorrSignature,
791
        ExecutionStack,
792
        Opcode::CheckMultiSigVerifyAggregatePubKey,
793
        ScriptContext,
794
        StackItem,
795
        StackItem::{Commitment, Hash, Number},
796
        TariScript,
797
        error::ScriptError,
798
        inputs,
799
        op_codes::{HashValue, Message, slice_to_boxed_hash, slice_to_boxed_message},
800
    };
801

802
    fn context_with_height(height: u64) -> ScriptContext {
55✔
803
        ScriptContext::new(height, &HashValue::default(), &CompressedPedersenCommitment::default())
55✔
804
    }
55✔
805

806
    #[test]
807
    fn pattern_match() {
1✔
808
        let script_a = script!(Or(1)).unwrap();
1✔
809
        let script_b = script!(Or(1)).unwrap();
1✔
810
        assert_eq!(script_a, script_b);
1✔
811
        assert!(script_a.pattern_match(&script_b));
1✔
812

813
        let script_b = script!(Or(2)).unwrap();
1✔
814
        assert_ne!(script_a, script_b);
1✔
815
        assert!(script_a.pattern_match(&script_b));
1✔
816

817
        let script_b = script!(Or(2) Or(2)).unwrap();
1✔
818
        assert_ne!(script_a, script_b);
1✔
819
        assert!(!script_a.pattern_match(&script_b));
1✔
820

821
        let script_a = script!(Or(2) Or(1)).unwrap();
1✔
822
        let script_b = script!(Or(3) Or(5)).unwrap();
1✔
823
        assert_ne!(script_a, script_b);
1✔
824
        assert!(script_a.pattern_match(&script_b));
1✔
825
    }
1✔
826

827
    #[test]
828
    fn op_or() {
1✔
829
        let script = script!(Or(1)).unwrap();
1✔
830

831
        let inputs = inputs!(4, 4);
1✔
832
        let result = script.execute(&inputs).unwrap();
1✔
833
        assert_eq!(result, Number(1));
1✔
834

835
        let inputs = inputs!(3, 4);
1✔
836
        let result = script.execute(&inputs).unwrap();
1✔
837
        assert_eq!(result, Number(0));
1✔
838

839
        let script = script!(Or(3)).unwrap();
1✔
840

841
        let inputs = inputs!(1, 2, 1, 3);
1✔
842
        let result = script.execute(&inputs).unwrap();
1✔
843
        assert_eq!(result, Number(1));
1✔
844

845
        let inputs = inputs!(1, 2, 4, 3);
1✔
846
        let result = script.execute(&inputs).unwrap();
1✔
847
        assert_eq!(result, Number(0));
1✔
848

849
        let mut rng = rand::thread_rng();
1✔
850
        let (_, p) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
851
        let inputs = inputs!(1, p.clone(), 1, 3);
1✔
852
        let err = script.execute(&inputs).unwrap_err();
1✔
853
        assert!(matches!(err, ScriptError::InvalidInput));
1✔
854

855
        let inputs = inputs!(p, 2, 1, 3);
1✔
856
        let err = script.execute(&inputs).unwrap_err();
1✔
857
        assert!(matches!(err, ScriptError::InvalidInput));
1✔
858

859
        let inputs = inputs!(2, 4, 3);
1✔
860
        let err = script.execute(&inputs).unwrap_err();
1✔
861
        assert!(matches!(err, ScriptError::StackUnderflow));
1✔
862

863
        let script = script!(OrVerify(1)).unwrap();
1✔
864

865
        let inputs = inputs!(1, 4, 4);
1✔
866
        let result = script.execute(&inputs).unwrap();
1✔
867
        assert_eq!(result, Number(1));
1✔
868

869
        let inputs = inputs!(1, 3, 4);
1✔
870
        let err = script.execute(&inputs).unwrap_err();
1✔
871
        assert!(matches!(err, ScriptError::VerifyFailed));
1✔
872

873
        let script = script!(OrVerify(2)).unwrap();
1✔
874

875
        let inputs = inputs!(1, 2, 2, 3);
1✔
876
        let result = script.execute(&inputs).unwrap();
1✔
877
        assert_eq!(result, Number(1));
1✔
878

879
        let inputs = inputs!(1, 2, 3, 4);
1✔
880
        let err = script.execute(&inputs).unwrap_err();
1✔
881
        assert!(matches!(err, ScriptError::VerifyFailed));
1✔
882
    }
1✔
883

884
    #[test]
885
    fn op_if_then_else() {
1✔
886
        // basic
887
        let script = script!(IfThen PushInt(420) Else PushInt(66) EndIf).unwrap();
1✔
888
        let inputs = inputs!(1);
1✔
889
        let result = script.execute(&inputs);
1✔
890
        assert_eq!(result.unwrap(), Number(420));
1✔
891

892
        let inputs = inputs!(0);
1✔
893
        let result = script.execute(&inputs);
1✔
894
        assert_eq!(result.unwrap(), Number(66));
1✔
895

896
        // nested
897
        let script =
1✔
898
            script!(IfThen PushOne IfThen PushInt(420) Else PushInt(555) EndIf Else PushInt(66) EndIf).unwrap();
1✔
899
        let inputs = inputs!(1);
1✔
900
        let result = script.execute(&inputs);
1✔
901
        assert_eq!(result.unwrap(), Number(420));
1✔
902

903
        let script =
1✔
904
            script!(IfThen PushInt(420) Else PushZero IfThen PushInt(111) Else PushInt(66) EndIf Nop EndIf).unwrap();
1✔
905
        let inputs = inputs!(0);
1✔
906
        let result = script.execute(&inputs);
1✔
907
        assert_eq!(result.unwrap(), Number(66));
1✔
908

909
        // duplicate else
910
        let script = script!(IfThen PushInt(420) Else PushInt(66) Else PushInt(777) EndIf).unwrap();
1✔
911
        let inputs = inputs!(0);
1✔
912
        let result = script.execute(&inputs);
1✔
913
        assert_eq!(result.unwrap_err(), ScriptError::InvalidOpcode);
1✔
914

915
        // unexpected else
916
        let script = script!(Else).unwrap();
1✔
917
        let inputs = inputs!(0);
1✔
918
        let result = script.execute(&inputs);
1✔
919
        assert_eq!(result.unwrap_err(), ScriptError::InvalidOpcode);
1✔
920

921
        // unexpected endif
922
        let script = script!(EndIf).unwrap();
1✔
923
        let inputs = inputs!(0);
1✔
924
        let result = script.execute(&inputs);
1✔
925
        assert_eq!(result.unwrap_err(), ScriptError::InvalidOpcode);
1✔
926

927
        // duplicate endif
928
        let script = script!(IfThen PushInt(420) Else PushInt(66) EndIf EndIf).unwrap();
1✔
929
        let inputs = inputs!(0);
1✔
930
        let result = script.execute(&inputs);
1✔
931
        assert_eq!(result.unwrap_err(), ScriptError::InvalidOpcode);
1✔
932

933
        // no else or endif
934
        let script = script!(IfThen PushOne IfThen PushOne).unwrap();
1✔
935
        let inputs = inputs!(1);
1✔
936
        let result = script.execute(&inputs);
1✔
937
        assert_eq!(result.unwrap_err(), ScriptError::MissingOpcode);
1✔
938

939
        // no else
940
        let script = script!(IfThen PushOne EndIf).unwrap();
1✔
941
        let inputs = inputs!(1);
1✔
942
        let result = script.execute(&inputs);
1✔
943
        assert_eq!(result.unwrap_err(), ScriptError::MissingOpcode);
1✔
944

945
        // nested bug
946
        let script =
1✔
947
            script!(IfThen PushInt(111) Else PushZero IfThen PushInt(222) Else PushInt(333) EndIf EndIf).unwrap();
1✔
948
        let inputs = inputs!(1);
1✔
949
        let result = script.execute(&inputs);
1✔
950
        assert_eq!(result.unwrap(), Number(111));
1✔
951
    }
1✔
952

953
    #[test]
954
    fn op_check_height() {
1✔
955
        let inputs = ExecutionStack::default();
1✔
956
        let script = script!(CheckHeight(5)).unwrap();
1✔
957

958
        for block_height in 1..=10 {
10✔
959
            let ctx = context_with_height(u64::try_from(block_height).unwrap());
10✔
960
            assert_eq!(
10✔
961
                script.execute_with_context(&inputs, &ctx).unwrap(),
10✔
962
                Number(block_height - 5)
10✔
963
            );
964
        }
965

966
        let script = script!(CheckHeight(u64::MAX)).unwrap();
1✔
967
        let ctx = context_with_height(i64::MAX as u64);
1✔
968
        let err = script.execute_with_context(&inputs, &ctx).unwrap_err();
1✔
969
        assert!(matches!(err, ScriptError::ValueExceedsBounds));
1✔
970

971
        let script = script!(CheckHeightVerify(5)).unwrap();
1✔
972
        let inputs = inputs!(1);
1✔
973

974
        for block_height in 1..5 {
4✔
975
            let ctx = context_with_height(block_height);
4✔
976
            let err = script.execute_with_context(&inputs, &ctx).unwrap_err();
4✔
977
            assert!(matches!(err, ScriptError::VerifyFailed));
4✔
978
        }
979

980
        for block_height in 5..=10 {
6✔
981
            let ctx = context_with_height(block_height);
6✔
982
            let result = script.execute_with_context(&inputs, &ctx).unwrap();
6✔
983
            assert_eq!(result, Number(1));
6✔
984
        }
985
    }
1✔
986

987
    #[test]
988
    fn op_compare_height() {
1✔
989
        let script = script!(CompareHeight).unwrap();
1✔
990
        let inputs = inputs!(5);
1✔
991

992
        for block_height in 1..=10 {
10✔
993
            let ctx = context_with_height(u64::try_from(block_height).unwrap());
10✔
994
            assert_eq!(
10✔
995
                script.execute_with_context(&inputs, &ctx).unwrap(),
10✔
996
                Number(block_height - 5)
10✔
997
            );
998
        }
999

1000
        let script = script!(CompareHeightVerify).unwrap();
1✔
1001
        let inputs = inputs!(1, 5);
1✔
1002

1003
        for block_height in 1..5 {
4✔
1004
            let ctx = context_with_height(block_height);
4✔
1005
            let err = script.execute_with_context(&inputs, &ctx).unwrap_err();
4✔
1006
            assert!(matches!(err, ScriptError::VerifyFailed));
4✔
1007
        }
1008

1009
        for block_height in 5..=10 {
6✔
1010
            let ctx = context_with_height(block_height);
6✔
1011
            let result = script.execute_with_context(&inputs, &ctx).unwrap();
6✔
1012
            assert_eq!(result, Number(1));
6✔
1013
        }
1014
    }
1✔
1015

1016
    #[test]
1017
    fn op_drop_push() {
1✔
1018
        let inputs = inputs!(420);
1✔
1019
        let script = script!(Drop PushOne).unwrap();
1✔
1020
        assert_eq!(script.execute(&inputs).unwrap(), Number(1));
1✔
1021

1022
        let script = script!(Drop PushZero).unwrap();
1✔
1023
        assert_eq!(script.execute(&inputs).unwrap(), Number(0));
1✔
1024

1025
        let script = script!(Drop PushInt(5)).unwrap();
1✔
1026
        assert_eq!(script.execute(&inputs).unwrap(), Number(5));
1✔
1027
    }
1✔
1028

1029
    #[test]
1030
    fn op_comparison_to_zero() {
1✔
1031
        let script = script!(GeZero).unwrap();
1✔
1032
        let inputs = inputs!(1);
1✔
1033
        assert_eq!(script.execute(&inputs).unwrap(), Number(1));
1✔
1034
        let inputs = inputs!(0);
1✔
1035
        assert_eq!(script.execute(&inputs).unwrap(), Number(1));
1✔
1036

1037
        let script = script!(GtZero).unwrap();
1✔
1038
        let inputs = inputs!(1);
1✔
1039
        assert_eq!(script.execute(&inputs).unwrap(), Number(1));
1✔
1040
        let inputs = inputs!(0);
1✔
1041
        assert_eq!(script.execute(&inputs).unwrap(), Number(0));
1✔
1042

1043
        let script = script!(LeZero).unwrap();
1✔
1044
        let inputs = inputs!(-1);
1✔
1045
        assert_eq!(script.execute(&inputs).unwrap(), Number(1));
1✔
1046
        let inputs = inputs!(0);
1✔
1047
        assert_eq!(script.execute(&inputs).unwrap(), Number(1));
1✔
1048

1049
        let script = script!(LtZero).unwrap();
1✔
1050
        let inputs = inputs!(-1);
1✔
1051
        assert_eq!(script.execute(&inputs).unwrap(), Number(1));
1✔
1052
        let inputs = inputs!(0);
1✔
1053
        assert_eq!(script.execute(&inputs).unwrap(), Number(0));
1✔
1054
    }
1✔
1055

1056
    #[test]
1057
    fn op_hash() {
1✔
1058
        let mut rng = rand::thread_rng();
1✔
1059
        let (_, p) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1060
        let c = CompressedCommitment::<RistrettoPublicKey>::from_compressed_key(p.clone());
1✔
1061
        let script = script!(HashSha256).unwrap();
1✔
1062

1063
        let hash = Sha256::digest(p.as_bytes());
1✔
1064
        let inputs = inputs!(p.clone());
1✔
1065
        assert_eq!(script.execute(&inputs).unwrap(), Hash(hash.into()));
1✔
1066

1067
        let hash = Sha256::digest(c.as_bytes());
1✔
1068
        let inputs = inputs!(c.clone());
1✔
1069
        assert_eq!(script.execute(&inputs).unwrap(), Hash(hash.into()));
1✔
1070

1071
        let script = script!(HashSha3).unwrap();
1✔
1072

1073
        let hash = Sha3::digest(p.as_bytes());
1✔
1074
        let inputs = inputs!(p);
1✔
1075
        assert_eq!(script.execute(&inputs).unwrap(), Hash(hash.into()));
1✔
1076

1077
        let hash = Sha3::digest(c.as_bytes());
1✔
1078
        let inputs = inputs!(c);
1✔
1079
        assert_eq!(script.execute(&inputs).unwrap(), Hash(hash.into()));
1✔
1080
    }
1✔
1081

1082
    #[test]
1083
    fn op_return() {
1✔
1084
        let script = script!(Return).unwrap();
1✔
1085
        let inputs = ExecutionStack::default();
1✔
1086
        assert_eq!(script.execute(&inputs), Err(ScriptError::Return));
1✔
1087
    }
1✔
1088

1089
    #[test]
1090
    fn op_add() {
1✔
1091
        let script = script!(Add).unwrap();
1✔
1092
        let inputs = inputs!(3, 2);
1✔
1093
        assert_eq!(script.execute(&inputs).unwrap(), Number(5));
1✔
1094
        let inputs = inputs!(3, -3);
1✔
1095
        assert_eq!(script.execute(&inputs).unwrap(), Number(0));
1✔
1096
        let inputs = inputs!(i64::MAX, 1);
1✔
1097
        assert_eq!(script.execute(&inputs), Err(ScriptError::ValueExceedsBounds));
1✔
1098
        let inputs = inputs!(1);
1✔
1099
        assert_eq!(script.execute(&inputs), Err(ScriptError::StackUnderflow));
1✔
1100
    }
1✔
1101

1102
    #[test]
1103
    fn op_add_commitments() {
1✔
1104
        let script = script!(Add).unwrap();
1✔
1105
        let mut rng = rand::thread_rng();
1✔
1106
        let (_, c1) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1107
        let (_, c2) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1108
        let c3 = &c1.to_public_key().unwrap() + &c2.to_public_key().unwrap();
1✔
1109
        let c3 = CompressedCommitment::<RistrettoPublicKey>::from_public_key(c3);
1✔
1110
        let inputs = inputs!(
1✔
1111
            CompressedCommitment::<RistrettoPublicKey>::from_compressed_key(c1),
1✔
1112
            CompressedCommitment::<RistrettoPublicKey>::from_compressed_key(c2)
1✔
1113
        );
1114
        assert_eq!(script.execute(&inputs).unwrap(), Commitment(c3));
1✔
1115
    }
1✔
1116

1117
    #[test]
1118
    fn op_sub() {
1✔
1119
        use crate::StackItem::Number;
1120
        let script = script!(Add Sub).unwrap();
1✔
1121
        let inputs = inputs!(5, 3, 2);
1✔
1122
        assert_eq!(script.execute(&inputs).unwrap(), Number(0));
1✔
1123
        let inputs = inputs!(i64::MAX, 1);
1✔
1124
        assert_eq!(script.execute(&inputs), Err(ScriptError::ValueExceedsBounds));
1✔
1125
        let script = script!(Sub).unwrap();
1✔
1126
        let inputs = inputs!(5, 3);
1✔
1127
        assert_eq!(script.execute(&inputs).unwrap(), Number(2));
1✔
1128
    }
1✔
1129

1130
    #[test]
1131
    fn serialisation() {
1✔
1132
        let script = script!(Add Sub Add).unwrap();
1✔
1133
        assert_eq!(&script.to_bytes(), &[0x93, 0x94, 0x93]);
1✔
1134
        assert_eq!(TariScript::from_bytes(&[0x93, 0x94, 0x93]).unwrap(), script);
1✔
1135
        assert_eq!(script.to_hex(), "939493");
1✔
1136
        assert_eq!(TariScript::from_hex("939493").unwrap(), script);
1✔
1137
    }
1✔
1138

1139
    #[test]
1140
    fn check_sig() {
1✔
1141
        use crate::StackItem::Number;
1142
        let mut rng = rand::thread_rng();
1✔
1143
        let (pvt_key, pub_key) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1144
        let m_key = RistrettoSecretKey::random(&mut rng);
1✔
1145
        let sig = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1146
            CheckSigSchnorrSignature::sign(&pvt_key, m_key.as_bytes(), &mut rng).unwrap(),
1✔
1147
        );
1148
        let msg = slice_to_boxed_message(m_key.as_bytes());
1✔
1149
        let script = script!(CheckSig(msg)).unwrap();
1✔
1150
        let inputs = inputs!(sig.clone(), pub_key.clone());
1✔
1151
        let result = script.execute(&inputs).unwrap();
1✔
1152
        assert_eq!(result, Number(1));
1✔
1153

1154
        let n_key = RistrettoSecretKey::random(&mut rng);
1✔
1155
        let msg = slice_to_boxed_message(n_key.as_bytes());
1✔
1156
        let script = script!(CheckSig(msg)).unwrap();
1✔
1157
        let inputs = inputs!(sig, pub_key);
1✔
1158
        let result = script.execute(&inputs).unwrap();
1✔
1159
        assert_eq!(result, Number(0));
1✔
1160
    }
1✔
1161

1162
    #[test]
1163
    fn check_sig_verify() {
1✔
1164
        use crate::StackItem::Number;
1165
        let mut rng = rand::thread_rng();
1✔
1166
        let (pvt_key, pub_key) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1167
        let m_key = RistrettoSecretKey::random(&mut rng);
1✔
1168
        let sig = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1169
            CheckSigSchnorrSignature::sign(&pvt_key, m_key.as_bytes(), &mut rng).unwrap(),
1✔
1170
        );
1171
        let msg = slice_to_boxed_message(m_key.as_bytes());
1✔
1172
        let script = script!(CheckSigVerify(msg) PushOne).unwrap();
1✔
1173
        let inputs = inputs!(sig.clone(), pub_key.clone());
1✔
1174
        let result = script.execute(&inputs).unwrap();
1✔
1175
        assert_eq!(result, Number(1));
1✔
1176

1177
        let n_key = RistrettoSecretKey::random(&mut rng);
1✔
1178
        let msg = slice_to_boxed_message(n_key.as_bytes());
1✔
1179
        let script = script!(CheckSigVerify(msg)).unwrap();
1✔
1180
        let inputs = inputs!(sig, pub_key);
1✔
1181
        let err = script.execute(&inputs).unwrap_err();
1✔
1182
        assert!(matches!(err, ScriptError::VerifyFailed));
1✔
1183
    }
1✔
1184

1185
    #[allow(clippy::type_complexity)]
1186
    fn multisig_data(
5✔
1187
        n: usize,
5✔
1188
    ) -> (
5✔
1189
        Box<Message>,
5✔
1190
        Vec<(
5✔
1191
            RistrettoSecretKey,
5✔
1192
            CompressedKey<RistrettoPublicKey>,
5✔
1193
            CompressedCheckSigSchnorrSignature,
5✔
1194
        )>,
5✔
1195
    ) {
5✔
1196
        let mut rng = rand::thread_rng();
5✔
1197
        let mut data = Vec::with_capacity(n);
5✔
1198
        let m = RistrettoSecretKey::random(&mut rng);
5✔
1199
        let msg = slice_to_boxed_message(m.as_bytes());
5✔
1200

1201
        for _ in 0..n {
55✔
1202
            let (k, p) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
55✔
1203
            let s = CompressedCheckSigSchnorrSignature::new_from_schnorr(
55✔
1204
                CheckSigSchnorrSignature::sign(&k, m.as_bytes(), &mut rng).unwrap(),
55✔
1205
            );
55✔
1206
            data.push((k, p, s));
55✔
1207
        }
55✔
1208

1209
        (msg, data)
5✔
1210
    }
5✔
1211

1212
    #[allow(clippy::too_many_lines)]
1213
    #[test]
1214
    fn check_multisig() {
1✔
1215
        use crate::{StackItem::Number, op_codes::Opcode::CheckMultiSig};
1216
        let mut rng = rand::thread_rng();
1✔
1217
        let (k_alice, p_alice) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1218
        let (k_bob, p_bob) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1219
        let (k_eve, _) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1220
        let (k_carol, p_carol) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1221
        let m = RistrettoSecretKey::random(&mut rng);
1✔
1222
        let s_alice = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1223
            CheckSigSchnorrSignature::sign(&k_alice, m.as_bytes(), &mut rng).unwrap(),
1✔
1224
        );
1225
        let s_bob = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1226
            CheckSigSchnorrSignature::sign(&k_bob, m.as_bytes(), &mut rng).unwrap(),
1✔
1227
        );
1228
        let s_eve = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1229
            CheckSigSchnorrSignature::sign(&k_eve, m.as_bytes(), &mut rng).unwrap(),
1✔
1230
        );
1231
        let s_carol = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1232
            CheckSigSchnorrSignature::sign(&k_carol, m.as_bytes(), &mut rng).unwrap(),
1✔
1233
        );
1234
        let s_alice2 = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1235
            CheckSigSchnorrSignature::sign(&k_alice, m.as_bytes(), &mut rng).unwrap(),
1✔
1236
        );
1237
        let msg = slice_to_boxed_message(m.as_bytes());
1✔
1238

1239
        // 1 of 2
1240
        let keys = vec![p_alice.clone(), p_bob.clone()];
1✔
1241
        let ops = vec![CheckMultiSig(1, 2, keys, msg.clone())];
1✔
1242
        let script = TariScript::new(ops).unwrap();
1✔
1243

1244
        let inputs = inputs!(s_alice.clone());
1✔
1245
        let result = script.execute(&inputs).unwrap();
1✔
1246
        assert_eq!(result, Number(1));
1✔
1247
        let inputs = inputs!(s_bob.clone());
1✔
1248
        let result = script.execute(&inputs).unwrap();
1✔
1249
        assert_eq!(result, Number(1));
1✔
1250
        let inputs = inputs!(s_eve.clone());
1✔
1251
        let result = script.execute(&inputs).unwrap();
1✔
1252
        assert_eq!(result, Number(0));
1✔
1253

1254
        // 2 of 2
1255
        let keys = vec![p_alice.clone(), p_bob.clone()];
1✔
1256
        let ops = vec![CheckMultiSig(2, 2, keys, msg.clone())];
1✔
1257
        let script = TariScript::new(ops).unwrap();
1✔
1258

1259
        let inputs = inputs!(s_alice.clone(), s_bob.clone());
1✔
1260
        let result = script.execute(&inputs).unwrap();
1✔
1261
        assert_eq!(result, Number(1));
1✔
1262
        let inputs = inputs!(s_bob.clone(), s_alice.clone());
1✔
1263
        let result = script.execute(&inputs).unwrap();
1✔
1264
        assert_eq!(result, Number(0));
1✔
1265
        let inputs = inputs!(s_eve.clone(), s_bob.clone());
1✔
1266
        let result = script.execute(&inputs).unwrap();
1✔
1267
        assert_eq!(result, Number(0));
1✔
1268

1269
        // 2 of 2 - don't allow same sig to sign twice
1270
        let inputs = inputs!(s_alice.clone(), s_alice.clone());
1✔
1271
        let result = script.execute(&inputs).unwrap();
1✔
1272
        assert_eq!(result, Number(0));
1✔
1273

1274
        // 1 of 3
1275
        let keys = vec![p_alice.clone(), p_bob.clone(), p_carol.clone()];
1✔
1276
        let ops = vec![CheckMultiSig(1, 3, keys, msg.clone())];
1✔
1277
        let script = TariScript::new(ops).unwrap();
1✔
1278

1279
        let inputs = inputs!(s_alice.clone());
1✔
1280
        let result = script.execute(&inputs).unwrap();
1✔
1281
        assert_eq!(result, Number(1));
1✔
1282
        let inputs = inputs!(s_bob.clone());
1✔
1283
        let result = script.execute(&inputs).unwrap();
1✔
1284
        assert_eq!(result, Number(1));
1✔
1285
        let inputs = inputs!(s_carol.clone());
1✔
1286
        let result = script.execute(&inputs).unwrap();
1✔
1287
        assert_eq!(result, Number(1));
1✔
1288
        let inputs = inputs!(s_eve.clone());
1✔
1289
        let result = script.execute(&inputs).unwrap();
1✔
1290
        assert_eq!(result, Number(0));
1✔
1291

1292
        // 2 of 3
1293
        let keys = vec![p_alice.clone(), p_bob.clone(), p_carol.clone()];
1✔
1294
        let ops = vec![CheckMultiSig(2, 3, keys, msg.clone())];
1✔
1295
        let script = TariScript::new(ops).unwrap();
1✔
1296

1297
        let inputs = inputs!(s_alice.clone(), s_bob.clone());
1✔
1298
        let result = script.execute(&inputs).unwrap();
1✔
1299
        assert_eq!(result, Number(1));
1✔
1300
        let inputs = inputs!(s_alice.clone(), s_carol.clone());
1✔
1301
        let result = script.execute(&inputs).unwrap();
1✔
1302
        assert_eq!(result, Number(1));
1✔
1303
        let inputs = inputs!(s_bob.clone(), s_carol.clone());
1✔
1304
        let result = script.execute(&inputs).unwrap();
1✔
1305
        assert_eq!(result, Number(1));
1✔
1306
        let inputs = inputs!(s_carol.clone(), s_bob.clone());
1✔
1307
        let result = script.execute(&inputs).unwrap();
1✔
1308
        assert_eq!(result, Number(0));
1✔
1309
        let inputs = inputs!(s_carol.clone(), s_eve.clone());
1✔
1310
        let result = script.execute(&inputs).unwrap();
1✔
1311
        assert_eq!(result, Number(0));
1✔
1312

1313
        // check that sigs are only counted once
1314
        let keys = vec![p_alice.clone(), p_bob.clone(), p_alice.clone()];
1✔
1315
        let ops = vec![CheckMultiSig(2, 3, keys, msg.clone())];
1✔
1316
        let script = TariScript::new(ops).unwrap();
1✔
1317

1318
        let inputs = inputs!(s_alice.clone(), s_carol.clone());
1✔
1319
        let result = script.execute(&inputs).unwrap();
1✔
1320
        assert_eq!(result, Number(0));
1✔
1321
        let inputs = inputs!(s_alice.clone(), s_alice.clone());
1✔
1322
        let result = script.execute(&inputs).unwrap();
1✔
1323
        assert_eq!(result, Number(0));
1✔
1324
        let inputs = inputs!(s_alice.clone(), s_alice2.clone());
1✔
1325
        let result = script.execute(&inputs).unwrap();
1✔
1326
        assert_eq!(result, Number(1));
1✔
1327
        // Interesting case where either sig could match either pubkey
1328
        let inputs = inputs!(s_alice2, s_alice.clone());
1✔
1329
        let result = script.execute(&inputs).unwrap();
1✔
1330
        assert_eq!(result, Number(1));
1✔
1331

1332
        // 3 of 3
1333
        let keys = vec![p_alice.clone(), p_bob.clone(), p_carol];
1✔
1334
        let ops = vec![CheckMultiSig(3, 3, keys, msg.clone())];
1✔
1335
        let script = TariScript::new(ops).unwrap();
1✔
1336

1337
        let inputs = inputs!(s_alice.clone(), s_bob.clone(), s_carol.clone());
1✔
1338
        let result = script.execute(&inputs).unwrap();
1✔
1339
        assert_eq!(result, Number(1));
1✔
1340
        let inputs = inputs!(s_carol.clone(), s_alice.clone(), s_bob.clone());
1✔
1341
        let result = script.execute(&inputs).unwrap();
1✔
1342
        assert_eq!(result, Number(0));
1✔
1343
        let inputs = inputs!(s_eve.clone(), s_bob.clone(), s_carol);
1✔
1344
        let result = script.execute(&inputs).unwrap();
1✔
1345
        assert_eq!(result, Number(0));
1✔
1346
        let inputs = inputs!(s_eve, s_bob);
1✔
1347
        let err = script.execute(&inputs).unwrap_err();
1✔
1348
        assert_eq!(err, ScriptError::StackUnderflow);
1✔
1349

1350
        // errors
1351
        let keys = vec![p_alice.clone(), p_bob.clone()];
1✔
1352
        let ops = vec![CheckMultiSig(0, 2, keys, msg.clone())];
1✔
1353
        let script = TariScript::new(ops).unwrap();
1✔
1354
        let inputs = inputs!(s_alice.clone());
1✔
1355
        let err = script.execute(&inputs).unwrap_err();
1✔
1356
        assert_eq!(err, ScriptError::ValueExceedsBounds);
1✔
1357

1358
        let keys = vec![p_alice.clone(), p_bob.clone()];
1✔
1359
        let ops = vec![CheckMultiSig(1, 0, keys, msg.clone())];
1✔
1360
        let script = TariScript::new(ops).unwrap();
1✔
1361
        let inputs = inputs!(s_alice.clone());
1✔
1362
        let err = script.execute(&inputs).unwrap_err();
1✔
1363
        assert_eq!(err, ScriptError::ValueExceedsBounds);
1✔
1364

1365
        let keys = vec![p_alice, p_bob];
1✔
1366
        let ops = vec![CheckMultiSig(2, 1, keys, msg)];
1✔
1367
        let script = TariScript::new(ops).unwrap();
1✔
1368
        let inputs = inputs!(s_alice);
1✔
1369
        let err = script.execute(&inputs).unwrap_err();
1✔
1370
        assert_eq!(err, ScriptError::ValueExceedsBounds);
1✔
1371

1372
        // max n is 32
1373
        let (msg, data) = multisig_data(33);
1✔
1374
        let keys = data.iter().map(|(_, p, _)| p.clone()).collect();
33✔
1375
        let sigs = data.iter().take(17).map(|(_, _, s)| s.clone());
17✔
1376
        let script = script!(CheckMultiSig(17, 33, keys, msg)).unwrap();
1✔
1377
        let items = sigs.map(StackItem::Signature).collect();
1✔
1378
        let inputs = ExecutionStack::new(items);
1✔
1379
        let err = script.execute(&inputs).unwrap_err();
1✔
1380
        assert_eq!(err, ScriptError::ValueExceedsBounds);
1✔
1381

1382
        // 3 of 4
1383
        let (msg, data) = multisig_data(4);
1✔
1384
        let keys = vec![
1✔
1385
            data[0].1.clone(),
1✔
1386
            data[1].1.clone(),
1✔
1387
            data[2].1.clone(),
1✔
1388
            data[3].1.clone(),
1✔
1389
        ];
1390
        let ops = vec![CheckMultiSig(3, 4, keys, msg)];
1✔
1391
        let script = TariScript::new(ops).unwrap();
1✔
1392
        let inputs = inputs!(data[0].2.clone(), data[1].2.clone(), data[2].2.clone());
1✔
1393
        let result = script.execute(&inputs).unwrap();
1✔
1394
        assert_eq!(result, Number(1));
1✔
1395

1396
        // 5 of 7
1397
        let (msg, data) = multisig_data(7);
1✔
1398
        let keys = vec![
1✔
1399
            data[0].1.clone(),
1✔
1400
            data[1].1.clone(),
1✔
1401
            data[2].1.clone(),
1✔
1402
            data[3].1.clone(),
1✔
1403
            data[4].1.clone(),
1✔
1404
            data[5].1.clone(),
1✔
1405
            data[6].1.clone(),
1✔
1406
        ];
1407
        let ops = vec![CheckMultiSig(5, 7, keys, msg)];
1✔
1408
        let script = TariScript::new(ops).unwrap();
1✔
1409
        let inputs = inputs!(
1✔
1410
            data[0].2.clone(),
1✔
1411
            data[1].2.clone(),
1✔
1412
            data[2].2.clone(),
1✔
1413
            data[3].2.clone(),
1✔
1414
            data[4].2.clone()
1✔
1415
        );
1416
        let result = script.execute(&inputs).unwrap();
1✔
1417
        assert_eq!(result, Number(1));
1✔
1418
    }
1✔
1419

1420
    #[allow(clippy::too_many_lines)]
1421
    #[test]
1422
    fn check_multisig_verify() {
1✔
1423
        use crate::{StackItem::Number, op_codes::Opcode::CheckMultiSigVerify};
1424
        let mut rng = rand::thread_rng();
1✔
1425
        let (k_alice, p_alice) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1426
        let (k_bob, p_bob) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1427
        let (k_eve, _) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1428
        let (k_carol, p_carol) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1429
        let m = RistrettoSecretKey::random(&mut rng);
1✔
1430
        let s_alice = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1431
            CheckSigSchnorrSignature::sign(&k_alice, m.as_bytes(), &mut rng).unwrap(),
1✔
1432
        );
1433
        let s_bob = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1434
            CheckSigSchnorrSignature::sign(&k_bob, m.as_bytes(), &mut rng).unwrap(),
1✔
1435
        );
1436
        let s_eve = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1437
            CheckSigSchnorrSignature::sign(&k_eve, m.as_bytes(), &mut rng).unwrap(),
1✔
1438
        );
1439
        let s_carol = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1440
            CheckSigSchnorrSignature::sign(&k_carol, m.as_bytes(), &mut rng).unwrap(),
1✔
1441
        );
1442
        let msg = slice_to_boxed_message(m.as_bytes());
1✔
1443

1444
        // 1 of 2
1445
        let keys = vec![p_alice.clone(), p_bob.clone()];
1✔
1446
        let ops = vec![CheckMultiSigVerify(1, 2, keys, msg.clone())];
1✔
1447
        let script = TariScript::new(ops).unwrap();
1✔
1448

1449
        let inputs = inputs!(Number(1), s_alice.clone());
1✔
1450
        let result = script.execute(&inputs).unwrap();
1✔
1451
        assert_eq!(result, Number(1));
1✔
1452
        let inputs = inputs!(Number(1), s_bob.clone());
1✔
1453
        let result = script.execute(&inputs).unwrap();
1✔
1454
        assert_eq!(result, Number(1));
1✔
1455
        let inputs = inputs!(Number(1), s_eve.clone());
1✔
1456
        let err = script.execute(&inputs).unwrap_err();
1✔
1457
        assert_eq!(err, ScriptError::VerifyFailed);
1✔
1458

1459
        // 2 of 2
1460
        let keys = vec![p_alice.clone(), p_bob.clone()];
1✔
1461
        let ops = vec![CheckMultiSigVerify(2, 2, keys, msg.clone())];
1✔
1462
        let script = TariScript::new(ops).unwrap();
1✔
1463

1464
        let inputs = inputs!(Number(1), s_alice.clone(), s_bob.clone());
1✔
1465
        let result = script.execute(&inputs).unwrap();
1✔
1466
        assert_eq!(result, Number(1));
1✔
1467
        let inputs = inputs!(Number(1), s_bob.clone(), s_alice.clone());
1✔
1468
        let err = script.execute(&inputs).unwrap_err();
1✔
1469
        assert_eq!(err, ScriptError::VerifyFailed);
1✔
1470
        let inputs = inputs!(Number(1), s_eve.clone(), s_bob.clone());
1✔
1471
        let err = script.execute(&inputs).unwrap_err();
1✔
1472
        assert_eq!(err, ScriptError::VerifyFailed);
1✔
1473

1474
        // 1 of 3
1475
        let keys = vec![p_alice.clone(), p_bob.clone(), p_carol.clone()];
1✔
1476
        let ops = vec![CheckMultiSigVerify(1, 3, keys, msg.clone())];
1✔
1477
        let script = TariScript::new(ops).unwrap();
1✔
1478

1479
        let inputs = inputs!(Number(1), s_alice.clone());
1✔
1480
        let result = script.execute(&inputs).unwrap();
1✔
1481
        assert_eq!(result, Number(1));
1✔
1482
        let inputs = inputs!(Number(1), s_bob.clone());
1✔
1483
        let result = script.execute(&inputs).unwrap();
1✔
1484
        assert_eq!(result, Number(1));
1✔
1485
        let inputs = inputs!(Number(1), s_carol.clone());
1✔
1486
        let result = script.execute(&inputs).unwrap();
1✔
1487
        assert_eq!(result, Number(1));
1✔
1488
        let inputs = inputs!(Number(1), s_eve.clone());
1✔
1489
        let err = script.execute(&inputs).unwrap_err();
1✔
1490
        assert_eq!(err, ScriptError::VerifyFailed);
1✔
1491

1492
        // 2 of 3
1493
        let keys = vec![p_alice.clone(), p_bob.clone(), p_carol.clone()];
1✔
1494
        let ops = vec![CheckMultiSigVerify(2, 3, keys, msg.clone())];
1✔
1495
        let script = TariScript::new(ops).unwrap();
1✔
1496

1497
        let inputs = inputs!(Number(1), s_alice.clone(), s_bob.clone());
1✔
1498
        let result = script.execute(&inputs).unwrap();
1✔
1499
        assert_eq!(result, Number(1));
1✔
1500
        let inputs = inputs!(Number(1), s_alice.clone(), s_carol.clone());
1✔
1501
        let result = script.execute(&inputs).unwrap();
1✔
1502
        assert_eq!(result, Number(1));
1✔
1503
        let inputs = inputs!(Number(1), s_bob.clone(), s_carol.clone());
1✔
1504
        let result = script.execute(&inputs).unwrap();
1✔
1505
        assert_eq!(result, Number(1));
1✔
1506
        let inputs = inputs!(Number(1), s_carol.clone(), s_bob.clone());
1✔
1507
        let err = script.execute(&inputs).unwrap_err();
1✔
1508
        assert_eq!(err, ScriptError::VerifyFailed);
1✔
1509
        let inputs = inputs!(Number(1), s_carol.clone(), s_eve.clone());
1✔
1510
        let err = script.execute(&inputs).unwrap_err();
1✔
1511
        assert_eq!(err, ScriptError::VerifyFailed);
1✔
1512

1513
        // 2 of 3 (returning the aggregate public key of the signatories)
1514
        let keys = vec![p_alice.clone(), p_bob.clone(), p_carol.clone()];
1✔
1515
        let ops = vec![CheckMultiSigVerifyAggregatePubKey(2, 3, keys, msg.clone())];
1✔
1516
        let script = TariScript::new(ops).unwrap();
1✔
1517

1518
        let inputs = inputs!(s_alice.clone(), s_bob.clone());
1✔
1519
        let agg_pub_key = script.execute(&inputs).unwrap();
1✔
1520
        assert_eq!(
1✔
1521
            agg_pub_key,
1522
            StackItem::PublicKey(CompressedKey::<RistrettoPublicKey>::new_from_pk(
1✔
1523
                &p_alice.clone().to_public_key().unwrap() + &p_bob.clone().to_public_key().unwrap()
1✔
1524
            ))
1✔
1525
        );
1526

1527
        let inputs = inputs!(s_alice.clone(), s_carol.clone());
1✔
1528
        let agg_pub_key = script.execute(&inputs).unwrap();
1✔
1529
        assert_eq!(
1✔
1530
            agg_pub_key,
1531
            StackItem::PublicKey(CompressedKey::<RistrettoPublicKey>::new_from_pk(
1✔
1532
                &p_alice.clone().to_public_key().unwrap() + &p_carol.clone().to_public_key().unwrap()
1✔
1533
            ))
1✔
1534
        );
1535

1536
        let inputs = inputs!(s_bob.clone(), s_carol.clone());
1✔
1537
        let agg_pub_key = script.execute(&inputs).unwrap();
1✔
1538
        assert_eq!(
1✔
1539
            agg_pub_key,
1540
            StackItem::PublicKey(CompressedKey::<RistrettoPublicKey>::new_from_pk(
1✔
1541
                &p_bob.clone().to_public_key().unwrap() + &p_carol.clone().to_public_key().unwrap()
1✔
1542
            ))
1✔
1543
        );
1544

1545
        let inputs = inputs!(s_carol.clone(), s_bob.clone());
1✔
1546
        let err = script.execute(&inputs).unwrap_err();
1✔
1547
        assert_eq!(err, ScriptError::VerifyFailed);
1✔
1548

1549
        let inputs = inputs!(s_alice.clone(), s_bob.clone(), s_carol.clone());
1✔
1550
        let err = script.execute(&inputs).unwrap_err();
1✔
1551
        assert_eq!(err, ScriptError::NonUnitLengthStack);
1✔
1552

1553
        let inputs = inputs!(p_bob.clone());
1✔
1554
        let err = script.execute(&inputs).unwrap_err();
1✔
1555
        assert_eq!(err, ScriptError::StackUnderflow);
1✔
1556

1557
        // 3 of 3
1558
        let keys = vec![p_alice.clone(), p_bob.clone(), p_carol];
1✔
1559
        let ops = vec![CheckMultiSigVerify(3, 3, keys, msg.clone())];
1✔
1560
        let script = TariScript::new(ops).unwrap();
1✔
1561

1562
        let inputs = inputs!(Number(1), s_alice.clone(), s_bob.clone(), s_carol.clone());
1✔
1563
        let result = script.execute(&inputs).unwrap();
1✔
1564
        assert_eq!(result, Number(1));
1✔
1565
        let inputs = inputs!(Number(1), s_bob.clone(), s_alice.clone(), s_carol.clone());
1✔
1566
        let err = script.execute(&inputs).unwrap_err();
1✔
1567
        assert_eq!(err, ScriptError::VerifyFailed);
1✔
1568
        let inputs = inputs!(Number(1), s_eve.clone(), s_bob.clone(), s_carol);
1✔
1569
        let err = script.execute(&inputs).unwrap_err();
1✔
1570
        assert_eq!(err, ScriptError::VerifyFailed);
1✔
1571
        let inputs = inputs!(Number(1), s_eve, s_bob);
1✔
1572
        let err = script.execute(&inputs).unwrap_err();
1✔
1573
        assert_eq!(err, ScriptError::IncompatibleTypes);
1✔
1574

1575
        // errors
1576
        let keys = vec![p_alice.clone(), p_bob.clone()];
1✔
1577
        let ops = vec![CheckMultiSigVerify(0, 2, keys, msg.clone())];
1✔
1578
        let script = TariScript::new(ops).unwrap();
1✔
1579
        let inputs = inputs!(s_alice.clone());
1✔
1580
        let err = script.execute(&inputs).unwrap_err();
1✔
1581
        assert_eq!(err, ScriptError::ValueExceedsBounds);
1✔
1582

1583
        let keys = vec![p_alice.clone(), p_bob.clone()];
1✔
1584
        let ops = vec![CheckMultiSigVerify(1, 0, keys, msg.clone())];
1✔
1585
        let script = TariScript::new(ops).unwrap();
1✔
1586
        let inputs = inputs!(s_alice.clone());
1✔
1587
        let err = script.execute(&inputs).unwrap_err();
1✔
1588
        assert_eq!(err, ScriptError::ValueExceedsBounds);
1✔
1589

1590
        let keys = vec![p_alice, p_bob];
1✔
1591
        let ops = vec![CheckMultiSigVerify(2, 1, keys, msg)];
1✔
1592
        let script = TariScript::new(ops).unwrap();
1✔
1593
        let inputs = inputs!(s_alice);
1✔
1594
        let err = script.execute(&inputs).unwrap_err();
1✔
1595
        assert_eq!(err, ScriptError::ValueExceedsBounds);
1✔
1596

1597
        // 3 of 4
1598
        let (msg, data) = multisig_data(4);
1✔
1599
        let keys = vec![
1✔
1600
            data[0].1.clone(),
1✔
1601
            data[1].1.clone(),
1✔
1602
            data[2].1.clone(),
1✔
1603
            data[3].1.clone(),
1✔
1604
        ];
1605
        let ops = vec![CheckMultiSigVerify(3, 4, keys, msg)];
1✔
1606
        let script = TariScript::new(ops).unwrap();
1✔
1607
        let inputs = inputs!(Number(1), data[0].2.clone(), data[1].2.clone(), data[2].2.clone());
1✔
1608
        let result = script.execute(&inputs).unwrap();
1✔
1609
        assert_eq!(result, Number(1));
1✔
1610

1611
        // 5 of 7
1612
        let (msg, data) = multisig_data(7);
1✔
1613
        let keys = vec![
1✔
1614
            data[0].1.clone(),
1✔
1615
            data[1].1.clone(),
1✔
1616
            data[2].1.clone(),
1✔
1617
            data[3].1.clone(),
1✔
1618
            data[4].1.clone(),
1✔
1619
            data[5].1.clone(),
1✔
1620
            data[6].1.clone(),
1✔
1621
        ];
1622
        let ops = vec![CheckMultiSigVerify(5, 7, keys, msg)];
1✔
1623
        let script = TariScript::new(ops).unwrap();
1✔
1624
        let inputs = inputs!(
1✔
1625
            Number(1),
1✔
1626
            data[0].2.clone(),
1✔
1627
            data[1].2.clone(),
1✔
1628
            data[2].2.clone(),
1✔
1629
            data[3].2.clone(),
1✔
1630
            data[4].2.clone()
1✔
1631
        );
1632
        let result = script.execute(&inputs).unwrap();
1✔
1633
        assert_eq!(result, Number(1));
1✔
1634
    }
1✔
1635

1636
    #[test]
1637
    fn pay_to_public_key_hash() {
1✔
1638
        use crate::StackItem::PublicKey;
1639
        let k =
1✔
1640
            RistrettoSecretKey::from_hex("7212ac93ee205cdbbb57c4f0f815fbf8db25b4d04d3532e2262e31907d82c700").unwrap();
1✔
1641
        let p = CompressedKey::<RistrettoPublicKey>::from_secret_key(&k); // 56c0fa32558d6edc0916baa26b48e745de834571534ca253ea82435f08ebbc7c
1✔
1642
        let hash = Blake2b::<U32>::digest(p.as_bytes());
1✔
1643
        let pkh = slice_to_boxed_hash(hash.as_slice()); // ae2337ce44f9ebb6169c863ec168046cb35ab4ef7aa9ed4f5f1f669bb74b09e5
1✔
1644

1645
        // Unlike in Bitcoin where P2PKH includes a CheckSig at the end of the script, that part of the process is built
1646
        // into definition of how TariScript is evaluated by a base node or wallet
1647
        let script = script!(Dup HashBlake256 PushHash(pkh) EqualVerify).unwrap();
1✔
1648
        let hex_script = "71b07aae2337ce44f9ebb6169c863ec168046cb35ab4ef7aa9ed4f5f1f669bb74b09e581";
1✔
1649
        // Test serialisation
1650
        assert_eq!(script.to_hex(), hex_script);
1✔
1651
        // Test de-serialisation
1652
        assert_eq!(TariScript::from_hex(hex_script).unwrap(), script);
1✔
1653

1654
        let inputs = inputs!(p.clone());
1✔
1655

1656
        let result = script.execute(&inputs).unwrap();
1✔
1657

1658
        assert_eq!(result, PublicKey(p));
1✔
1659
    }
1✔
1660

1661
    #[test]
1662
    fn hex_roundtrip() {
1✔
1663
        // Generate a signature
1664
        let mut rng = rand::thread_rng();
1✔
1665
        let (secret_key, public_key) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1666
        let message = [1u8; 32];
1✔
1667
        let sig = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1668
            CheckSigSchnorrSignature::sign(&secret_key, message, &mut rng).unwrap(),
1✔
1669
        );
1670

1671
        // Produce a script using the signature
1672
        let script = script!(CheckSig(slice_to_boxed_message(message.as_bytes()))).unwrap();
1✔
1673

1674
        // Produce input satisfying the script
1675
        let input = inputs!(sig, public_key);
1✔
1676

1677
        // Check that script execution succeeds
1678
        assert_eq!(script.execute(&input).unwrap(), StackItem::Number(1));
1✔
1679

1680
        // Convert the script to hex and back
1681
        let parsed_script = TariScript::from_hex(script.to_hex().as_str()).unwrap();
1✔
1682
        assert_eq!(script.to_opcodes(), parsed_script.to_opcodes());
1✔
1683

1684
        // Convert the input to hex and back
1685
        let parsed_input = ExecutionStack::from_hex(input.to_hex().as_str()).unwrap();
1✔
1686
        assert_eq!(input, parsed_input);
1✔
1687

1688
        // Check that script execution still succeeds
1689
        assert_eq!(parsed_script.execute(&parsed_input).unwrap(), StackItem::Number(1));
1✔
1690
    }
1✔
1691

1692
    #[test]
1693
    fn disassemble() {
1✔
1694
        let hex_script = "71b07aae2337ce44f9ebb6169c863ec168046cb35ab4ef7aa9ed4f5f1f669bb74b09e58170ac276657a418820f34036b20ea615302b373c70ac8feab8d30681a3e0f0960e708";
1✔
1695
        let script = TariScript::from_hex(hex_script).unwrap();
1✔
1696
        let ops = vec![
1✔
1697
            "Dup",
1698
            "HashBlake256",
1✔
1699
            "PushHash(ae2337ce44f9ebb6169c863ec168046cb35ab4ef7aa9ed4f5f1f669bb74b09e5)",
1✔
1700
            "EqualVerify",
1✔
1701
            "Drop",
1✔
1702
            "CheckSig(276657a418820f34036b20ea615302b373c70ac8feab8d30681a3e0f0960e708)",
1✔
1703
        ]
1704
        .into_iter()
1✔
1705
        .map(String::from)
1✔
1706
        .collect::<Vec<String>>();
1✔
1707
        assert_eq!(script.to_opcodes(), ops);
1✔
1708
        assert_eq!(
1✔
1709
            script.to_string(),
1✔
1710
            "Dup HashBlake256 PushHash(ae2337ce44f9ebb6169c863ec168046cb35ab4ef7aa9ed4f5f1f669bb74b09e5) EqualVerify \
1711
             Drop CheckSig(276657a418820f34036b20ea615302b373c70ac8feab8d30681a3e0f0960e708)"
1712
        );
1713
    }
1✔
1714

1715
    #[test]
1716
    fn time_locked_contract_example() {
1✔
1717
        let k_alice =
1✔
1718
            RistrettoSecretKey::from_hex("f305e64c0e73cbdb665165ac97b69e5df37b2cd81f9f8f569c3bd854daff290e").unwrap();
1✔
1719
        let p_alice = CompressedKey::<RistrettoPublicKey>::from_secret_key(&k_alice); // 9c35e9f0f11cf25ce3ca1182d37682ab5824aa033f2024651e007364d06ec355
1✔
1720

1721
        let k_bob =
1✔
1722
            RistrettoSecretKey::from_hex("e0689386a018e88993a7bb14cbff5bad8a8858ea101d6e0da047df3ddf499c0e").unwrap();
1✔
1723
        let p_bob = CompressedKey::<RistrettoPublicKey>::from_secret_key(&k_bob); // 3a58f371e94da76a8902e81b4b55ddabb7dc006cd8ebde3011c46d0e02e9172f
1✔
1724

1725
        let lock_height = 4000u64;
1✔
1726

1727
        let script = script!(Dup PushPubKey(Box::new(p_bob.clone())) CheckHeight(lock_height) GeZero IfThen PushPubKey(Box::new(p_alice.clone())) OrVerify(2) Else EqualVerify EndIf ).unwrap();
1✔
1728

1729
        // Alice tries to spend the output before the height is reached
1730
        let inputs_alice_spends_early = inputs!(p_alice.clone());
1✔
1731
        let ctx = context_with_height(3990u64);
1✔
1732
        assert_eq!(
1✔
1733
            script.execute_with_context(&inputs_alice_spends_early, &ctx),
1✔
1734
            Err(ScriptError::VerifyFailed)
1735
        );
1736

1737
        // Alice tries to spend the output after the height is reached
1738
        let inputs_alice_spends_early = inputs!(p_alice.clone());
1✔
1739
        let ctx = context_with_height(4000u64);
1✔
1740
        assert_eq!(
1✔
1741
            script.execute_with_context(&inputs_alice_spends_early, &ctx).unwrap(),
1✔
1742
            StackItem::PublicKey(p_alice)
1✔
1743
        );
1744

1745
        // Bob spends before time lock is reached
1746
        let inputs_bob_spends_early = inputs!(p_bob.clone());
1✔
1747
        let ctx = context_with_height(3990u64);
1✔
1748
        assert_eq!(
1✔
1749
            script.execute_with_context(&inputs_bob_spends_early, &ctx).unwrap(),
1✔
1750
            StackItem::PublicKey(p_bob.clone())
1✔
1751
        );
1752

1753
        // Bob spends after time lock is reached
1754
        let inputs_bob_spends_early = inputs!(p_bob.clone());
1✔
1755
        let ctx = context_with_height(4001u64);
1✔
1756
        assert_eq!(
1✔
1757
            script.execute_with_context(&inputs_bob_spends_early, &ctx).unwrap(),
1✔
1758
            StackItem::PublicKey(p_bob)
1✔
1759
        );
1760
    }
1✔
1761

1762
    #[test]
1763
    fn m_of_n_signatures() {
1✔
1764
        use crate::StackItem::PublicKey;
1765
        let mut rng = rand::thread_rng();
1✔
1766
        let (k_alice, p_alice) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1767
        let (k_bob, p_bob) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1768
        let (k_eve, _) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1769

1770
        let m = RistrettoSecretKey::random(&mut rng);
1✔
1771
        let msg = slice_to_boxed_message(m.as_bytes());
1✔
1772

1773
        let s_alice = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1774
            CheckSigSchnorrSignature::sign(&k_alice, m.as_bytes(), &mut rng).unwrap(),
1✔
1775
        );
1776
        let s_bob = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1777
            CheckSigSchnorrSignature::sign(&k_bob, m.as_bytes(), &mut rng).unwrap(),
1✔
1778
        );
1779
        let s_eve = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1780
            CheckSigSchnorrSignature::sign(&k_eve, m.as_bytes(), &mut rng).unwrap(),
1✔
1781
        );
1782

1783
        // 1 of 2
1784
        use crate::Opcode::{CheckSig, Drop, Dup, Else, EndIf, IfThen, PushPubKey, Return};
1785
        let ops = vec![
1✔
1786
            Dup,
1✔
1787
            PushPubKey(Box::new(p_alice.clone())),
1✔
1788
            CheckSig(msg.clone()),
1✔
1789
            IfThen,
1✔
1790
            Drop,
1✔
1791
            PushPubKey(Box::new(p_alice.clone())),
1✔
1792
            Else,
1✔
1793
            PushPubKey(Box::new(p_bob.clone())),
1✔
1794
            CheckSig(msg),
1✔
1795
            IfThen,
1✔
1796
            PushPubKey(Box::new(p_bob.clone())),
1✔
1797
            Else,
1✔
1798
            Return,
1✔
1799
            EndIf,
1✔
1800
            EndIf,
1✔
1801
        ];
1802
        let script = TariScript::new(ops).unwrap();
1✔
1803

1804
        // alice
1805
        let inputs = inputs!(s_alice);
1✔
1806
        let result = script.execute(&inputs).unwrap();
1✔
1807
        assert_eq!(result, PublicKey(p_alice));
1✔
1808

1809
        // bob
1810
        let inputs = inputs!(s_bob);
1✔
1811
        let result = script.execute(&inputs).unwrap();
1✔
1812
        assert_eq!(result, PublicKey(p_bob));
1✔
1813

1814
        // eve
1815
        let inputs = inputs!(s_eve);
1✔
1816
        let result = script.execute(&inputs).unwrap_err();
1✔
1817
        assert_eq!(result, ScriptError::Return);
1✔
1818
    }
1✔
1819

1820
    #[test]
1821
    fn to_ristretto_point() {
1✔
1822
        use crate::{Opcode::ToRistrettoPoint, StackItem::PublicKey};
1823

1824
        // Generate a key pair
1825
        let mut rng = rand::thread_rng();
1✔
1826
        let (k_1, p_1) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1827

1828
        // Generate a test script
1829
        let ops = vec![ToRistrettoPoint];
1✔
1830
        let script = TariScript::new(ops).unwrap();
1✔
1831

1832
        // Invalid stack type
1833
        let inputs = inputs!(CompressedKey::<RistrettoPublicKey>::default());
1✔
1834
        let err = script.execute(&inputs).unwrap_err();
1✔
1835
        assert!(matches!(err, ScriptError::IncompatibleTypes));
1✔
1836

1837
        // Valid scalar
1838
        let mut scalar = [0u8; 32];
1✔
1839
        scalar.copy_from_slice(k_1.as_bytes());
1✔
1840
        let inputs = inputs!(scalar);
1✔
1841
        let result = script.execute(&inputs).unwrap();
1✔
1842
        assert_eq!(result, PublicKey(p_1.clone()));
1✔
1843

1844
        // Valid hash
1845
        let inputs = ExecutionStack::new(vec![Hash(scalar)]);
1✔
1846
        let result = script.execute(&inputs).unwrap();
1✔
1847
        assert_eq!(result, PublicKey(p_1));
1✔
1848

1849
        // Invalid bytes
1850
        let invalid = [u8::MAX; 32]; // not a canonical scalar encoding!
1✔
1851
        let inputs = inputs!(invalid);
1✔
1852
        assert!(matches!(script.execute(&inputs), Err(ScriptError::InvalidInput)));
1✔
1853
    }
1✔
1854

1855
    #[test]
1856
    fn test_borsh_de_serialization() {
1✔
1857
        let hex_script = "71b07aae2337ce44f9ebb6169c863ec168046cb35ab4ef7aa9ed4f5f1f669bb74b09e58170ac276657a418820f34036b20ea615302b373c70ac8feab8d30681a3e0f0960e708";
1✔
1858
        let script = TariScript::from_hex(hex_script).unwrap();
1✔
1859
        let mut buf = Vec::new();
1✔
1860
        script.serialize(&mut buf).unwrap();
1✔
1861
        buf.extend_from_slice(&[1, 2, 3]);
1✔
1862
        let buf = &mut buf.as_slice();
1✔
1863
        assert_eq!(script, TariScript::deserialize(buf).unwrap());
1✔
1864
        assert_eq!(buf, &[1, 2, 3]);
1✔
1865
    }
1✔
1866

1867
    #[test]
1868
    fn test_borsh_de_serialization_too_large() {
1✔
1869
        // We dont care about the actual script here, just that its not too large on the varint size
1870
        // We lie about the size to try and get a mem panic, and say this script is u64::max large.
1871
        let buf = vec![255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 49, 8, 2, 5, 6];
1✔
1872
        let buf = &mut buf.as_slice();
1✔
1873
        assert!(TariScript::deserialize(buf).is_err());
1✔
1874
    }
1✔
1875

1876
    #[test]
1877
    fn test_compare_height_block_height_exceeds_bounds() {
1✔
1878
        let script = script!(CompareHeight).unwrap();
1✔
1879

1880
        let inputs = inputs!(0);
1✔
1881
        let ctx = context_with_height(u64::MAX);
1✔
1882
        let stack_item = script.execute_with_context(&inputs, &ctx);
1✔
1883
        assert!(matches!(stack_item, Err(ScriptError::ValueExceedsBounds)));
1✔
1884
    }
1✔
1885

1886
    #[test]
1887
    fn test_compare_height_underflows() {
1✔
1888
        let script = script!(CompareHeight).unwrap();
1✔
1889

1890
        let inputs = ExecutionStack::new(vec![Number(i64::MIN)]);
1✔
1891
        let ctx = context_with_height(i64::MAX as u64);
1✔
1892
        let stack_item = script.execute_with_context(&inputs, &ctx);
1✔
1893
        assert!(matches!(stack_item, Err(ScriptError::CompareFailed(_))));
1✔
1894
    }
1✔
1895

1896
    #[test]
1897
    fn test_compare_height_underflows_on_empty_stack() {
1✔
1898
        let script = script!(CompareHeight).unwrap();
1✔
1899

1900
        let inputs = ExecutionStack::new(vec![]);
1✔
1901
        let ctx = context_with_height(i64::MAX as u64);
1✔
1902
        let stack_item = script.execute_with_context(&inputs, &ctx);
1✔
1903
        assert!(matches!(stack_item, Err(ScriptError::StackUnderflow)));
1✔
1904
    }
1✔
1905

1906
    #[test]
1907
    fn test_compare_height_valid_with_uint_result() {
1✔
1908
        let script = script!(CompareHeight).unwrap();
1✔
1909

1910
        let inputs = inputs!(100);
1✔
1911
        let ctx = context_with_height(24_u64);
1✔
1912
        let stack_item = script.execute_with_context(&inputs, &ctx);
1✔
1913
        assert!(stack_item.is_ok());
1✔
1914
        assert_eq!(stack_item.unwrap(), Number(-76))
1✔
1915
    }
1✔
1916

1917
    #[test]
1918
    fn test_compare_height_valid_with_int_result() {
1✔
1919
        let script = script!(CompareHeight).unwrap();
1✔
1920

1921
        let inputs = inputs!(100);
1✔
1922
        let ctx = context_with_height(110_u64);
1✔
1923
        let stack_item = script.execute_with_context(&inputs, &ctx);
1✔
1924
        assert!(stack_item.is_ok());
1✔
1925
        assert_eq!(stack_item.unwrap(), Number(10))
1✔
1926
    }
1✔
1927

1928
    #[test]
1929
    fn test_check_height_block_height_exceeds_bounds() {
1✔
1930
        let script = script!(CheckHeight(0)).unwrap();
1✔
1931

1932
        let inputs = ExecutionStack::new(vec![]);
1✔
1933
        let ctx = context_with_height(u64::MAX);
1✔
1934
        let stack_item = script.execute_with_context(&inputs, &ctx);
1✔
1935
        assert!(matches!(stack_item, Err(ScriptError::ValueExceedsBounds)));
1✔
1936
    }
1✔
1937

1938
    #[test]
1939
    fn test_check_height_exceeds_bounds() {
1✔
1940
        let script = script!(CheckHeight(u64::MAX)).unwrap();
1✔
1941

1942
        let inputs = ExecutionStack::new(vec![]);
1✔
1943
        let ctx = context_with_height(10_u64);
1✔
1944
        let stack_item = script.execute_with_context(&inputs, &ctx);
1✔
1945
        assert!(matches!(stack_item, Err(ScriptError::ValueExceedsBounds)));
1✔
1946
    }
1✔
1947

1948
    #[test]
1949
    fn test_check_height_overflows_on_max_stack() {
1✔
1950
        let script = script!(CheckHeight(0)).unwrap();
1✔
1951

1952
        let mut inputs = ExecutionStack::new(vec![]);
1✔
1953

1954
        for i in 0..255 {
255✔
1955
            inputs.push(Number(i)).unwrap();
255✔
1956
        }
255✔
1957

1958
        let ctx = context_with_height(i64::MAX as u64);
1✔
1959
        let stack_item = script.execute_with_context(&inputs, &ctx);
1✔
1960
        assert!(matches!(stack_item, Err(ScriptError::StackOverflow)));
1✔
1961
    }
1✔
1962

1963
    #[test]
1964
    fn test_check_height_valid_with_uint_result() {
1✔
1965
        let script = script!(CheckHeight(24)).unwrap();
1✔
1966

1967
        let inputs = ExecutionStack::new(vec![]);
1✔
1968
        let ctx = context_with_height(100_u64);
1✔
1969
        let stack_item = script.execute_with_context(&inputs, &ctx);
1✔
1970
        assert!(stack_item.is_ok());
1✔
1971
        assert_eq!(stack_item.unwrap(), Number(76))
1✔
1972
    }
1✔
1973

1974
    #[test]
1975
    fn test_check_height_valid_with_int_result() {
1✔
1976
        let script = script!(CheckHeight(100)).unwrap();
1✔
1977

1978
        let inputs = ExecutionStack::new(vec![]);
1✔
1979
        let ctx = context_with_height(24_u64);
1✔
1980
        let stack_item = script.execute_with_context(&inputs, &ctx);
1✔
1981
        assert!(stack_item.is_ok());
1✔
1982
        assert_eq!(stack_item.unwrap(), Number(-76))
1✔
1983
    }
1✔
1984
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc