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}