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

tari-project / tari / 16200110666

10 Jul 2025 04:01PM UTC coverage: 58.097% (-6.2%) from 64.296%
16200110666

push

github

web-flow
chore: fix tests (#7300)

Description
---
removes uncalled tests,
fixes some tests

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added the ability to save the last scanned blockchain height in the
output manager's storage backend.

* **Refactor**
* Generalized wallet service initializers to support custom HTTP client
factories, improving flexibility for connectivity and transaction
services.
* Updated wallet startup and service initialization to use new generic
parameters for HTTP client factories.

* **Bug Fixes**
* Corrected balance assertions in tests to reflect updated time-locked
balance handling.
* Fixed the `submit_transaction` method in the HTTP base node mock to
return a valid response.

* **Tests**
* Removed comprehensive test suites for transaction protocols and UTXO
scanner services.
* Updated and streamlined test setups to match new service initializer
signatures and database usage.
  * Marked a broken transaction service test as ignored.
* Removed unused module declarations and imports in integration and
wallet tests.

* **Chores**
* Removed deprecated FFI function and related wallet methods for adding
base node peers.
* Removed step definitions related to ffi wallet lifecycle and
connectivity management in integration tests.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

0 of 33 new or added lines in 6 files covered. (0.0%)

7429 existing lines in 95 files now uncovered.

69707 of 119984 relevant lines covered (58.1%)

226952.75 hits per line

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

97.36
/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::{consts::U32, Digest};
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
    hex::{from_hex, to_hex, Hex, HexError},
35
    ByteArray,
36
};
37

38
use crate::{
39
    op_codes::Message,
40
    slice_to_hash,
41
    CompressedCheckSigSchnorrSignature,
42
    ExecutionStack,
43
    HashValue,
44
    Opcode,
45
    ScriptContext,
46
    ScriptError,
47
    StackItem,
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<()> {
31,766✔
73
        let bytes = self.to_bytes();
31,766✔
74
        writer.write_varint(bytes.len())?;
31,766✔
75
        for b in &bytes {
3,279,530✔
76
            b.serialize(writer)?;
3,247,764✔
77
        }
78
        Ok(())
31,766✔
79
    }
31,766✔
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> {
1,284✔
104
        let script = ScriptOpcodes::try_from(script)?;
1,284✔
105
        Ok(TariScript { script })
1,284✔
106
    }
1,284✔
107

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

128
    /// Retrieve the opcode at the index, returns None if the index does not exist
129
    pub fn opcode(&self, i: usize) -> Option<&Opcode> {
9✔
130
        if i >= self.script.len() {
9✔
131
            return None;
3✔
132
        }
6✔
133
        Some(&self.script[i])
6✔
134
    }
9✔
135

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

141
    /// Execute the script with the given inputs and the provided context. If successful, returns the final stack item.
142
    pub fn execute_with_context(
197✔
143
        &self,
197✔
144
        inputs: &ExecutionStack,
197✔
145
        context: &ScriptContext,
197✔
146
    ) -> Result<StackItem, ScriptError> {
197✔
147
        // Copy all inputs onto the stack
197✔
148
        let mut stack = inputs.clone();
197✔
149

197✔
150
        // Local execution state
197✔
151
        let mut state = ExecutionState::default();
197✔
152

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

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

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

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

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

191
    pub fn to_bytes(&self) -> Vec<u8> {
38,846✔
192
        self.script.iter().fold(Vec::new(), |mut bytes, op| {
169,846✔
193
            op.to_bytes(&mut bytes);
169,846✔
194
            bytes
169,846✔
195
        })
169,846✔
196
    }
38,846✔
197

198
    pub fn as_slice(&self) -> &[Opcode] {
552✔
199
        self.script.as_ref()
552✔
200
    }
552✔
201

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

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

217
        Ok(TariScript { script })
9,236✔
218
    }
9,236✔
219

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

50✔
648
        let mut agg_pub_key = RistrettoPublicKey::default();
50✔
649

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

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

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

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

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

700
impl Hex for TariScript {
701
    fn from_hex(hex: &str) -> Result<Self, HexError>
5✔
702
    where Self: Sized {
5✔
703
        let bytes = from_hex(hex)?;
5✔
704
        TariScript::from_bytes(&bytes).map_err(|_| HexError::HexConversionError {})
5✔
705
    }
5✔
706

707
    fn to_hex(&self) -> String {
3✔
708
        to_hex(&self.to_bytes())
3✔
709
    }
3✔
710
}
711

712
/// The default Tari script is to push a sender pubkey onto the stack
713
impl Default for TariScript {
714
    fn default() -> Self {
149✔
715
        script!(PushPubKey(Box::default())).expect("default will not fail")
149✔
716
    }
149✔
717
}
718

719
impl fmt::Display for TariScript {
720
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1✔
721
        let s = self.to_opcodes().join(" ");
1✔
722
        f.write_str(&s)
1✔
723
    }
1✔
724
}
725

726
#[derive(Clone, Debug, PartialEq, Eq)]
727
enum Branch {
728
    NotExecuted,
729
    ExecuteIf,
730
    ExecuteElse,
731
}
732

733
#[derive(Clone, Debug, PartialEq, Eq)]
734
struct IfState {
735
    branch: Branch,
736
    else_expected: bool,
737
}
738

739
#[derive(Clone, Debug, PartialEq, Eq)]
740
struct ExecutionState {
741
    executing: bool,
742
    if_stack: Vec<IfState>,
743
}
744

745
impl Default for ExecutionState {
746
    fn default() -> Self {
197✔
747
        Self {
197✔
748
            executing: true,
197✔
749
            if_stack: Vec::new(),
197✔
750
        }
197✔
751
    }
197✔
752
}
753

754
#[cfg(test)]
755
mod test {
756
    use blake2::Blake2b;
757
    use borsh::{BorshDeserialize, BorshSerialize};
758
    use digest::{consts::U32, Digest};
759
    use sha2::Sha256;
760
    use sha3::Sha3_256 as Sha3;
761
    use tari_crypto::{
762
        compressed_commitment::CompressedCommitment,
763
        compressed_key::CompressedKey,
764
        keys::SecretKey,
765
        ristretto::{pedersen::CompressedPedersenCommitment, RistrettoPublicKey, RistrettoSecretKey},
766
    };
767
    use tari_utilities::{hex::Hex, ByteArray};
768

769
    use crate::{
770
        error::ScriptError,
771
        inputs,
772
        op_codes::{slice_to_boxed_hash, slice_to_boxed_message, HashValue, Message},
773
        CheckSigSchnorrSignature,
774
        CompressedCheckSigSchnorrSignature,
775
        ExecutionStack,
776
        Opcode::CheckMultiSigVerifyAggregatePubKey,
777
        ScriptContext,
778
        StackItem,
779
        StackItem::{Commitment, Hash, Number},
780
        TariScript,
781
    };
782

783
    fn context_with_height(height: u64) -> ScriptContext {
55✔
784
        ScriptContext::new(height, &HashValue::default(), &CompressedPedersenCommitment::default())
55✔
785
    }
55✔
786

787
    #[test]
788
    fn pattern_match() {
1✔
789
        let script_a = script!(Or(1)).unwrap();
1✔
790
        let script_b = script!(Or(1)).unwrap();
1✔
791
        assert_eq!(script_a, script_b);
1✔
792
        assert!(script_a.pattern_match(&script_b));
1✔
793

794
        let script_b = script!(Or(2)).unwrap();
1✔
795
        assert_ne!(script_a, script_b);
1✔
796
        assert!(script_a.pattern_match(&script_b));
1✔
797

798
        let script_b = script!(Or(2) Or(2)).unwrap();
1✔
799
        assert_ne!(script_a, script_b);
1✔
800
        assert!(!script_a.pattern_match(&script_b));
1✔
801

802
        let script_a = script!(Or(2) Or(1)).unwrap();
1✔
803
        let script_b = script!(Or(3) Or(5)).unwrap();
1✔
804
        assert_ne!(script_a, script_b);
1✔
805
        assert!(script_a.pattern_match(&script_b));
1✔
806
    }
1✔
807

808
    #[test]
809
    fn op_or() {
1✔
810
        let script = script!(Or(1)).unwrap();
1✔
811

1✔
812
        let inputs = inputs!(4, 4);
1✔
813
        let result = script.execute(&inputs).unwrap();
1✔
814
        assert_eq!(result, Number(1));
1✔
815

816
        let inputs = inputs!(3, 4);
1✔
817
        let result = script.execute(&inputs).unwrap();
1✔
818
        assert_eq!(result, Number(0));
1✔
819

820
        let script = script!(Or(3)).unwrap();
1✔
821

1✔
822
        let inputs = inputs!(1, 2, 1, 3);
1✔
823
        let result = script.execute(&inputs).unwrap();
1✔
824
        assert_eq!(result, Number(1));
1✔
825

826
        let inputs = inputs!(1, 2, 4, 3);
1✔
827
        let result = script.execute(&inputs).unwrap();
1✔
828
        assert_eq!(result, Number(0));
1✔
829

830
        let mut rng = rand::thread_rng();
1✔
831
        let (_, p) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
832
        let inputs = inputs!(1, p.clone(), 1, 3);
1✔
833
        let err = script.execute(&inputs).unwrap_err();
1✔
834
        assert!(matches!(err, ScriptError::InvalidInput));
1✔
835

836
        let inputs = inputs!(p, 2, 1, 3);
1✔
837
        let err = script.execute(&inputs).unwrap_err();
1✔
838
        assert!(matches!(err, ScriptError::InvalidInput));
1✔
839

840
        let inputs = inputs!(2, 4, 3);
1✔
841
        let err = script.execute(&inputs).unwrap_err();
1✔
842
        assert!(matches!(err, ScriptError::StackUnderflow));
1✔
843

844
        let script = script!(OrVerify(1)).unwrap();
1✔
845

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

850
        let inputs = inputs!(1, 3, 4);
1✔
851
        let err = script.execute(&inputs).unwrap_err();
1✔
852
        assert!(matches!(err, ScriptError::VerifyFailed));
1✔
853

854
        let script = script!(OrVerify(2)).unwrap();
1✔
855

1✔
856
        let inputs = inputs!(1, 2, 2, 3);
1✔
857
        let result = script.execute(&inputs).unwrap();
1✔
858
        assert_eq!(result, Number(1));
1✔
859

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

865
    #[test]
866
    fn op_if_then_else() {
1✔
867
        // basic
1✔
868
        let script = script!(IfThen PushInt(420) Else PushInt(66) EndIf).unwrap();
1✔
869
        let inputs = inputs!(1);
1✔
870
        let result = script.execute(&inputs);
1✔
871
        assert_eq!(result.unwrap(), Number(420));
1✔
872

873
        let inputs = inputs!(0);
1✔
874
        let result = script.execute(&inputs);
1✔
875
        assert_eq!(result.unwrap(), Number(66));
1✔
876

877
        // nested
878
        let script =
1✔
879
            script!(IfThen PushOne IfThen PushInt(420) Else PushInt(555) EndIf Else PushInt(66) EndIf).unwrap();
1✔
880
        let inputs = inputs!(1);
1✔
881
        let result = script.execute(&inputs);
1✔
882
        assert_eq!(result.unwrap(), Number(420));
1✔
883

884
        let script =
1✔
885
            script!(IfThen PushInt(420) Else PushZero IfThen PushInt(111) Else PushInt(66) EndIf Nop EndIf).unwrap();
1✔
886
        let inputs = inputs!(0);
1✔
887
        let result = script.execute(&inputs);
1✔
888
        assert_eq!(result.unwrap(), Number(66));
1✔
889

890
        // duplicate else
891
        let script = script!(IfThen PushInt(420) Else PushInt(66) Else PushInt(777) EndIf).unwrap();
1✔
892
        let inputs = inputs!(0);
1✔
893
        let result = script.execute(&inputs);
1✔
894
        assert_eq!(result.unwrap_err(), ScriptError::InvalidOpcode);
1✔
895

896
        // unexpected else
897
        let script = script!(Else).unwrap();
1✔
898
        let inputs = inputs!(0);
1✔
899
        let result = script.execute(&inputs);
1✔
900
        assert_eq!(result.unwrap_err(), ScriptError::InvalidOpcode);
1✔
901

902
        // unexpected endif
903
        let script = script!(EndIf).unwrap();
1✔
904
        let inputs = inputs!(0);
1✔
905
        let result = script.execute(&inputs);
1✔
906
        assert_eq!(result.unwrap_err(), ScriptError::InvalidOpcode);
1✔
907

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

914
        // no else or endif
915
        let script = script!(IfThen PushOne IfThen PushOne).unwrap();
1✔
916
        let inputs = inputs!(1);
1✔
917
        let result = script.execute(&inputs);
1✔
918
        assert_eq!(result.unwrap_err(), ScriptError::MissingOpcode);
1✔
919

920
        // no else
921
        let script = script!(IfThen PushOne EndIf).unwrap();
1✔
922
        let inputs = inputs!(1);
1✔
923
        let result = script.execute(&inputs);
1✔
924
        assert_eq!(result.unwrap_err(), ScriptError::MissingOpcode);
1✔
925

926
        // nested bug
927
        let script =
1✔
928
            script!(IfThen PushInt(111) Else PushZero IfThen PushInt(222) Else PushInt(333) EndIf EndIf).unwrap();
1✔
929
        let inputs = inputs!(1);
1✔
930
        let result = script.execute(&inputs);
1✔
931
        assert_eq!(result.unwrap(), Number(111));
1✔
932
    }
1✔
933

934
    #[test]
935
    fn op_check_height() {
1✔
936
        let inputs = ExecutionStack::default();
1✔
937
        let script = script!(CheckHeight(5)).unwrap();
1✔
938

939
        for block_height in 1..=10 {
11✔
940
            let ctx = context_with_height(u64::try_from(block_height).unwrap());
10✔
941
            assert_eq!(
10✔
942
                script.execute_with_context(&inputs, &ctx).unwrap(),
10✔
943
                Number(block_height - 5)
10✔
944
            );
10✔
945
        }
946

947
        let script = script!(CheckHeight(u64::MAX)).unwrap();
1✔
948
        let ctx = context_with_height(i64::MAX as u64);
1✔
949
        let err = script.execute_with_context(&inputs, &ctx).unwrap_err();
1✔
950
        assert!(matches!(err, ScriptError::ValueExceedsBounds));
1✔
951

952
        let script = script!(CheckHeightVerify(5)).unwrap();
1✔
953
        let inputs = inputs!(1);
1✔
954

955
        for block_height in 1..5 {
5✔
956
            let ctx = context_with_height(block_height);
4✔
957
            let err = script.execute_with_context(&inputs, &ctx).unwrap_err();
4✔
958
            assert!(matches!(err, ScriptError::VerifyFailed));
4✔
959
        }
960

961
        for block_height in 5..=10 {
7✔
962
            let ctx = context_with_height(block_height);
6✔
963
            let result = script.execute_with_context(&inputs, &ctx).unwrap();
6✔
964
            assert_eq!(result, Number(1));
6✔
965
        }
966
    }
1✔
967

968
    #[test]
969
    fn op_compare_height() {
1✔
970
        let script = script!(CompareHeight).unwrap();
1✔
971
        let inputs = inputs!(5);
1✔
972

973
        for block_height in 1..=10 {
11✔
974
            let ctx = context_with_height(u64::try_from(block_height).unwrap());
10✔
975
            assert_eq!(
10✔
976
                script.execute_with_context(&inputs, &ctx).unwrap(),
10✔
977
                Number(block_height - 5)
10✔
978
            );
10✔
979
        }
980

981
        let script = script!(CompareHeightVerify).unwrap();
1✔
982
        let inputs = inputs!(1, 5);
1✔
983

984
        for block_height in 1..5 {
5✔
985
            let ctx = context_with_height(block_height);
4✔
986
            let err = script.execute_with_context(&inputs, &ctx).unwrap_err();
4✔
987
            assert!(matches!(err, ScriptError::VerifyFailed));
4✔
988
        }
989

990
        for block_height in 5..=10 {
7✔
991
            let ctx = context_with_height(block_height);
6✔
992
            let result = script.execute_with_context(&inputs, &ctx).unwrap();
6✔
993
            assert_eq!(result, Number(1));
6✔
994
        }
995
    }
1✔
996

997
    #[test]
998
    fn op_drop_push() {
1✔
999
        let inputs = inputs!(420);
1✔
1000
        let script = script!(Drop PushOne).unwrap();
1✔
1001
        assert_eq!(script.execute(&inputs).unwrap(), Number(1));
1✔
1002

1003
        let script = script!(Drop PushZero).unwrap();
1✔
1004
        assert_eq!(script.execute(&inputs).unwrap(), Number(0));
1✔
1005

1006
        let script = script!(Drop PushInt(5)).unwrap();
1✔
1007
        assert_eq!(script.execute(&inputs).unwrap(), Number(5));
1✔
1008
    }
1✔
1009

1010
    #[test]
1011
    fn op_comparison_to_zero() {
1✔
1012
        let script = script!(GeZero).unwrap();
1✔
1013
        let inputs = inputs!(1);
1✔
1014
        assert_eq!(script.execute(&inputs).unwrap(), Number(1));
1✔
1015
        let inputs = inputs!(0);
1✔
1016
        assert_eq!(script.execute(&inputs).unwrap(), Number(1));
1✔
1017

1018
        let script = script!(GtZero).unwrap();
1✔
1019
        let inputs = inputs!(1);
1✔
1020
        assert_eq!(script.execute(&inputs).unwrap(), Number(1));
1✔
1021
        let inputs = inputs!(0);
1✔
1022
        assert_eq!(script.execute(&inputs).unwrap(), Number(0));
1✔
1023

1024
        let script = script!(LeZero).unwrap();
1✔
1025
        let inputs = inputs!(-1);
1✔
1026
        assert_eq!(script.execute(&inputs).unwrap(), Number(1));
1✔
1027
        let inputs = inputs!(0);
1✔
1028
        assert_eq!(script.execute(&inputs).unwrap(), Number(1));
1✔
1029

1030
        let script = script!(LtZero).unwrap();
1✔
1031
        let inputs = inputs!(-1);
1✔
1032
        assert_eq!(script.execute(&inputs).unwrap(), Number(1));
1✔
1033
        let inputs = inputs!(0);
1✔
1034
        assert_eq!(script.execute(&inputs).unwrap(), Number(0));
1✔
1035
    }
1✔
1036

1037
    #[test]
1038
    fn op_hash() {
1✔
1039
        let mut rng = rand::thread_rng();
1✔
1040
        let (_, p) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1041
        let c = CompressedCommitment::<RistrettoPublicKey>::from_compressed_key(p.clone());
1✔
1042
        let script = script!(HashSha256).unwrap();
1✔
1043

1✔
1044
        let hash = Sha256::digest(p.as_bytes());
1✔
1045
        let inputs = inputs!(p.clone());
1✔
1046
        assert_eq!(script.execute(&inputs).unwrap(), Hash(hash.into()));
1✔
1047

1048
        let hash = Sha256::digest(c.as_bytes());
1✔
1049
        let inputs = inputs!(c.clone());
1✔
1050
        assert_eq!(script.execute(&inputs).unwrap(), Hash(hash.into()));
1✔
1051

1052
        let script = script!(HashSha3).unwrap();
1✔
1053

1✔
1054
        let hash = Sha3::digest(p.as_bytes());
1✔
1055
        let inputs = inputs!(p);
1✔
1056
        assert_eq!(script.execute(&inputs).unwrap(), Hash(hash.into()));
1✔
1057

1058
        let hash = Sha3::digest(c.as_bytes());
1✔
1059
        let inputs = inputs!(c);
1✔
1060
        assert_eq!(script.execute(&inputs).unwrap(), Hash(hash.into()));
1✔
1061
    }
1✔
1062

1063
    #[test]
1064
    fn op_return() {
1✔
1065
        let script = script!(Return).unwrap();
1✔
1066
        let inputs = ExecutionStack::default();
1✔
1067
        assert_eq!(script.execute(&inputs), Err(ScriptError::Return));
1✔
1068
    }
1✔
1069

1070
    #[test]
1071
    fn op_add() {
1✔
1072
        let script = script!(Add).unwrap();
1✔
1073
        let inputs = inputs!(3, 2);
1✔
1074
        assert_eq!(script.execute(&inputs).unwrap(), Number(5));
1✔
1075
        let inputs = inputs!(3, -3);
1✔
1076
        assert_eq!(script.execute(&inputs).unwrap(), Number(0));
1✔
1077
        let inputs = inputs!(i64::MAX, 1);
1✔
1078
        assert_eq!(script.execute(&inputs), Err(ScriptError::ValueExceedsBounds));
1✔
1079
        let inputs = inputs!(1);
1✔
1080
        assert_eq!(script.execute(&inputs), Err(ScriptError::StackUnderflow));
1✔
1081
    }
1✔
1082

1083
    #[test]
1084
    fn op_add_commitments() {
1✔
1085
        let script = script!(Add).unwrap();
1✔
1086
        let mut rng = rand::thread_rng();
1✔
1087
        let (_, c1) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1088
        let (_, c2) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1089
        let c3 = &c1.to_public_key().unwrap() + &c2.to_public_key().unwrap();
1✔
1090
        let c3 = CompressedCommitment::<RistrettoPublicKey>::from_public_key(c3);
1✔
1091
        let inputs = inputs!(
1✔
1092
            CompressedCommitment::<RistrettoPublicKey>::from_compressed_key(c1),
1✔
1093
            CompressedCommitment::<RistrettoPublicKey>::from_compressed_key(c2)
1✔
1094
        );
1✔
1095
        assert_eq!(script.execute(&inputs).unwrap(), Commitment(c3));
1✔
1096
    }
1✔
1097

1098
    #[test]
1099
    fn op_sub() {
1✔
1100
        use crate::StackItem::Number;
1101
        let script = script!(Add Sub).unwrap();
1✔
1102
        let inputs = inputs!(5, 3, 2);
1✔
1103
        assert_eq!(script.execute(&inputs).unwrap(), Number(0));
1✔
1104
        let inputs = inputs!(i64::MAX, 1);
1✔
1105
        assert_eq!(script.execute(&inputs), Err(ScriptError::ValueExceedsBounds));
1✔
1106
        let script = script!(Sub).unwrap();
1✔
1107
        let inputs = inputs!(5, 3);
1✔
1108
        assert_eq!(script.execute(&inputs).unwrap(), Number(2));
1✔
1109
    }
1✔
1110

1111
    #[test]
1112
    fn serialisation() {
1✔
1113
        let script = script!(Add Sub Add).unwrap();
1✔
1114
        assert_eq!(&script.to_bytes(), &[0x93, 0x94, 0x93]);
1✔
1115
        assert_eq!(TariScript::from_bytes(&[0x93, 0x94, 0x93]).unwrap(), script);
1✔
1116
        assert_eq!(script.to_hex(), "939493");
1✔
1117
        assert_eq!(TariScript::from_hex("939493").unwrap(), script);
1✔
1118
    }
1✔
1119

1120
    #[test]
1121
    fn check_sig() {
1✔
1122
        use crate::StackItem::Number;
1123
        let mut rng = rand::thread_rng();
1✔
1124
        let (pvt_key, pub_key) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1125
        let m_key = RistrettoSecretKey::random(&mut rng);
1✔
1126
        let sig = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1127
            CheckSigSchnorrSignature::sign(&pvt_key, m_key.as_bytes(), &mut rng).unwrap(),
1✔
1128
        );
1✔
1129
        let msg = slice_to_boxed_message(m_key.as_bytes());
1✔
1130
        let script = script!(CheckSig(msg)).unwrap();
1✔
1131
        let inputs = inputs!(sig.clone(), pub_key.clone());
1✔
1132
        let result = script.execute(&inputs).unwrap();
1✔
1133
        assert_eq!(result, Number(1));
1✔
1134

1135
        let n_key = RistrettoSecretKey::random(&mut rng);
1✔
1136
        let msg = slice_to_boxed_message(n_key.as_bytes());
1✔
1137
        let script = script!(CheckSig(msg)).unwrap();
1✔
1138
        let inputs = inputs!(sig, pub_key);
1✔
1139
        let result = script.execute(&inputs).unwrap();
1✔
1140
        assert_eq!(result, Number(0));
1✔
1141
    }
1✔
1142

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

1158
        let n_key = RistrettoSecretKey::random(&mut rng);
1✔
1159
        let msg = slice_to_boxed_message(n_key.as_bytes());
1✔
1160
        let script = script!(CheckSigVerify(msg)).unwrap();
1✔
1161
        let inputs = inputs!(sig, pub_key);
1✔
1162
        let err = script.execute(&inputs).unwrap_err();
1✔
1163
        assert!(matches!(err, ScriptError::VerifyFailed));
1✔
1164
    }
1✔
1165

1166
    #[allow(clippy::type_complexity)]
1167
    fn multisig_data(
5✔
1168
        n: usize,
5✔
1169
    ) -> (
5✔
1170
        Box<Message>,
5✔
1171
        Vec<(
5✔
1172
            RistrettoSecretKey,
5✔
1173
            CompressedKey<RistrettoPublicKey>,
5✔
1174
            CompressedCheckSigSchnorrSignature,
5✔
1175
        )>,
5✔
1176
    ) {
5✔
1177
        let mut rng = rand::thread_rng();
5✔
1178
        let mut data = Vec::with_capacity(n);
5✔
1179
        let m = RistrettoSecretKey::random(&mut rng);
5✔
1180
        let msg = slice_to_boxed_message(m.as_bytes());
5✔
1181

5✔
1182
        for _ in 0..n {
55✔
1183
            let (k, p) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
55✔
1184
            let s = CompressedCheckSigSchnorrSignature::new_from_schnorr(
55✔
1185
                CheckSigSchnorrSignature::sign(&k, m.as_bytes(), &mut rng).unwrap(),
55✔
1186
            );
55✔
1187
            data.push((k, p, s));
55✔
1188
        }
55✔
1189

1190
        (msg, data)
5✔
1191
    }
5✔
1192

1193
    #[allow(clippy::too_many_lines)]
1194
    #[test]
1195
    fn check_multisig() {
1✔
1196
        use crate::{op_codes::Opcode::CheckMultiSig, StackItem::Number};
1197
        let mut rng = rand::thread_rng();
1✔
1198
        let (k_alice, p_alice) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1199
        let (k_bob, p_bob) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1200
        let (k_eve, _) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1201
        let (k_carol, p_carol) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1202
        let m = RistrettoSecretKey::random(&mut rng);
1✔
1203
        let s_alice = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1204
            CheckSigSchnorrSignature::sign(&k_alice, m.as_bytes(), &mut rng).unwrap(),
1✔
1205
        );
1✔
1206
        let s_bob = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1207
            CheckSigSchnorrSignature::sign(&k_bob, m.as_bytes(), &mut rng).unwrap(),
1✔
1208
        );
1✔
1209
        let s_eve = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1210
            CheckSigSchnorrSignature::sign(&k_eve, m.as_bytes(), &mut rng).unwrap(),
1✔
1211
        );
1✔
1212
        let s_carol = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1213
            CheckSigSchnorrSignature::sign(&k_carol, m.as_bytes(), &mut rng).unwrap(),
1✔
1214
        );
1✔
1215
        let s_alice2 = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1216
            CheckSigSchnorrSignature::sign(&k_alice, m.as_bytes(), &mut rng).unwrap(),
1✔
1217
        );
1✔
1218
        let msg = slice_to_boxed_message(m.as_bytes());
1✔
1219

1✔
1220
        // 1 of 2
1✔
1221
        let keys = vec![p_alice.clone(), p_bob.clone()];
1✔
1222
        let ops = vec![CheckMultiSig(1, 2, keys, msg.clone())];
1✔
1223
        let script = TariScript::new(ops).unwrap();
1✔
1224

1✔
1225
        let inputs = inputs!(s_alice.clone());
1✔
1226
        let result = script.execute(&inputs).unwrap();
1✔
1227
        assert_eq!(result, Number(1));
1✔
1228
        let inputs = inputs!(s_bob.clone());
1✔
1229
        let result = script.execute(&inputs).unwrap();
1✔
1230
        assert_eq!(result, Number(1));
1✔
1231
        let inputs = inputs!(s_eve.clone());
1✔
1232
        let result = script.execute(&inputs).unwrap();
1✔
1233
        assert_eq!(result, Number(0));
1✔
1234

1235
        // 2 of 2
1236
        let keys = vec![p_alice.clone(), p_bob.clone()];
1✔
1237
        let ops = vec![CheckMultiSig(2, 2, keys, msg.clone())];
1✔
1238
        let script = TariScript::new(ops).unwrap();
1✔
1239

1✔
1240
        let inputs = inputs!(s_alice.clone(), s_bob.clone());
1✔
1241
        let result = script.execute(&inputs).unwrap();
1✔
1242
        assert_eq!(result, Number(1));
1✔
1243
        let inputs = inputs!(s_bob.clone(), s_alice.clone());
1✔
1244
        let result = script.execute(&inputs).unwrap();
1✔
1245
        assert_eq!(result, Number(0));
1✔
1246
        let inputs = inputs!(s_eve.clone(), s_bob.clone());
1✔
1247
        let result = script.execute(&inputs).unwrap();
1✔
1248
        assert_eq!(result, Number(0));
1✔
1249

1250
        // 2 of 2 - don't allow same sig to sign twice
1251
        let inputs = inputs!(s_alice.clone(), s_alice.clone());
1✔
1252
        let result = script.execute(&inputs).unwrap();
1✔
1253
        assert_eq!(result, Number(0));
1✔
1254

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

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

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

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

1294
        // check that sigs are only counted once
1295
        let keys = vec![p_alice.clone(), p_bob.clone(), p_alice.clone()];
1✔
1296
        let ops = vec![CheckMultiSig(2, 3, keys, msg.clone())];
1✔
1297
        let script = TariScript::new(ops).unwrap();
1✔
1298

1✔
1299
        let inputs = inputs!(s_alice.clone(), s_carol.clone());
1✔
1300
        let result = script.execute(&inputs).unwrap();
1✔
1301
        assert_eq!(result, Number(0));
1✔
1302
        let inputs = inputs!(s_alice.clone(), s_alice.clone());
1✔
1303
        let result = script.execute(&inputs).unwrap();
1✔
1304
        assert_eq!(result, Number(0));
1✔
1305
        let inputs = inputs!(s_alice.clone(), s_alice2.clone());
1✔
1306
        let result = script.execute(&inputs).unwrap();
1✔
1307
        assert_eq!(result, Number(1));
1✔
1308
        // Interesting case where either sig could match either pubkey
1309
        let inputs = inputs!(s_alice2, s_alice.clone());
1✔
1310
        let result = script.execute(&inputs).unwrap();
1✔
1311
        assert_eq!(result, Number(1));
1✔
1312

1313
        // 3 of 3
1314
        let keys = vec![p_alice.clone(), p_bob.clone(), p_carol];
1✔
1315
        let ops = vec![CheckMultiSig(3, 3, keys, msg.clone())];
1✔
1316
        let script = TariScript::new(ops).unwrap();
1✔
1317

1✔
1318
        let inputs = inputs!(s_alice.clone(), s_bob.clone(), s_carol.clone());
1✔
1319
        let result = script.execute(&inputs).unwrap();
1✔
1320
        assert_eq!(result, Number(1));
1✔
1321
        let inputs = inputs!(s_carol.clone(), s_alice.clone(), s_bob.clone());
1✔
1322
        let result = script.execute(&inputs).unwrap();
1✔
1323
        assert_eq!(result, Number(0));
1✔
1324
        let inputs = inputs!(s_eve.clone(), s_bob.clone(), s_carol);
1✔
1325
        let result = script.execute(&inputs).unwrap();
1✔
1326
        assert_eq!(result, Number(0));
1✔
1327
        let inputs = inputs!(s_eve, s_bob);
1✔
1328
        let err = script.execute(&inputs).unwrap_err();
1✔
1329
        assert_eq!(err, ScriptError::StackUnderflow);
1✔
1330

1331
        // errors
1332
        let keys = vec![p_alice.clone(), p_bob.clone()];
1✔
1333
        let ops = vec![CheckMultiSig(0, 2, keys, msg.clone())];
1✔
1334
        let script = TariScript::new(ops).unwrap();
1✔
1335
        let inputs = inputs!(s_alice.clone());
1✔
1336
        let err = script.execute(&inputs).unwrap_err();
1✔
1337
        assert_eq!(err, ScriptError::ValueExceedsBounds);
1✔
1338

1339
        let keys = vec![p_alice.clone(), p_bob.clone()];
1✔
1340
        let ops = vec![CheckMultiSig(1, 0, keys, msg.clone())];
1✔
1341
        let script = TariScript::new(ops).unwrap();
1✔
1342
        let inputs = inputs!(s_alice.clone());
1✔
1343
        let err = script.execute(&inputs).unwrap_err();
1✔
1344
        assert_eq!(err, ScriptError::ValueExceedsBounds);
1✔
1345

1346
        let keys = vec![p_alice, p_bob];
1✔
1347
        let ops = vec![CheckMultiSig(2, 1, keys, msg)];
1✔
1348
        let script = TariScript::new(ops).unwrap();
1✔
1349
        let inputs = inputs!(s_alice);
1✔
1350
        let err = script.execute(&inputs).unwrap_err();
1✔
1351
        assert_eq!(err, ScriptError::ValueExceedsBounds);
1✔
1352

1353
        // max n is 32
1354
        let (msg, data) = multisig_data(33);
1✔
1355
        let keys = data.iter().map(|(_, p, _)| p.clone()).collect();
33✔
1356
        let sigs = data.iter().take(17).map(|(_, _, s)| s.clone());
17✔
1357
        let script = script!(CheckMultiSig(17, 33, keys, msg)).unwrap();
1✔
1358
        let items = sigs.map(StackItem::Signature).collect();
1✔
1359
        let inputs = ExecutionStack::new(items);
1✔
1360
        let err = script.execute(&inputs).unwrap_err();
1✔
1361
        assert_eq!(err, ScriptError::ValueExceedsBounds);
1✔
1362

1363
        // 3 of 4
1364
        let (msg, data) = multisig_data(4);
1✔
1365
        let keys = vec![
1✔
1366
            data[0].1.clone(),
1✔
1367
            data[1].1.clone(),
1✔
1368
            data[2].1.clone(),
1✔
1369
            data[3].1.clone(),
1✔
1370
        ];
1✔
1371
        let ops = vec![CheckMultiSig(3, 4, keys, msg)];
1✔
1372
        let script = TariScript::new(ops).unwrap();
1✔
1373
        let inputs = inputs!(data[0].2.clone(), data[1].2.clone(), data[2].2.clone());
1✔
1374
        let result = script.execute(&inputs).unwrap();
1✔
1375
        assert_eq!(result, Number(1));
1✔
1376

1377
        // 5 of 7
1378
        let (msg, data) = multisig_data(7);
1✔
1379
        let keys = vec![
1✔
1380
            data[0].1.clone(),
1✔
1381
            data[1].1.clone(),
1✔
1382
            data[2].1.clone(),
1✔
1383
            data[3].1.clone(),
1✔
1384
            data[4].1.clone(),
1✔
1385
            data[5].1.clone(),
1✔
1386
            data[6].1.clone(),
1✔
1387
        ];
1✔
1388
        let ops = vec![CheckMultiSig(5, 7, keys, msg)];
1✔
1389
        let script = TariScript::new(ops).unwrap();
1✔
1390
        let inputs = inputs!(
1✔
1391
            data[0].2.clone(),
1✔
1392
            data[1].2.clone(),
1✔
1393
            data[2].2.clone(),
1✔
1394
            data[3].2.clone(),
1✔
1395
            data[4].2.clone()
1✔
1396
        );
1✔
1397
        let result = script.execute(&inputs).unwrap();
1✔
1398
        assert_eq!(result, Number(1));
1✔
1399
    }
1✔
1400

1401
    #[allow(clippy::too_many_lines)]
1402
    #[test]
1403
    fn check_multisig_verify() {
1✔
1404
        use crate::{op_codes::Opcode::CheckMultiSigVerify, StackItem::Number};
1405
        let mut rng = rand::thread_rng();
1✔
1406
        let (k_alice, p_alice) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1407
        let (k_bob, p_bob) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1408
        let (k_eve, _) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1409
        let (k_carol, p_carol) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1410
        let m = RistrettoSecretKey::random(&mut rng);
1✔
1411
        let s_alice = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1412
            CheckSigSchnorrSignature::sign(&k_alice, m.as_bytes(), &mut rng).unwrap(),
1✔
1413
        );
1✔
1414
        let s_bob = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1415
            CheckSigSchnorrSignature::sign(&k_bob, m.as_bytes(), &mut rng).unwrap(),
1✔
1416
        );
1✔
1417
        let s_eve = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1418
            CheckSigSchnorrSignature::sign(&k_eve, m.as_bytes(), &mut rng).unwrap(),
1✔
1419
        );
1✔
1420
        let s_carol = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1421
            CheckSigSchnorrSignature::sign(&k_carol, m.as_bytes(), &mut rng).unwrap(),
1✔
1422
        );
1✔
1423
        let msg = slice_to_boxed_message(m.as_bytes());
1✔
1424

1✔
1425
        // 1 of 2
1✔
1426
        let keys = vec![p_alice.clone(), p_bob.clone()];
1✔
1427
        let ops = vec![CheckMultiSigVerify(1, 2, keys, msg.clone())];
1✔
1428
        let script = TariScript::new(ops).unwrap();
1✔
1429

1✔
1430
        let inputs = inputs!(Number(1), s_alice.clone());
1✔
1431
        let result = script.execute(&inputs).unwrap();
1✔
1432
        assert_eq!(result, Number(1));
1✔
1433
        let inputs = inputs!(Number(1), s_bob.clone());
1✔
1434
        let result = script.execute(&inputs).unwrap();
1✔
1435
        assert_eq!(result, Number(1));
1✔
1436
        let inputs = inputs!(Number(1), s_eve.clone());
1✔
1437
        let err = script.execute(&inputs).unwrap_err();
1✔
1438
        assert_eq!(err, ScriptError::VerifyFailed);
1✔
1439

1440
        // 2 of 2
1441
        let keys = vec![p_alice.clone(), p_bob.clone()];
1✔
1442
        let ops = vec![CheckMultiSigVerify(2, 2, keys, msg.clone())];
1✔
1443
        let script = TariScript::new(ops).unwrap();
1✔
1444

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

1455
        // 1 of 3
1456
        let keys = vec![p_alice.clone(), p_bob.clone(), p_carol.clone()];
1✔
1457
        let ops = vec![CheckMultiSigVerify(1, 3, keys, msg.clone())];
1✔
1458
        let script = TariScript::new(ops).unwrap();
1✔
1459

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

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

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

1494
        // 2 of 3 (returning the aggregate public key of the signatories)
1495
        let keys = vec![p_alice.clone(), p_bob.clone(), p_carol.clone()];
1✔
1496
        let ops = vec![CheckMultiSigVerifyAggregatePubKey(2, 3, keys, msg.clone())];
1✔
1497
        let script = TariScript::new(ops).unwrap();
1✔
1498

1✔
1499
        let inputs = inputs!(s_alice.clone(), s_bob.clone());
1✔
1500
        let agg_pub_key = script.execute(&inputs).unwrap();
1✔
1501
        assert_eq!(
1✔
1502
            agg_pub_key,
1✔
1503
            StackItem::PublicKey(CompressedKey::<RistrettoPublicKey>::new_from_pk(
1✔
1504
                &p_alice.clone().to_public_key().unwrap() + &p_bob.clone().to_public_key().unwrap()
1✔
1505
            ))
1✔
1506
        );
1✔
1507

1508
        let inputs = inputs!(s_alice.clone(), s_carol.clone());
1✔
1509
        let agg_pub_key = script.execute(&inputs).unwrap();
1✔
1510
        assert_eq!(
1✔
1511
            agg_pub_key,
1✔
1512
            StackItem::PublicKey(CompressedKey::<RistrettoPublicKey>::new_from_pk(
1✔
1513
                &p_alice.clone().to_public_key().unwrap() + &p_carol.clone().to_public_key().unwrap()
1✔
1514
            ))
1✔
1515
        );
1✔
1516

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

1526
        let inputs = inputs!(s_carol.clone(), s_bob.clone());
1✔
1527
        let err = script.execute(&inputs).unwrap_err();
1✔
1528
        assert_eq!(err, ScriptError::VerifyFailed);
1✔
1529

1530
        let inputs = inputs!(s_alice.clone(), s_bob.clone(), s_carol.clone());
1✔
1531
        let err = script.execute(&inputs).unwrap_err();
1✔
1532
        assert_eq!(err, ScriptError::NonUnitLengthStack);
1✔
1533

1534
        let inputs = inputs!(p_bob.clone());
1✔
1535
        let err = script.execute(&inputs).unwrap_err();
1✔
1536
        assert_eq!(err, ScriptError::StackUnderflow);
1✔
1537

1538
        // 3 of 3
1539
        let keys = vec![p_alice.clone(), p_bob.clone(), p_carol];
1✔
1540
        let ops = vec![CheckMultiSigVerify(3, 3, keys, msg.clone())];
1✔
1541
        let script = TariScript::new(ops).unwrap();
1✔
1542

1✔
1543
        let inputs = inputs!(Number(1), s_alice.clone(), s_bob.clone(), s_carol.clone());
1✔
1544
        let result = script.execute(&inputs).unwrap();
1✔
1545
        assert_eq!(result, Number(1));
1✔
1546
        let inputs = inputs!(Number(1), s_bob.clone(), s_alice.clone(), s_carol.clone());
1✔
1547
        let err = script.execute(&inputs).unwrap_err();
1✔
1548
        assert_eq!(err, ScriptError::VerifyFailed);
1✔
1549
        let inputs = inputs!(Number(1), s_eve.clone(), s_bob.clone(), s_carol);
1✔
1550
        let err = script.execute(&inputs).unwrap_err();
1✔
1551
        assert_eq!(err, ScriptError::VerifyFailed);
1✔
1552
        let inputs = inputs!(Number(1), s_eve, s_bob);
1✔
1553
        let err = script.execute(&inputs).unwrap_err();
1✔
1554
        assert_eq!(err, ScriptError::IncompatibleTypes);
1✔
1555

1556
        // errors
1557
        let keys = vec![p_alice.clone(), p_bob.clone()];
1✔
1558
        let ops = vec![CheckMultiSigVerify(0, 2, keys, msg.clone())];
1✔
1559
        let script = TariScript::new(ops).unwrap();
1✔
1560
        let inputs = inputs!(s_alice.clone());
1✔
1561
        let err = script.execute(&inputs).unwrap_err();
1✔
1562
        assert_eq!(err, ScriptError::ValueExceedsBounds);
1✔
1563

1564
        let keys = vec![p_alice.clone(), p_bob.clone()];
1✔
1565
        let ops = vec![CheckMultiSigVerify(1, 0, keys, msg.clone())];
1✔
1566
        let script = TariScript::new(ops).unwrap();
1✔
1567
        let inputs = inputs!(s_alice.clone());
1✔
1568
        let err = script.execute(&inputs).unwrap_err();
1✔
1569
        assert_eq!(err, ScriptError::ValueExceedsBounds);
1✔
1570

1571
        let keys = vec![p_alice, p_bob];
1✔
1572
        let ops = vec![CheckMultiSigVerify(2, 1, keys, msg)];
1✔
1573
        let script = TariScript::new(ops).unwrap();
1✔
1574
        let inputs = inputs!(s_alice);
1✔
1575
        let err = script.execute(&inputs).unwrap_err();
1✔
1576
        assert_eq!(err, ScriptError::ValueExceedsBounds);
1✔
1577

1578
        // 3 of 4
1579
        let (msg, data) = multisig_data(4);
1✔
1580
        let keys = vec![
1✔
1581
            data[0].1.clone(),
1✔
1582
            data[1].1.clone(),
1✔
1583
            data[2].1.clone(),
1✔
1584
            data[3].1.clone(),
1✔
1585
        ];
1✔
1586
        let ops = vec![CheckMultiSigVerify(3, 4, keys, msg)];
1✔
1587
        let script = TariScript::new(ops).unwrap();
1✔
1588
        let inputs = inputs!(Number(1), data[0].2.clone(), data[1].2.clone(), data[2].2.clone());
1✔
1589
        let result = script.execute(&inputs).unwrap();
1✔
1590
        assert_eq!(result, Number(1));
1✔
1591

1592
        // 5 of 7
1593
        let (msg, data) = multisig_data(7);
1✔
1594
        let keys = vec![
1✔
1595
            data[0].1.clone(),
1✔
1596
            data[1].1.clone(),
1✔
1597
            data[2].1.clone(),
1✔
1598
            data[3].1.clone(),
1✔
1599
            data[4].1.clone(),
1✔
1600
            data[5].1.clone(),
1✔
1601
            data[6].1.clone(),
1✔
1602
        ];
1✔
1603
        let ops = vec![CheckMultiSigVerify(5, 7, keys, msg)];
1✔
1604
        let script = TariScript::new(ops).unwrap();
1✔
1605
        let inputs = inputs!(
1✔
1606
            Number(1),
1✔
1607
            data[0].2.clone(),
1✔
1608
            data[1].2.clone(),
1✔
1609
            data[2].2.clone(),
1✔
1610
            data[3].2.clone(),
1✔
1611
            data[4].2.clone()
1✔
1612
        );
1✔
1613
        let result = script.execute(&inputs).unwrap();
1✔
1614
        assert_eq!(result, Number(1));
1✔
1615
    }
1✔
1616

1617
    #[test]
1618
    fn pay_to_public_key_hash() {
1✔
1619
        use crate::StackItem::PublicKey;
1620
        let k =
1✔
1621
            RistrettoSecretKey::from_hex("7212ac93ee205cdbbb57c4f0f815fbf8db25b4d04d3532e2262e31907d82c700").unwrap();
1✔
1622
        let p = CompressedKey::<RistrettoPublicKey>::from_secret_key(&k); // 56c0fa32558d6edc0916baa26b48e745de834571534ca253ea82435f08ebbc7c
1✔
1623
        let hash = Blake2b::<U32>::digest(p.as_bytes());
1✔
1624
        let pkh = slice_to_boxed_hash(hash.as_slice()); // ae2337ce44f9ebb6169c863ec168046cb35ab4ef7aa9ed4f5f1f669bb74b09e5
1✔
1625

1✔
1626
        // Unlike in Bitcoin where P2PKH includes a CheckSig at the end of the script, that part of the process is built
1✔
1627
        // into definition of how TariScript is evaluated by a base node or wallet
1✔
1628
        let script = script!(Dup HashBlake256 PushHash(pkh) EqualVerify).unwrap();
1✔
1629
        let hex_script = "71b07aae2337ce44f9ebb6169c863ec168046cb35ab4ef7aa9ed4f5f1f669bb74b09e581";
1✔
1630
        // Test serialisation
1✔
1631
        assert_eq!(script.to_hex(), hex_script);
1✔
1632
        // Test de-serialisation
1633
        assert_eq!(TariScript::from_hex(hex_script).unwrap(), script);
1✔
1634

1635
        let inputs = inputs!(p.clone());
1✔
1636

1✔
1637
        let result = script.execute(&inputs).unwrap();
1✔
1638

1✔
1639
        assert_eq!(result, PublicKey(p));
1✔
1640
    }
1✔
1641

1642
    #[test]
1643
    fn hex_roundtrip() {
1✔
1644
        // Generate a signature
1✔
1645
        let mut rng = rand::thread_rng();
1✔
1646
        let (secret_key, public_key) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1647
        let message = [1u8; 32];
1✔
1648
        let sig = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1649
            CheckSigSchnorrSignature::sign(&secret_key, message, &mut rng).unwrap(),
1✔
1650
        );
1✔
1651

1✔
1652
        // Produce a script using the signature
1✔
1653
        let script = script!(CheckSig(slice_to_boxed_message(message.as_bytes()))).unwrap();
1✔
1654

1✔
1655
        // Produce input satisfying the script
1✔
1656
        let input = inputs!(sig, public_key);
1✔
1657

1✔
1658
        // Check that script execution succeeds
1✔
1659
        assert_eq!(script.execute(&input).unwrap(), StackItem::Number(1));
1✔
1660

1661
        // Convert the script to hex and back
1662
        let parsed_script = TariScript::from_hex(script.to_hex().as_str()).unwrap();
1✔
1663
        assert_eq!(script.to_opcodes(), parsed_script.to_opcodes());
1✔
1664

1665
        // Convert the input to hex and back
1666
        let parsed_input = ExecutionStack::from_hex(input.to_hex().as_str()).unwrap();
1✔
1667
        assert_eq!(input, parsed_input);
1✔
1668

1669
        // Check that script execution still succeeds
1670
        assert_eq!(parsed_script.execute(&parsed_input).unwrap(), StackItem::Number(1));
1✔
1671
    }
1✔
1672

1673
    #[test]
1674
    fn disassemble() {
1✔
1675
        let hex_script = "71b07aae2337ce44f9ebb6169c863ec168046cb35ab4ef7aa9ed4f5f1f669bb74b09e58170ac276657a418820f34036b20ea615302b373c70ac8feab8d30681a3e0f0960e708";
1✔
1676
        let script = TariScript::from_hex(hex_script).unwrap();
1✔
1677
        let ops = vec![
1✔
1678
            "Dup",
1✔
1679
            "HashBlake256",
1✔
1680
            "PushHash(ae2337ce44f9ebb6169c863ec168046cb35ab4ef7aa9ed4f5f1f669bb74b09e5)",
1✔
1681
            "EqualVerify",
1✔
1682
            "Drop",
1✔
1683
            "CheckSig(276657a418820f34036b20ea615302b373c70ac8feab8d30681a3e0f0960e708)",
1✔
1684
        ]
1✔
1685
        .into_iter()
1✔
1686
        .map(String::from)
1✔
1687
        .collect::<Vec<String>>();
1✔
1688
        assert_eq!(script.to_opcodes(), ops);
1✔
1689
        assert_eq!(
1✔
1690
            script.to_string(),
1✔
1691
            "Dup HashBlake256 PushHash(ae2337ce44f9ebb6169c863ec168046cb35ab4ef7aa9ed4f5f1f669bb74b09e5) EqualVerify \
1✔
1692
             Drop CheckSig(276657a418820f34036b20ea615302b373c70ac8feab8d30681a3e0f0960e708)"
1✔
1693
        );
1✔
1694
    }
1✔
1695

1696
    #[test]
1697
    fn time_locked_contract_example() {
1✔
1698
        let k_alice =
1✔
1699
            RistrettoSecretKey::from_hex("f305e64c0e73cbdb665165ac97b69e5df37b2cd81f9f8f569c3bd854daff290e").unwrap();
1✔
1700
        let p_alice = CompressedKey::<RistrettoPublicKey>::from_secret_key(&k_alice); // 9c35e9f0f11cf25ce3ca1182d37682ab5824aa033f2024651e007364d06ec355
1✔
1701

1✔
1702
        let k_bob =
1✔
1703
            RistrettoSecretKey::from_hex("e0689386a018e88993a7bb14cbff5bad8a8858ea101d6e0da047df3ddf499c0e").unwrap();
1✔
1704
        let p_bob = CompressedKey::<RistrettoPublicKey>::from_secret_key(&k_bob); // 3a58f371e94da76a8902e81b4b55ddabb7dc006cd8ebde3011c46d0e02e9172f
1✔
1705

1✔
1706
        let lock_height = 4000u64;
1✔
1707

1✔
1708
        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✔
1709

1✔
1710
        // Alice tries to spend the output before the height is reached
1✔
1711
        let inputs_alice_spends_early = inputs!(p_alice.clone());
1✔
1712
        let ctx = context_with_height(3990u64);
1✔
1713
        assert_eq!(
1✔
1714
            script.execute_with_context(&inputs_alice_spends_early, &ctx),
1✔
1715
            Err(ScriptError::VerifyFailed)
1✔
1716
        );
1✔
1717

1718
        // Alice tries to spend the output after the height is reached
1719
        let inputs_alice_spends_early = inputs!(p_alice.clone());
1✔
1720
        let ctx = context_with_height(4000u64);
1✔
1721
        assert_eq!(
1✔
1722
            script.execute_with_context(&inputs_alice_spends_early, &ctx).unwrap(),
1✔
1723
            StackItem::PublicKey(p_alice)
1✔
1724
        );
1✔
1725

1726
        // Bob spends before time lock is reached
1727
        let inputs_bob_spends_early = inputs!(p_bob.clone());
1✔
1728
        let ctx = context_with_height(3990u64);
1✔
1729
        assert_eq!(
1✔
1730
            script.execute_with_context(&inputs_bob_spends_early, &ctx).unwrap(),
1✔
1731
            StackItem::PublicKey(p_bob.clone())
1✔
1732
        );
1✔
1733

1734
        // Bob spends after time lock is reached
1735
        let inputs_bob_spends_early = inputs!(p_bob.clone());
1✔
1736
        let ctx = context_with_height(4001u64);
1✔
1737
        assert_eq!(
1✔
1738
            script.execute_with_context(&inputs_bob_spends_early, &ctx).unwrap(),
1✔
1739
            StackItem::PublicKey(p_bob)
1✔
1740
        );
1✔
1741
    }
1✔
1742

1743
    #[test]
1744
    fn m_of_n_signatures() {
1✔
1745
        use crate::StackItem::PublicKey;
1746
        let mut rng = rand::thread_rng();
1✔
1747
        let (k_alice, p_alice) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1748
        let (k_bob, p_bob) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1749
        let (k_eve, _) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1750

1✔
1751
        let m = RistrettoSecretKey::random(&mut rng);
1✔
1752
        let msg = slice_to_boxed_message(m.as_bytes());
1✔
1753

1✔
1754
        let s_alice = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1755
            CheckSigSchnorrSignature::sign(&k_alice, m.as_bytes(), &mut rng).unwrap(),
1✔
1756
        );
1✔
1757
        let s_bob = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1758
            CheckSigSchnorrSignature::sign(&k_bob, m.as_bytes(), &mut rng).unwrap(),
1✔
1759
        );
1✔
1760
        let s_eve = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
1761
            CheckSigSchnorrSignature::sign(&k_eve, m.as_bytes(), &mut rng).unwrap(),
1✔
1762
        );
1✔
1763

1764
        // 1 of 2
1765
        use crate::Opcode::{CheckSig, Drop, Dup, Else, EndIf, IfThen, PushPubKey, Return};
1766
        let ops = vec![
1✔
1767
            Dup,
1✔
1768
            PushPubKey(Box::new(p_alice.clone())),
1✔
1769
            CheckSig(msg.clone()),
1✔
1770
            IfThen,
1✔
1771
            Drop,
1✔
1772
            PushPubKey(Box::new(p_alice.clone())),
1✔
1773
            Else,
1✔
1774
            PushPubKey(Box::new(p_bob.clone())),
1✔
1775
            CheckSig(msg),
1✔
1776
            IfThen,
1✔
1777
            PushPubKey(Box::new(p_bob.clone())),
1✔
1778
            Else,
1✔
1779
            Return,
1✔
1780
            EndIf,
1✔
1781
            EndIf,
1✔
1782
        ];
1✔
1783
        let script = TariScript::new(ops).unwrap();
1✔
1784

1✔
1785
        // alice
1✔
1786
        let inputs = inputs!(s_alice);
1✔
1787
        let result = script.execute(&inputs).unwrap();
1✔
1788
        assert_eq!(result, PublicKey(p_alice));
1✔
1789

1790
        // bob
1791
        let inputs = inputs!(s_bob);
1✔
1792
        let result = script.execute(&inputs).unwrap();
1✔
1793
        assert_eq!(result, PublicKey(p_bob));
1✔
1794

1795
        // eve
1796
        let inputs = inputs!(s_eve);
1✔
1797
        let result = script.execute(&inputs).unwrap_err();
1✔
1798
        assert_eq!(result, ScriptError::Return);
1✔
1799
    }
1✔
1800

1801
    #[test]
1802
    fn to_ristretto_point() {
1✔
1803
        use crate::{Opcode::ToRistrettoPoint, StackItem::PublicKey};
1804

1805
        // Generate a key pair
1806
        let mut rng = rand::thread_rng();
1✔
1807
        let (k_1, p_1) = CompressedKey::<RistrettoPublicKey>::random_keypair(&mut rng);
1✔
1808

1✔
1809
        // Generate a test script
1✔
1810
        let ops = vec![ToRistrettoPoint];
1✔
1811
        let script = TariScript::new(ops).unwrap();
1✔
1812

1✔
1813
        // Invalid stack type
1✔
1814
        let inputs = inputs!(CompressedKey::<RistrettoPublicKey>::default());
1✔
1815
        let err = script.execute(&inputs).unwrap_err();
1✔
1816
        assert!(matches!(err, ScriptError::IncompatibleTypes));
1✔
1817

1818
        // Valid scalar
1819
        let mut scalar = [0u8; 32];
1✔
1820
        scalar.copy_from_slice(k_1.as_bytes());
1✔
1821
        let inputs = inputs!(scalar);
1✔
1822
        let result = script.execute(&inputs).unwrap();
1✔
1823
        assert_eq!(result, PublicKey(p_1.clone()));
1✔
1824

1825
        // Valid hash
1826
        let inputs = ExecutionStack::new(vec![Hash(scalar)]);
1✔
1827
        let result = script.execute(&inputs).unwrap();
1✔
1828
        assert_eq!(result, PublicKey(p_1));
1✔
1829

1830
        // Invalid bytes
1831
        let invalid = [u8::MAX; 32]; // not a canonical scalar encoding!
1✔
1832
        let inputs = inputs!(invalid);
1✔
1833
        assert!(matches!(script.execute(&inputs), Err(ScriptError::InvalidInput)));
1✔
1834
    }
1✔
1835

1836
    #[test]
1837
    fn test_borsh_de_serialization() {
1✔
1838
        let hex_script = "71b07aae2337ce44f9ebb6169c863ec168046cb35ab4ef7aa9ed4f5f1f669bb74b09e58170ac276657a418820f34036b20ea615302b373c70ac8feab8d30681a3e0f0960e708";
1✔
1839
        let script = TariScript::from_hex(hex_script).unwrap();
1✔
1840
        let mut buf = Vec::new();
1✔
1841
        script.serialize(&mut buf).unwrap();
1✔
1842
        buf.extend_from_slice(&[1, 2, 3]);
1✔
1843
        let buf = &mut buf.as_slice();
1✔
1844
        assert_eq!(script, TariScript::deserialize(buf).unwrap());
1✔
1845
        assert_eq!(buf, &[1, 2, 3]);
1✔
1846
    }
1✔
1847

1848
    #[test]
1849
    fn test_borsh_de_serialization_too_large() {
1✔
1850
        // We dont care about the actual script here, just that its not too large on the varint size
1✔
1851
        // We lie about the size to try and get a mem panic, and say this script is u64::max large.
1✔
1852
        let buf = vec![255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 49, 8, 2, 5, 6];
1✔
1853
        let buf = &mut buf.as_slice();
1✔
1854
        assert!(TariScript::deserialize(buf).is_err());
1✔
1855
    }
1✔
1856

1857
    #[test]
1858
    fn test_compare_height_block_height_exceeds_bounds() {
1✔
1859
        let script = script!(CompareHeight).unwrap();
1✔
1860

1✔
1861
        let inputs = inputs!(0);
1✔
1862
        let ctx = context_with_height(u64::MAX);
1✔
1863
        let stack_item = script.execute_with_context(&inputs, &ctx);
1✔
1864
        assert!(matches!(stack_item, Err(ScriptError::ValueExceedsBounds)));
1✔
1865
    }
1✔
1866

1867
    #[test]
1868
    fn test_compare_height_underflows() {
1✔
1869
        let script = script!(CompareHeight).unwrap();
1✔
1870

1✔
1871
        let inputs = ExecutionStack::new(vec![Number(i64::MIN)]);
1✔
1872
        let ctx = context_with_height(i64::MAX as u64);
1✔
1873
        let stack_item = script.execute_with_context(&inputs, &ctx);
1✔
1874
        assert!(matches!(stack_item, Err(ScriptError::CompareFailed(_))));
1✔
1875
    }
1✔
1876

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

1✔
1881
        let inputs = ExecutionStack::new(vec![]);
1✔
1882
        let ctx = context_with_height(i64::MAX as u64);
1✔
1883
        let stack_item = script.execute_with_context(&inputs, &ctx);
1✔
1884
        assert!(matches!(stack_item, Err(ScriptError::StackUnderflow)));
1✔
1885
    }
1✔
1886

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

1✔
1891
        let inputs = inputs!(100);
1✔
1892
        let ctx = context_with_height(24_u64);
1✔
1893
        let stack_item = script.execute_with_context(&inputs, &ctx);
1✔
1894
        assert!(stack_item.is_ok());
1✔
1895
        assert_eq!(stack_item.unwrap(), Number(-76))
1✔
1896
    }
1✔
1897

1898
    #[test]
1899
    fn test_compare_height_valid_with_int_result() {
1✔
1900
        let script = script!(CompareHeight).unwrap();
1✔
1901

1✔
1902
        let inputs = inputs!(100);
1✔
1903
        let ctx = context_with_height(110_u64);
1✔
1904
        let stack_item = script.execute_with_context(&inputs, &ctx);
1✔
1905
        assert!(stack_item.is_ok());
1✔
1906
        assert_eq!(stack_item.unwrap(), Number(10))
1✔
1907
    }
1✔
1908

1909
    #[test]
1910
    fn test_check_height_block_height_exceeds_bounds() {
1✔
1911
        let script = script!(CheckHeight(0)).unwrap();
1✔
1912

1✔
1913
        let inputs = ExecutionStack::new(vec![]);
1✔
1914
        let ctx = context_with_height(u64::MAX);
1✔
1915
        let stack_item = script.execute_with_context(&inputs, &ctx);
1✔
1916
        assert!(matches!(stack_item, Err(ScriptError::ValueExceedsBounds)));
1✔
1917
    }
1✔
1918

1919
    #[test]
1920
    fn test_check_height_exceeds_bounds() {
1✔
1921
        let script = script!(CheckHeight(u64::MAX)).unwrap();
1✔
1922

1✔
1923
        let inputs = ExecutionStack::new(vec![]);
1✔
1924
        let ctx = context_with_height(10_u64);
1✔
1925
        let stack_item = script.execute_with_context(&inputs, &ctx);
1✔
1926
        assert!(matches!(stack_item, Err(ScriptError::ValueExceedsBounds)));
1✔
1927
    }
1✔
1928

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

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

1935
        for i in 0..255 {
256✔
1936
            inputs.push(Number(i)).unwrap();
255✔
1937
        }
255✔
1938

1939
        let ctx = context_with_height(i64::MAX as u64);
1✔
1940
        let stack_item = script.execute_with_context(&inputs, &ctx);
1✔
1941
        assert!(matches!(stack_item, Err(ScriptError::StackOverflow)));
1✔
1942
    }
1✔
1943

1944
    #[test]
1945
    fn test_check_height_valid_with_uint_result() {
1✔
1946
        let script = script!(CheckHeight(24)).unwrap();
1✔
1947

1✔
1948
        let inputs = ExecutionStack::new(vec![]);
1✔
1949
        let ctx = context_with_height(100_u64);
1✔
1950
        let stack_item = script.execute_with_context(&inputs, &ctx);
1✔
1951
        assert!(stack_item.is_ok());
1✔
1952
        assert_eq!(stack_item.unwrap(), Number(76))
1✔
1953
    }
1✔
1954

1955
    #[test]
1956
    fn test_check_height_valid_with_int_result() {
1✔
1957
        let script = script!(CheckHeight(100)).unwrap();
1✔
1958

1✔
1959
        let inputs = ExecutionStack::new(vec![]);
1✔
1960
        let ctx = context_with_height(24_u64);
1✔
1961
        let stack_item = script.execute_with_context(&inputs, &ctx);
1✔
1962
        assert!(stack_item.is_ok());
1✔
1963
        assert_eq!(stack_item.unwrap(), Number(-76))
1✔
1964
    }
1✔
1965
}
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