aluvm/core/
core.rs

1// Reference rust implementation of AluVM (arithmetic logic unit virtual machine).
2// To find more on AluVM please check <https://aluvm.org>
3//
4// SPDX-License-Identifier: Apache-2.0
5//
6// Designed in 2021-2025 by Dr Maxim Orlovsky <orlovsky@ubideco.org>
7// Written in 2021-2025 by Dr Maxim Orlovsky <orlovsky@ubideco.org>
8//
9// Copyright (C) 2021-2024 LNP/BP Standards Association, Switzerland.
10// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO),
11//                         Institute for Distributed and Cognitive Systems (InDCS), Switzerland.
12// Copyright (C) 2021-2025 Dr Maxim Orlovsky.
13// All rights under the above copyrights are reserved.
14//
15// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
16// in compliance with the License. You may obtain a copy of the License at
17//
18//        http://www.apache.org/licenses/LICENSE-2.0
19//
20// Unless required by applicable law or agreed to in writing, software distributed under the License
21// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
22// or implied. See the License for the specific language governing permissions and limitations under
23// the License.
24
25use core::fmt::{self, Debug, Formatter};
26
27use amplify::confinement::ConfinedVec;
28
29use super::{Site, SiteId, Status};
30use crate::{Register, LIB_NAME_ALUVM};
31
32/// Maximal size of the call stack.
33///
34/// Equals to 0xFFFF (i.e., maximum limited by `cy` and `cp` bit size).
35pub const CALL_STACK_SIZE_MAX: u16 = 0xFF;
36
37/// Extension to the AluVM core provided by an ISA.
38pub trait CoreExt: Clone + Debug {
39    /// A type of registers provided by the ISA.
40    type Reg: Register;
41    /// A configuration used in initializing the core extension.
42    type Config: Default;
43
44    /// Constructs the core extensions to be added to AluVM core.
45    fn with(config: Self::Config) -> Self;
46
47    /// Read the value of a register.
48    fn get(&self, reg: Self::Reg) -> Option<<Self::Reg as Register>::Value>;
49
50    /// Clear the register by setting it to `None`.
51    fn clr(&mut self, reg: Self::Reg);
52
53    /// Set the register to the provided value.
54    fn set(&mut self, reg: Self::Reg, val: <Self::Reg as Register>::Value) {
55        self.put(reg, Some(val))
56    }
57
58    /// Put either a value or None to the register.
59    fn put(&mut self, reg: Self::Reg, val: Option<<Self::Reg as Register>::Value>);
60
61    /// Reset the core extension by setting all the registers to `None`.
62    fn reset(&mut self);
63}
64
65/// A trait for the external part of AluVM core which can operate with core ISA extensions.
66pub trait Supercore<Subcore> {
67    /// An ISA extension subcore.
68    fn subcore(&self) -> Subcore;
69
70    /// Merge the values generated in the subcore ISA extension with the main core.
71    fn merge_subcore(&mut self, subcore: Subcore);
72}
73
74/// Registers of a single CPU/VM core.
75#[derive(Clone)]
76pub struct Core<
77    Id: SiteId,
78    Cx: CoreExt,
79    const CALL_STACK_SIZE: usize = { CALL_STACK_SIZE_MAX as usize },
80> {
81    /// Halt register. If set to `true`, halts program when `CK` is set to [`Status::Failed`] for
82    /// the first time.
83    ///
84    /// # See also
85    ///
86    /// - [`Core::ck`] register
87    /// - [`Core::cf`] register
88    pub(super) ch: bool,
89
90    /// Check register, which is set on any failure (accessing register in `None` state, zero
91    /// division, etc.). Can be reset.
92    ///
93    /// # See also
94    ///
95    /// - [`Core::ch`] register
96    /// - [`Core::cf`] register
97    pub(super) ck: Status,
98
99    /// Failure register, which counts how many times `CK` was set, and can't be reset.
100    ///
101    /// # See also
102    ///
103    /// - [`Core::ch`] register
104    /// - [`Core::ck`] register
105    pub(super) cf: u64,
106
107    /// Test register, which acts as a boolean test result (also a carry flag).
108    pub(super) co: Status,
109
110    /// Counts number of jumps (possible cycles). The number of jumps is limited by 2^16 per
111    /// script.
112    pub(super) cy: u16,
113
114    /// Complexity accumulator / counter.
115    ///
116    /// Each instruction has an associated computational complexity level. This register sums
117    /// the complexity of executed instructions.
118    ///
119    /// # See also
120    ///
121    /// - [`Core::cy`] register
122    /// - [`Core::cl`] register
123    pub(super) ca: u64,
124
125    /// Complexity limit.
126    ///
127    /// If this register has a value set, once [`Core::ca`] reaches this value, the VM will stop
128    /// program execution setting `CK` to a failure.
129    pub(super) cl: Option<u64>,
130
131    /// Call stack.
132    ///
133    /// # See also
134    ///
135    /// - [`CALL_STACK_SIZE_MAX`] constant
136    /// - [`Core::cp`] register
137    pub(super) cs: ConfinedVec<Site<Id>, 0, CALL_STACK_SIZE>,
138
139    /// Core extension module.
140    pub cx: Cx,
141}
142
143/// Configuration for [`Core`] initialization.
144#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Debug)]
145#[derive(StrictType, StrictEncode, StrictDecode)]
146#[strict_type(lib = LIB_NAME_ALUVM)]
147#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
148pub struct CoreConfig {
149    /// Initial value for the `CH` register.
150    pub halt: bool,
151    /// Initial value for the `CL` register.
152    pub complexity_lim: Option<u64>,
153}
154
155impl Default for CoreConfig {
156    /// Sets
157    /// - [`CoreConfig::halt`] to `true`,
158    /// - [`CoreConfig::complexity_lim`] to `None`
159    ///
160    /// # See also
161    ///
162    /// - [`CoreConfig::halt`]
163    /// - [`CoreConfig::complexity_lim`]
164    fn default() -> Self { CoreConfig { halt: true, complexity_lim: None } }
165}
166
167impl<Id: SiteId, Cx: CoreExt, const CALL_STACK_SIZE: usize> Default
168    for Core<Id, Cx, CALL_STACK_SIZE>
169{
170    fn default() -> Self { Core::new() }
171}
172
173impl<Id: SiteId, Cx: CoreExt, const CALL_STACK_SIZE: usize> Core<Id, Cx, CALL_STACK_SIZE> {
174    /// Initializes registers. Sets `CK` to `true`, counters to zero, call stack to empty and the
175    /// rest of registers to `None` value.
176    ///
177    /// An alias for [`Core::with`]`(`[`CoreConfig::default()`]`, Cx::default())`.
178    #[inline]
179    pub fn new() -> Self {
180        assert!(CALL_STACK_SIZE <= CALL_STACK_SIZE_MAX as usize, "Call stack size is too large");
181        Core::with(default!(), default!())
182    }
183
184    /// Initializes registers using a configuration object [`CoreConfig`].
185    pub fn with(config: CoreConfig, cx_config: Cx::Config) -> Self {
186        assert!(CALL_STACK_SIZE <= CALL_STACK_SIZE_MAX as usize, "Call stack size is too large");
187        Core {
188            ch: config.halt,
189            ck: Status::Ok,
190            cf: 0,
191            co: Status::Ok,
192            cy: 0,
193            ca: 0,
194            cl: config.complexity_lim,
195            cs: ConfinedVec::with_capacity(CALL_STACK_SIZE),
196            cx: Cx::with(cx_config),
197        }
198    }
199
200    /// Reset the core extension by setting all the registers to `None`.
201    pub fn reset(&mut self) {
202        let mut new = Self::new();
203        new.ch = self.ch;
204        new.cl = self.cl;
205        new.cx.reset();
206        *self = new;
207    }
208}
209
210#[cfg_attr(coverage_nightly, coverage(off))]
211impl<Id: SiteId, Cx: CoreExt, const CALL_STACK_SIZE: usize> Debug
212    for Core<Id, Cx, CALL_STACK_SIZE>
213{
214    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
215        let (sect, reg, val, reset) = if f.alternate() {
216            ("\x1B[0;4;1m", "\x1B[0;1m", "\x1B[0;32m", "\x1B[0m")
217        } else {
218            ("", "", "", "")
219        };
220
221        writeln!(f, "{sect}C-regs:{reset}")?;
222        write!(f, "{reg}CH{reset} {val}{}{reset}, ", self.ch)?;
223        write!(f, "{reg}CK{reset} {val}{}{reset}, ", self.ck)?;
224        write!(f, "{reg}CF{reset} {val}{}{reset}, ", self.cf)?;
225        write!(f, "{reg}CO{reset} {val}{}{reset}, ", self.co)?;
226        write!(f, "{reg}CY{reset} {val}{}{reset}, ", self.cy)?;
227        write!(f, "{reg}CA{reset} {val}{}{reset}, ", self.ca)?;
228        let cl = self
229            .cl
230            .map(|v| v.to_string())
231            .unwrap_or_else(|| "~".to_string());
232        write!(f, "{reg}CL{reset} {val}{cl}{reset}, ")?;
233        write!(f, "{reg}CP{reset} {val}{}{reset}, ", self.cp())?;
234        write!(f, "\n{reg}CS{reset} {val}{reset}")?;
235        for item in &self.cs {
236            write!(f, "{}   ", item)?;
237        }
238        writeln!(f)?;
239
240        Debug::fmt(&self.cx, f)
241    }
242}
243
244impl<Id: SiteId, Cx: CoreExt + Supercore<Cx2>, Cx2: CoreExt, const CALL_STACK_SIZE: usize>
245    Supercore<Core<Id, Cx2, CALL_STACK_SIZE>> for Core<Id, Cx, CALL_STACK_SIZE>
246{
247    fn subcore(&self) -> Core<Id, Cx2, CALL_STACK_SIZE> {
248        Core {
249            ch: self.ch,
250            ck: self.ck,
251            cf: self.cf,
252            co: self.co,
253            cy: self.cy,
254            ca: self.ca,
255            cl: self.cl,
256            cs: self.cs.clone(),
257            cx: self.cx.subcore(),
258        }
259    }
260
261    fn merge_subcore(&mut self, subcore: Core<Id, Cx2, CALL_STACK_SIZE>) {
262        assert_eq!(self.ch, subcore.ch);
263        self.ck = subcore.ck;
264        self.co = subcore.co;
265        self.cf = subcore.cf;
266        self.cy = subcore.cy;
267        self.ca = subcore.ca;
268        assert_eq!(self.cl, subcore.cl);
269        self.cs = subcore.cs;
270        self.cx.merge_subcore(subcore.cx);
271    }
272}