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

tari-project / tari / 25106105978

29 Apr 2026 11:25AM UTC coverage: 61.025% (+0.08%) from 60.949%
25106105978

push

github

web-flow
fix(deps): upgrade diesel to 2.3.8 for RUSTSEC-2026-0111 (#7790)

Description
---
Upgrade Diesel dependencies from 2.2.10 to 2.3.8 across Tari crates that
use the SQLite backend, and refresh diesel_migrations to 2.3.

This addresses the RUSTSEC-2026-0111 advisory (possible UTF-8 corruption
unsoundness in Diesel's SQLite backend).

Motivation and Context
---
Issue #7787 reports that the workspace lockfile includes vulnerable
diesel 2.2.10.

This patch keeps the scope limited to dependency updates in crates that
directly depend on Diesel:
- common_sqlite
- comms/core
- comms/dht
- base_layer/wallet
- base_layer/transaction_key_manager

How Has This Been Tested?
---
- cargo check --locked --ignore-rust-version -p tari_common_sqlite -p
tari_comms -p tari_comms_dht -p tari_transaction_key_manager -p
minotari_wallet

Note: local toolchain in this environment is rustc 1.92.0 while
workspace requires 1.93.0; therefore --ignore-rust-version was used for
local verification.

What process can a PR reviewer use to test or verify this change?
---
1. Confirm Cargo.toml Diesel version bumps in the five crates above.
2. Confirm Cargo.lock no longer contains diesel 2.2.10.
3. Run:
cargo check --locked -p tari_common_sqlite -p tari_comms -p
tari_comms_dht -p tari_transaction_key_manager -p minotari_wallet

Breaking Changes
---
- [x] None
- [ ] Requires data directory on base node to be deleted
- [ ] Requires hard fork
- [ ] Other - Please specify

Closes #7787.

70852 of 116103 relevant lines covered (61.03%)

223856.56 hits per line

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

87.0
/infrastructure/tari_script/src/stack.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
use std::io;
19

20
use borsh::{BorshDeserialize, BorshSerialize};
21
use integer_encoding::{VarIntReader, VarIntWriter};
22
use tari_crypto::{
23
    compressed_commitment::CompressedCommitment,
24
    compressed_key::CompressedKey,
25
    ristretto::{RistrettoPublicKey, RistrettoSecretKey},
26
};
27
use tari_utilities::{
28
    ByteArray,
29
    hex::{Hex, HexError, from_hex, to_hex},
30
};
31

32
use crate::{
33
    CheckSigSchnorrSignature,
34
    CompressedCheckSigSchnorrSignature,
35
    error::ScriptError,
36
    op_codes::{HashValue, ScalarValue},
37
};
38

39
pub const MAX_STACK_SIZE: usize = 255;
40

41
#[macro_export]
42
macro_rules! inputs {
43
    ($($input:expr),+) => {{
44
        use $crate::{ExecutionStack, StackItem};
45

46
        let items = vec![$(StackItem::from($input)),+];
47
        ExecutionStack::new(items)
48
    }}
49
}
50

51
macro_rules! stack_item_from {
52
    ($from_type:ty => $variant:ident) => {
53
        impl From<$from_type> for StackItem {
54
            fn from(item: $from_type) -> Self {
2,704✔
55
                StackItem::$variant(item)
2,704✔
56
            }
2,704✔
57
        }
58
    };
59
}
60

61
pub const TYPE_NUMBER: u8 = 1;
62
pub const TYPE_HASH: u8 = 2;
63
pub const TYPE_COMMITMENT: u8 = 3;
64
pub const TYPE_PUBKEY: u8 = 4;
65
pub const TYPE_SIG: u8 = 5;
66
pub const TYPE_SCALAR: u8 = 6;
67

68
#[derive(Debug, Clone, PartialEq, Eq)]
69
pub enum StackItem {
70
    Number(i64),
71
    Hash(HashValue),
72
    Scalar(ScalarValue),
73
    Commitment(CompressedCommitment<RistrettoPublicKey>),
74
    PublicKey(CompressedKey<RistrettoPublicKey>),
75
    Signature(CompressedCheckSigSchnorrSignature),
76
}
77

78
impl StackItem {
79
    /// Convert an input item into its binary representation and append it to the array. The function returns the byte
80
    /// slice that matches the item as a convenience
81
    pub fn to_bytes<'a>(&self, array: &'a mut Vec<u8>) -> &'a [u8] {
16,487✔
82
        let n = array.len();
16,487✔
83
        match self {
16,487✔
84
            StackItem::Number(v) => {
2✔
85
                array.push(TYPE_NUMBER);
2✔
86
                array.extend_from_slice(&v.to_le_bytes());
2✔
87
            },
2✔
88
            StackItem::Hash(h) => {
1✔
89
                array.push(TYPE_HASH);
1✔
90
                array.extend_from_slice(&h[..]);
1✔
91
            },
1✔
92
            StackItem::Commitment(c) => {
×
93
                array.push(TYPE_COMMITMENT);
×
94
                array.extend_from_slice(c.as_bytes());
×
95
            },
×
96
            StackItem::PublicKey(p) => {
1,544✔
97
                array.push(TYPE_PUBKEY);
1,544✔
98
                array.extend_from_slice(p.as_bytes());
1,544✔
99
            },
1,544✔
100
            StackItem::Signature(s) => {
14,938✔
101
                array.push(TYPE_SIG);
14,938✔
102
                array.extend_from_slice(s.get_compressed_public_nonce().as_bytes());
14,938✔
103
                array.extend_from_slice(s.get_signature().as_bytes());
14,938✔
104
            },
14,938✔
105
            StackItem::Scalar(scalar) => {
2✔
106
                array.push(TYPE_SCALAR);
2✔
107
                array.extend_from_slice(scalar);
2✔
108
            },
2✔
109
        };
110
        array.get(n..).expect("Length is always valid")
16,487✔
111
    }
16,487✔
112

113
    /// Take a byte slice and read the next stack item from it, including any associated data. `read_next` returns a
114
    /// tuple of the deserialised item, and an updated slice that has the Opcode and data removed.
115
    pub fn read_next(bytes: &[u8]) -> Option<(Self, &[u8])> {
13,253✔
116
        let code = bytes.first()?;
13,253✔
117
        let remaining = bytes.get(1..)?;
13,253✔
118
        match *code {
13,253✔
119
            TYPE_NUMBER => StackItem::b_to_number(remaining),
3✔
120
            TYPE_HASH => StackItem::b_to_hash(remaining),
×
121
            TYPE_COMMITMENT => StackItem::b_to_commitment(remaining),
1✔
122
            TYPE_PUBKEY => StackItem::b_to_pubkey(remaining),
452✔
123
            TYPE_SIG => StackItem::b_to_sig(remaining),
12,792✔
124
            TYPE_SCALAR => StackItem::b_to_scalar(remaining),
5✔
125
            _ => None,
×
126
        }
127
    }
13,253✔
128

129
    fn b_to_number(b: &[u8]) -> Option<(Self, &[u8])> {
3✔
130
        let mut arr = [0u8; 8];
3✔
131
        arr.copy_from_slice(b.get(..8)?);
3✔
132
        Some((StackItem::Number(i64::from_le_bytes(arr)), b.get(8..)?))
3✔
133
    }
3✔
134

135
    fn b_to_hash(b: &[u8]) -> Option<(Self, &[u8])> {
×
136
        let mut arr = [0u8; 32];
×
137
        arr.copy_from_slice(b.get(..32)?);
×
138
        Some((StackItem::Hash(arr), b.get(32..)?))
×
139
    }
×
140

141
    fn b_to_scalar(b: &[u8]) -> Option<(Self, &[u8])> {
5✔
142
        let mut arr = [0u8; 32];
5✔
143
        arr.copy_from_slice(b.get(..32)?);
5✔
144
        Some((StackItem::Scalar(arr), b.get(32..)?))
5✔
145
    }
5✔
146

147
    fn b_to_commitment(b: &[u8]) -> Option<(Self, &[u8])> {
1✔
148
        let c = CompressedCommitment::from_canonical_bytes(b.get(..32)?).ok()?;
1✔
149
        Some((StackItem::Commitment(c), b.get(32..)?))
1✔
150
    }
1✔
151

152
    fn b_to_pubkey(b: &[u8]) -> Option<(Self, &[u8])> {
452✔
153
        let p = CompressedKey::from_canonical_bytes(b.get(..32)?).ok()?;
452✔
154
        Some((StackItem::PublicKey(p), b.get(32..)?))
452✔
155
    }
452✔
156

157
    fn b_to_sig(b: &[u8]) -> Option<(Self, &[u8])> {
12,792✔
158
        let r = RistrettoPublicKey::from_canonical_bytes(b.get(..32)?).ok()?;
12,792✔
159
        let s = RistrettoSecretKey::from_canonical_bytes(b.get(32..64)?).ok()?;
12,792✔
160
        let sig = CompressedCheckSigSchnorrSignature::new_from_schnorr(CheckSigSchnorrSignature::new(r, s));
12,792✔
161
        Some((StackItem::Signature(sig), b.get(64..)?))
12,792✔
162
    }
12,792✔
163
}
164

165
stack_item_from!(i64 => Number);
166
stack_item_from!(CompressedCommitment<RistrettoPublicKey> => Commitment);
167
stack_item_from!(CompressedKey<RistrettoPublicKey> => PublicKey);
168
stack_item_from!(CompressedCheckSigSchnorrSignature => Signature);
169
stack_item_from!(ScalarValue => Scalar);
170

171
#[derive(Debug, Default, Clone, PartialEq, Eq)]
172
pub struct ExecutionStack {
173
    items: Vec<StackItem>,
174
}
175

176
impl BorshSerialize for ExecutionStack {
177
    fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
3,880✔
178
        let bytes = self.to_bytes();
3,880✔
179
        writer.write_varint(bytes.len())?;
3,880✔
180
        for b in &bytes {
417,320✔
181
            b.serialize(writer)?;
417,320✔
182
        }
183
        Ok(())
3,880✔
184
    }
3,880✔
185
}
186

187
impl BorshDeserialize for ExecutionStack {
188
    fn deserialize_reader<R>(reader: &mut R) -> Result<Self, io::Error>
2✔
189
    where R: io::Read {
2✔
190
        let len = reader.read_varint()?;
2✔
191
        if len > MAX_STACK_SIZE {
2✔
192
            return Err(io::Error::new(
1✔
193
                io::ErrorKind::InvalidInput,
1✔
194
                "Larger than max execution stack bytes".to_string(),
1✔
195
            ));
1✔
196
        }
1✔
197
        let mut data = Vec::with_capacity(len);
1✔
198
        for _ in 0..len {
1✔
199
            data.push(u8::deserialize_reader(reader)?);
131✔
200
        }
201
        let stack = Self::from_bytes(data.as_slice())
1✔
202
            .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e.to_string()))?;
1✔
203
        Ok(stack)
1✔
204
    }
2✔
205
}
206

207
impl ExecutionStack {
208
    /// Return a new `ExecutionStack` using the vector of [StackItem] in `items`
209
    pub fn new(items: Vec<StackItem>) -> Self {
2,617✔
210
        ExecutionStack { items }
2,617✔
211
    }
2,617✔
212

213
    /// Returns the number of entries in the execution stack
214
    pub fn size(&self) -> usize {
14,033✔
215
        self.items.len()
14,033✔
216
    }
14,033✔
217

218
    /// Returns a reference to the top entry in the stack without affecting the stack
219
    pub fn peek(&self) -> Option<&StackItem> {
8✔
220
        self.items.last()
8✔
221
    }
8✔
222

223
    /// Returns true if the stack is empty
224
    pub fn is_empty(&self) -> bool {
1✔
225
        self.items.is_empty()
1✔
226
    }
1✔
227

228
    /// Pops the top item in the stack. If the stack is not empty, `pop` returns the item, otherwise return `None` if
229
    /// it is empty.
230
    pub fn pop(&mut self) -> Option<StackItem> {
397✔
231
        self.items.pop()
397✔
232
    }
397✔
233

234
    /// Pops the top item in the stack and applies TryFrom for the given generic type. If the stack is not empty, and is
235
    /// a StackItem::Number, `pop_into_number` returns the parsed number. Returns an error if the stack is empty or if
236
    /// the top item is a different variant.
237
    pub fn pop_into_number<T: TryFrom<i64>>(&mut self) -> Result<T, ScriptError> {
37✔
238
        let item = self.items.pop().ok_or(ScriptError::StackUnderflow)?;
37✔
239

240
        let number = match item {
36✔
241
            StackItem::Number(n) => T::try_from(n).map_err(|_| ScriptError::ValueExceedsBounds)?,
36✔
242
            _ => return Err(ScriptError::InvalidInput),
×
243
        };
244

245
        Ok(number)
36✔
246
    }
37✔
247

248
    /// Pops n + 1 items from the stack. Checks if the last popped item matches any of the first n items. Returns an
249
    /// error if all n + 1 items aren't of the same variant, or if there are not n + 1 items on the stack.
250
    pub fn pop_n_plus_one_contains(&mut self, n: u8) -> Result<bool, ScriptError> {
13✔
251
        let items = self.pop_num_items(n as usize)?;
13✔
252
        let item = self.pop().ok_or(ScriptError::StackUnderflow)?;
13✔
253

254
        // check that all popped items are of the same variant
255
        // first count each variant
256
        let counts = items.iter().fold([0; 6], counter);
12✔
257
        // also check the n + 1 item
258
        let counts = counter(counts, &item);
12✔
259

260
        // then filter those with more than 0
261
        let num_distinct_variants = counts.iter().filter(|&c| *c > 0).count();
72✔
262

263
        if num_distinct_variants > 1 {
12✔
264
            return Err(ScriptError::InvalidInput);
2✔
265
        }
10✔
266

267
        Ok(items.contains(&item))
10✔
268
    }
13✔
269

270
    /// Pops the top n items in the stack. If the stack has at least n items, `pop_num_items` returns the items in stack
271
    /// order (ie. bottom first), otherwise returns an error.
272
    pub fn pop_num_items(&mut self, num_items: usize) -> Result<Vec<StackItem>, ScriptError> {
67✔
273
        let stack_size = self.size();
67✔
274

275
        if stack_size < num_items {
67✔
276
            Err(ScriptError::StackUnderflow)
2✔
277
        } else {
278
            let at = stack_size - num_items;
65✔
279
            let items = self.items.split_off(at);
65✔
280

281
            Ok(items)
65✔
282
        }
283
    }
67✔
284

285
    /// Return a binary array representation of the input stack
286
    pub fn to_bytes(&self) -> Vec<u8> {
8,783✔
287
        self.items.iter().fold(Vec::new(), |mut bytes, item| {
16,487✔
288
            item.to_bytes(&mut bytes);
16,487✔
289
            bytes
16,487✔
290
        })
16,487✔
291
    }
8,783✔
292

293
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, ScriptError> {
6,691✔
294
        let mut stack = ExecutionStack { items: Vec::new() };
6,691✔
295
        let mut byte_str = bytes;
6,691✔
296
        while !byte_str.is_empty() {
19,944✔
297
            match StackItem::read_next(byte_str) {
13,253✔
298
                Some((item, b)) => {
13,253✔
299
                    stack.push(item)?;
13,253✔
300
                    byte_str = b;
13,253✔
301
                },
302
                None => return Err(ScriptError::InvalidInput),
×
303
            }
304
        }
305
        Ok(stack)
6,691✔
306
    }
6,691✔
307

308
    /// Pushes the item onto the top of the stack. This function will only error if the new stack size exceeds the
309
    /// maximum allowed stack size, given by [MAX_STACK_SIZE]
310
    pub fn push(&mut self, item: StackItem) -> Result<(), ScriptError> {
13,670✔
311
        if self.size() >= MAX_STACK_SIZE {
13,670✔
312
            return Err(ScriptError::StackOverflow);
1✔
313
        }
13,669✔
314
        self.items.push(item);
13,669✔
315
        Ok(())
13,669✔
316
    }
13,670✔
317

318
    /// Pushes the top stack element down `depth` positions
319
    pub(crate) fn push_down(&mut self, depth: usize) -> Result<(), ScriptError> {
×
320
        let n = self.size();
×
321
        if n < depth + 1 {
×
322
            return Err(ScriptError::StackUnderflow);
×
323
        }
×
324
        if depth == 0 {
×
325
            return Ok(());
×
326
        }
×
327
        let top = self.pop().unwrap();
×
328
        self.items.insert(n - depth - 1, top);
×
329
        Ok(())
×
330
    }
×
331
}
332

333
impl Hex for ExecutionStack {
334
    fn from_hex(hex: &str) -> Result<Self, HexError>
3✔
335
    where Self: Sized {
3✔
336
        let b = from_hex(hex)?;
3✔
337
        ExecutionStack::from_bytes(&b).map_err(|_| HexError::HexConversionError {})
3✔
338
    }
3✔
339

340
    fn to_hex(&self) -> String {
2✔
341
        to_hex(&self.to_bytes())
2✔
342
    }
2✔
343
}
344

345
/// Utility function that given a count of `StackItem` variants, adds 1 for the given item.
346
#[allow(clippy::many_single_char_names)]
347
fn counter(values: [u8; 6], item: &StackItem) -> [u8; 6] {
36✔
348
    let [n, h, c, p, s, z] = values;
36✔
349
    #[allow(clippy::enum_glob_use)]
350
    use StackItem::*;
351
    match item {
36✔
352
        Number(_) => {
353
            let n = n + 1;
28✔
354
            [n, h, c, p, s, z]
28✔
355
        },
356
        Hash(_) => {
357
            let h = h + 1;
×
358
            [n, h, c, p, s, z]
×
359
        },
360
        Commitment(_) => {
361
            let c = c + 1;
×
362
            [n, h, c, p, s, z]
×
363
        },
364
        PublicKey(_) => {
365
            let p = p + 1;
8✔
366
            [n, h, c, p, s, z]
8✔
367
        },
368
        Signature(_) => {
369
            let s = s + 1;
×
370
            [n, h, c, p, s, z]
×
371
        },
372
        Scalar(_) => {
373
            let z = z + 1;
×
374
            [n, h, c, p, s, z]
×
375
        },
376
    }
377
}
36✔
378

379
#[cfg(test)]
380
mod test {
381
    use blake2::Blake2b;
382
    use borsh::{BorshDeserialize, BorshSerialize};
383
    use digest::{Digest, consts::U32};
384
    use tari_crypto::{
385
        compressed_commitment::CompressedCommitment,
386
        compressed_key::CompressedKey,
387
        keys::SecretKey,
388
        ristretto::{RistrettoPublicKey, RistrettoSecretKey},
389
    };
390
    use tari_utilities::{
391
        hex::{Hex, from_hex},
392
        message_format::MessageFormat,
393
    };
394

395
    use crate::{
396
        CheckSigSchnorrSignature,
397
        CompressedCheckSigSchnorrSignature,
398
        ExecutionStack,
399
        HashValue,
400
        StackItem,
401
        op_codes::ScalarValue,
402
    };
403

404
    #[test]
405
    fn as_bytes_roundtrip() {
1✔
406
        use crate::StackItem::{Number, PublicKey, Signature};
407
        let k = RistrettoSecretKey::random(&mut rand::rng());
1✔
408
        let p = CompressedKey::<RistrettoPublicKey>::from_secret_key(&k);
1✔
409
        let s = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
410
            CheckSigSchnorrSignature::sign(&k, b"hi", &mut rand::rng()).unwrap(),
1✔
411
        );
412
        let items = vec![Number(5432), Number(21), Signature(s), PublicKey(p)];
1✔
413
        let stack = ExecutionStack::new(items);
1✔
414
        let bytes = stack.to_bytes();
1✔
415
        let stack2 = ExecutionStack::from_bytes(&bytes).unwrap();
1✔
416
        assert_eq!(stack, stack2);
1✔
417
    }
1✔
418

419
    #[test]
420
    fn deserialisation() {
1✔
421
        let k =
1✔
422
            RistrettoSecretKey::from_hex("7212ac93ee205cdbbb57c4f0f815fbf8db25b4d04d3532e2262e31907d82c700").unwrap();
1✔
423
        let r =
1✔
424
            RistrettoSecretKey::from_hex("193ee873f3de511eda8ae387db6498f3d194d31a130a94cdf13dc5890ec1ad0f").unwrap();
1✔
425
        let p = CompressedKey::<RistrettoPublicKey>::from_secret_key(&k);
1✔
426
        let m = [1u8; 32];
1✔
427
        let sig = CompressedCheckSigSchnorrSignature::new_from_schnorr(
1✔
428
            CheckSigSchnorrSignature::sign_with_nonce_and_message(&k, r, m).unwrap(),
1✔
429
        );
430
        let inputs = inputs!(sig, p, m as HashValue);
1✔
431
        assert_eq!(inputs.to_hex(),
1✔
432
        "0500f7c695528c858cde76dab3076908e01228b6dbdd5f671bed1b03b89e170c31c6134be1c65544fa3f26c59903165f664db0dc364cbbaa4b35a9b33342cc01000456c0fa32558d6edc0916baa26b48e745de834571534ca253ea82435f08ebbc7c060101010101010101010101010101010101010101010101010101010101010101");
433
    }
1✔
434

435
    #[test]
436
    fn serialisation() {
1✔
437
        // let p =
438
        //     RistrettoPublicKey::from_hex("56c0fa32558d6edc0916baa26b48e745de834571534ca253ea82435f08ebbc7c").
439
        // unwrap(); let r =
440
        //     RistrettoPublicKey::from_hex("00f7c695528c858cde76dab3076908e01228b6dbdd5f671bed1b03b89e170c31").
441
        // unwrap(); let s =
442
        //     RistrettoSecretKey::from_hex("6db1023d5c46d78a97da8eb6c5a37e00d5f2fee182dcb38c1b6c65e90a43c109").
443
        // unwrap(); let sig = RistrettoSchnorr::new(r, s);
444
        // let m: HashValue = Blake2b::<U32>::digest(b"Hello Tari Script").into();
445
        // let inputs = inputs!(m, sig, p);
446
        // eprintln!("to_hex(&m) = {:?}", tari_utilities::hex::to_hex(&m));
447
        // eprintln!("inputs.to_hex() = {:?}", inputs.to_hex());
448

449
        let s = "06fdf9fc345d2cdd8aff624a55f824c7c9ce3cc972e011b4e750e417a90ecc5da50500f7c695528c858cde76dab3076908e0122\
1✔
450
        8b6dbdd5f671bed1b03b89e170c316db1023d5c46d78a97da8eb6c5a37e00d5f2fee182dcb38c1b6c65e90a43c1090456c0fa32558d6edc0916baa2\
1✔
451
        6b48e745de834571534ca253ea82435f08ebbc7c";
1✔
452
        let mut stack = ExecutionStack::from_hex(s).unwrap();
1✔
453
        assert_eq!(stack.size(), 3);
1✔
454
        if let Some(StackItem::PublicKey(p)) = stack.pop() {
1✔
455
            assert_eq!(
1✔
456
                p.to_hex(),
1✔
457
                "56c0fa32558d6edc0916baa26b48e745de834571534ca253ea82435f08ebbc7c"
458
            );
459
        } else {
460
            panic!("Expected pubkey")
×
461
        }
462
        if let Some(StackItem::Signature(s)) = stack.pop() {
1✔
463
            assert_eq!(
1✔
464
                s.get_compressed_public_nonce().to_hex(),
1✔
465
                "00f7c695528c858cde76dab3076908e01228b6dbdd5f671bed1b03b89e170c31"
466
            );
467
            assert_eq!(
1✔
468
                s.get_signature().to_hex(),
1✔
469
                "6db1023d5c46d78a97da8eb6c5a37e00d5f2fee182dcb38c1b6c65e90a43c109"
470
            );
471
        } else {
472
            panic!("Expected signature")
×
473
        }
474
        if let Some(StackItem::Scalar(s)) = stack.pop() {
1✔
475
            assert_eq!(
1✔
476
                s.as_slice(),
1✔
477
                from_hex("fdf9fc345d2cdd8aff624a55f824c7c9ce3cc972e011b4e750e417a90ecc5da5").unwrap()
1✔
478
            );
479
        } else {
480
            panic!("Expected scalar")
×
481
        }
482
    }
1✔
483

484
    #[test]
485
    fn serde_serialization_non_breaking() {
1✔
486
        const SERDE_ENCODED_BYTES: &str = "ce0000000000000006fdf9fc345d2cdd8aff624a55f824c7c9ce3cc9\
487
        72e011b4e750e417a90ecc5da50456c0fa32558d6edc0916baa26b48e745de834571534ca253ea82435f08ebbc\
488
        7c0556c0fa32558d6edc0916baa26b48e745de834571534ca253ea82435f08ebbc7c6db1023d5c46d78a97da8eb\
489
        6c5a37e00d5f2fee182dcb38c1b6c65e90a43c10906fdf9fc345d2cdd8aff624a55f824c7c9ce3cc972e011b4e7\
490
        50e417a90ecc5da501d2040000000000000356c0fa32558d6edc0916baa26b48e745de834571534ca253ea82435\
491
        f08ebbc7c";
492
        let p = CompressedKey::<RistrettoPublicKey>::from_hex(
1✔
493
            "56c0fa32558d6edc0916baa26b48e745de834571534ca253ea82435f08ebbc7c",
1✔
494
        )
495
        .unwrap();
1✔
496
        let s =
1✔
497
            RistrettoSecretKey::from_hex("6db1023d5c46d78a97da8eb6c5a37e00d5f2fee182dcb38c1b6c65e90a43c109").unwrap();
1✔
498
        let sig = CompressedCheckSigSchnorrSignature::new(p.clone(), s);
1✔
499
        let m: HashValue = Blake2b::<U32>::digest(b"Hello Tari Script").into();
1✔
500
        let s: ScalarValue = m;
1✔
501
        let commitment = CompressedCommitment::<RistrettoPublicKey>::from_compressed_key(p.clone());
1✔
502

503
        // Includes all variants for StackItem
504
        let mut expected_inputs = inputs!(s, p, sig, m, 1234, commitment);
1✔
505
        let stack = ExecutionStack::from_binary(&from_hex(SERDE_ENCODED_BYTES).unwrap()).unwrap();
1✔
506

507
        for (i, item) in stack.items.into_iter().enumerate().rev() {
6✔
508
            assert_eq!(
6✔
509
                item,
510
                expected_inputs.pop().unwrap(),
6✔
511
                "Stack items did not match at index {i}"
512
            );
513
        }
514

515
        assert!(expected_inputs.is_empty());
1✔
516
    }
1✔
517

518
    #[test]
519
    fn test_borsh_de_serialization() {
1✔
520
        let s = "06fdf9fc345d2cdd8aff624a55f824c7c9ce3cc972e011b4e750e417a90ecc5da50500f7c695528c858cde76dab3076908e0122\
1✔
521
        8b6dbdd5f671bed1b03b89e170c316db1023d5c46d78a97da8eb6c5a37e00d5f2fee182dcb38c1b6c65e90a43c1090456c0fa32558d6edc0916baa2\
1✔
522
        6b48e745de834571534ca253ea82435f08ebbc7c";
1✔
523
        let stack = ExecutionStack::from_hex(s).unwrap();
1✔
524
        let mut buf = Vec::new();
1✔
525
        stack.serialize(&mut buf).unwrap();
1✔
526
        buf.extend_from_slice(&[1, 2, 3]);
1✔
527
        let buf = &mut buf.as_slice();
1✔
528
        assert_eq!(stack, ExecutionStack::deserialize(buf).unwrap());
1✔
529
        assert_eq!(buf, &[1, 2, 3]);
1✔
530
    }
1✔
531

532
    #[test]
533
    fn test_borsh_de_serialization_too_large() {
1✔
534
        // We dont care about the actual stack here, just that its not too large on the varint size
535
        // We lie about the size to try and get a mem panic, and say this stack is u64::max large.
536
        let buf = vec![255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 49, 8, 2, 5, 6];
1✔
537
        let buf = &mut buf.as_slice();
1✔
538
        assert!(ExecutionStack::deserialize(buf).is_err());
1✔
539
    }
1✔
540
}
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