Skip to content

Commit

Permalink
Merge pull request #111 from urbit/as/mink-scry
Browse files Browse the repository at this point in the history
Handle scries in Ares with virtual Nock 12
  • Loading branch information
ashelkovnykov authored Oct 26, 2023
2 parents 3f4bd26 + 15e0b53 commit 4c7d79b
Show file tree
Hide file tree
Showing 15 changed files with 1,434 additions and 1,077 deletions.
665 changes: 395 additions & 270 deletions rust/ares/src/interpreter.rs

Large diffs are not rendered by default.

148 changes: 88 additions & 60 deletions rust/ares/src/jets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub mod sort;
pub mod text;
pub mod tree;

use crate::interpreter::Context;
use crate::interpreter::{Context, Error};
use crate::jets::bits::*;
use crate::jets::cold::Cold;
use crate::jets::form::*;
Expand All @@ -24,7 +24,8 @@ use crate::jets::text::*;
use crate::jets::tree::*;
use crate::jets::warm::Warm;
use crate::mem::NockStack;
use crate::noun::{self, Noun, Slots};
use crate::newt::Newt;
use crate::noun::{self, Noun, Slots, D};
use ares_macros::tas;
use std::cmp;

Expand All @@ -38,23 +39,31 @@ pub type Jet = fn(&mut Context, Noun) -> Result;
* Only return a deterministic error if the Nock would have deterministically
* crashed.
*/
#[derive(Debug, PartialEq)]
#[derive(Clone, Copy, Debug)]
pub enum JetErr {
Punt, // Retry with the raw nock
Deterministic, // The Nock would have crashed
NonDeterministic, // Other error
Punt, // Retry with the raw nock
Fail(Error), // Error; do not retry
}

impl From<noun::Error> for JetErr {
fn from(_err: noun::Error) -> Self {
Self::Deterministic
Self::Fail(Error::Deterministic(D(0)))
}
}

impl From<JetErr> for () {
fn from(_: JetErr) -> Self {}
}

impl From<JetErr> for Error {
fn from(e: JetErr) -> Self {
match e {
JetErr::Fail(f) => f,
JetErr::Punt => panic!("unhandled JetErr::Punt"),
}
}
}

pub fn get_jet(jet_name: Noun) -> Option<Jet> {
match jet_name.as_direct().ok()?.data() {
tas!(b"add") => Some(jet_add),
Expand Down Expand Up @@ -135,20 +144,21 @@ pub mod util {
pub fn checked_add(a: usize, b: usize) -> result::Result<usize, JetErr> {
a.checked_add(b)
.filter(|x| x <= &MAX_BIT_LENGTH)
.ok_or(JetErr::NonDeterministic)
.ok_or(JetErr::Fail(Error::NonDeterministic(D(0))))
}

/// Performs addition that returns None on Noun size overflow
pub fn checked_sub(a: usize, b: usize) -> result::Result<usize, JetErr> {
a.checked_sub(b).ok_or(JetErr::NonDeterministic)
a.checked_sub(b)
.ok_or(JetErr::Fail(Error::NonDeterministic(D(0))))
}

pub fn checked_left_shift(bloq: usize, a: usize) -> result::Result<usize, JetErr> {
let res = a << bloq;

// Catch overflow
if (res >> bloq) < a || res > MAX_BIT_LENGTH {
Err(JetErr::NonDeterministic)
Err(JetErr::Fail(Error::NonDeterministic(D(0))))
} else {
Ok(res)
}
Expand All @@ -165,14 +175,15 @@ pub mod util {
}

pub fn slot(noun: Noun, axis: u64) -> Result {
noun.slot(axis).map_err(|_e| JetErr::Deterministic)
noun.slot(axis)
.map_err(|_e| JetErr::Fail(Error::Deterministic(D(0))))
}

/// Extract a bloq and check that it's computable by the current system
pub fn bloq(a: Noun) -> result::Result<usize, JetErr> {
let bloq = a.as_direct()?.data() as usize;
if bloq >= 47 {
Err(JetErr::NonDeterministic)
Err(JetErr::Fail(Error::NonDeterministic(D(0))))
} else {
Ok(bloq)
}
Expand Down Expand Up @@ -307,8 +318,23 @@ pub mod util {
use assert_no_alloc::assert_no_alloc;
use ibig::UBig;

pub fn init_stack() -> NockStack {
NockStack::new(8 << 10 << 10, 0)
pub fn init_context() -> Context {
let mut stack = NockStack::new(8 << 10 << 10, 0);
let newt = Newt::new_mock();
let cold = Cold::new(&mut stack);
let warm = Warm::new();
let hot = Hot::init(&mut stack);
let cache = Hamt::<Noun>::new();

Context {
stack,
newt,
cold,
warm,
hot,
cache,
scry_stack: D(0),
}
}

#[allow(non_snake_case)]
Expand All @@ -321,51 +347,25 @@ pub mod util {
assert!(eq, "got: {}, need: {}", a, b);
}

pub fn assert_jet(stack: &mut NockStack, jet: Jet, sam: Noun, res: Noun) {
// XX: consider making a mock context singleton that tests can use
let mut cache = Hamt::<Noun>::new();
let mut cold = Cold::new(stack);
let mut warm = Warm::new();
let hot = Hot::init(stack);
let mut context = Context {
stack,
newt: None,
cache: &mut cache,
cold: &mut cold,
warm: &mut warm,
hot: &hot,
};
let sam = T(context.stack, &[D(0), sam, D(0)]);
let jet_res = assert_no_alloc(|| jet(&mut context, sam).unwrap());
assert_noun_eq(stack, jet_res, res);
pub fn assert_jet(context: &mut Context, jet: Jet, sam: Noun, res: Noun) {
let sam = T(&mut context.stack, &[D(0), sam, D(0)]);
let jet_res = assert_no_alloc(|| jet(context, sam).unwrap());
assert_noun_eq(&mut context.stack, jet_res, res);
}

pub fn assert_jet_ubig(stack: &mut NockStack, jet: Jet, sam: Noun, res: UBig) {
let res = A(stack, &res);
assert_jet(stack, jet, sam, res);
pub fn assert_jet_ubig(context: &mut Context, jet: Jet, sam: Noun, res: UBig) {
let res = A(&mut context.stack, &res);
assert_jet(context, jet, sam, res);
}

pub fn assert_nary_jet_ubig(stack: &mut NockStack, jet: Jet, sam: &[Noun], res: UBig) {
let sam = T(stack, sam);
assert_jet_ubig(stack, jet, sam, res);
pub fn assert_nary_jet_ubig(context: &mut Context, jet: Jet, sam: &[Noun], res: UBig) {
let sam = T(&mut context.stack, sam);
assert_jet_ubig(context, jet, sam, res);
}

pub fn assert_jet_err(stack: &mut NockStack, jet: Jet, sam: Noun, err: JetErr) {
// XX: consider making a mock context singleton that tests can use
let mut cache = Hamt::<Noun>::new();
let mut cold = Cold::new(stack);
let mut warm = Warm::new();
let hot = Hot::init(stack);
let mut context = Context {
stack,
newt: None,
cache: &mut cache,
cold: &mut cold,
warm: &mut warm,
hot: &hot,
};
let sam = T(context.stack, &[D(0), sam, D(0)]);
let jet_res = jet(&mut context, sam);
pub fn assert_jet_err(context: &mut Context, jet: Jet, sam: Noun, err: JetErr) {
let sam = T(&mut context.stack, &[D(0), sam, D(0)]);
let jet_res = jet(context, sam);
assert!(
jet_res.is_err(),
"with sample: {}, expected err: {:?}, got: {:?}",
Expand All @@ -374,23 +374,51 @@ pub mod util {
&jet_res
);
let jet_err = jet_res.unwrap_err();
assert_eq!(
jet_err, err,
"with sample: {}, expected err: {:?}, got: {:?}",
sam, err, jet_err
);
match (jet_err, err) {
(JetErr::Punt, JetErr::Punt) => {}
(JetErr::Fail(actual_err), JetErr::Fail(expected_err)) => {
match (actual_err, expected_err) {
(Error::ScryBlocked(mut actual), Error::ScryBlocked(mut expected))
| (Error::ScryCrashed(mut actual), Error::ScryCrashed(mut expected))
| (Error::Deterministic(mut actual), Error::Deterministic(mut expected))
| (
Error::NonDeterministic(mut actual),
Error::NonDeterministic(mut expected),
) => unsafe {
assert!(unifying_equality(
&mut context.stack,
&mut actual,
&mut expected
));
},
_ => {
panic!(
"with sample: {}, expected err: {:?}, got: {:?}",
sam, expected_err, actual_err
);
}
}
}
_ => {
panic!(
"with sample: {}, expected err: {:?}, got: {:?}",
sam, err, jet_err
);
}
}
}
}

#[cfg(test)]
mod tests {
use super::test::{init_stack, A};
use super::test::{init_context, A};
use super::*;
use ibig::ubig;

#[test]
fn test_met() {
let s = &mut init_stack();
let c = &mut init_context();
let s = &mut c.stack;

let a = A(s, &ubig!(0xdeadbeef12345678fedcba9876543210))
.as_atom()
Expand Down
Loading

0 comments on commit 4c7d79b

Please sign in to comment.