Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement GM::GetVerifyingPredicate metadata instr #174

Merged
merged 3 commits into from
Jul 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 52 additions & 5 deletions src/context.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
//! VM runtime context definitions

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
use crate::predicate::RuntimePredicate;

use fuel_asm::Word;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
/// Runtime context description.
pub enum Context {
/// Current context is a predicate verification.
Predicate,
Predicate {
/// Predicate program to be executed
program: RuntimePredicate,
},
/// Current context is a script execution.
Script,
Script {
/// Block height of the context
block_height: u32,
},
/// Current context is under a `CALL` scop.e
Call,
Call {
/// Block height of the context
block_height: u32,
},
/// No transaction initialized/invalid context.
NotInitialized,
}
Expand All @@ -23,6 +36,40 @@ impl Default for Context {
impl Context {
/// Return `true` if the context is external; `false` otherwise.
pub const fn is_external(&self) -> bool {
matches!(self, Self::Predicate | Self::Script)
matches!(self, Self::Predicate { .. } | Self::Script { .. })
}

/// Return the program to be executed, if its a predicate
pub const fn predicate(&self) -> Option<&RuntimePredicate> {
match self {
Context::Predicate { program } => Some(program),
_ => None,
}
}

/// Return the block height from the context, if either script or call
pub const fn block_height(&self) -> Option<u32> {
match self {
Context::Script { block_height } | Context::Call { block_height } => Some(*block_height),
_ => None,
}
}

/// Update the context according to the provided frame pointer
pub fn update_from_frame_pointer(&mut self, fp: Word) {
match self {
Context::Script { block_height } if fp != 0 => {
*self = Self::Call {
block_height: *block_height,
}
}

Context::Call { block_height } if fp == 0 => {
*self = Self::Script {
block_height: *block_height,
}
}
_ => (),
}
}
}
1 change: 0 additions & 1 deletion src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ pub struct Interpreter<S> {
storage: S,
debugger: Debugger,
context: Context,
block_height: u32,
balances: RuntimeBalances,
#[cfg(feature = "profile-any")]
profiler: Profiler,
Expand Down
12 changes: 12 additions & 0 deletions src/interpreter/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,18 @@ where
self.inc_pc()
}

pub(crate) fn set_block_height(&mut self, ra: RegisterId) -> Result<(), RuntimeError> {
Self::is_register_writable(ra)?;

self.context
.block_height()
.map(|h| h as Word)
.map(|h| self.registers[ra] = h)
.ok_or(PanicReason::TransactionValidity)?;

self.inc_pc()
}

pub(crate) fn block_proposer(&mut self, a: Word) -> Result<(), RuntimeError> {
self.coinbase()
.and_then(|data| self.try_mem_write(a as usize, data.as_ref()))?;
Expand Down
1 change: 0 additions & 1 deletion src/interpreter/constructors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ impl<S> Interpreter<S> {
storage,
debugger: Debugger::default(),
context: Context::default(),
block_height: 0,
balances: RuntimeBalances::default(),
#[cfg(feature = "profile-any")]
profiler: Profiler::default(),
Expand Down
2 changes: 1 addition & 1 deletion src/interpreter/executors/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ where

OpcodeRepr::BHEI => {
self.gas_charge(GAS_BHEI)?;
self.alu_set(ra, self.block_height() as Word)?;
self.set_block_height(ra)?;
}

OpcodeRepr::BHSH => {
Expand Down
28 changes: 9 additions & 19 deletions src/interpreter/executors/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::consts::*;
use crate::crypto;
use crate::error::InterpreterError;
use crate::interpreter::{Interpreter, MemoryRange};
use crate::interpreter::Interpreter;
use crate::predicate::RuntimePredicate;
use crate::prelude::*;
use crate::state::{ExecuteState, ProgramState, StateTransitionRef};
use crate::storage::{InterpreterStorage, PredicateStorage};
Expand Down Expand Up @@ -32,12 +33,12 @@ impl Interpreter<PredicateStorage> {
return false;
}

let predicates: Vec<MemoryRange> = tx
let predicates: Vec<RuntimePredicate> = tx
.as_ref()
.inputs()
.iter()
.enumerate()
.filter_map(|(idx, _)| vm.input_to_predicate(&tx, idx))
.filter_map(|(idx, _)| RuntimePredicate::from_tx(&params, tx.as_ref(), idx))
.collect();

predicates
Expand All @@ -56,28 +57,17 @@ impl Interpreter<PredicateStorage> {
pub fn check_predicate(&mut self, tx: CheckedTransaction, idx: usize) -> bool {
tx.as_ref()
.check_predicate_owner(idx)
.then(|| self.input_to_predicate(&tx, idx))
.then(|| RuntimePredicate::from_tx(self.params(), tx.as_ref(), idx))
.flatten()
.map(|predicate| self.init_predicate(tx) && self._check_predicate(predicate))
.unwrap_or(false)
}

fn init_predicate(&mut self, tx: CheckedTransaction) -> bool {
let block_height = 0;

self.init(true, block_height, tx).is_ok()
}

fn input_to_predicate(&self, tx: &CheckedTransaction, idx: usize) -> Option<MemoryRange> {
tx.as_ref()
.input_coin_predicate_offset(idx)
.map(|(ofs, len)| (ofs as Word + self.tx_offset() as Word, len as Word))
.map(|(ofs, len)| MemoryRange::new(ofs, len))
}

/// Validate the predicate, assuming the interpreter is initialized
fn _check_predicate(&mut self, predicate: MemoryRange) -> bool {
match self.verify_predicate(&predicate) {
fn _check_predicate(&mut self, predicate: RuntimePredicate) -> bool {
self.context = Context::Predicate { program: predicate };

match self.verify_predicate() {
Ok(ProgramState::Return(0x01)) => true,
_ => false,
}
Expand Down
12 changes: 7 additions & 5 deletions src/interpreter/executors/predicate.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
use crate::consts::*;
use crate::error::InterpreterError;
use crate::interpreter::{Interpreter, MemoryRange};
use crate::interpreter::Interpreter;
use crate::state::{ExecuteState, ProgramState};
use crate::storage::PredicateStorage;

use fuel_asm::PanicReason;

impl Interpreter<PredicateStorage> {
pub(crate) fn verify_predicate(&mut self, predicate: &MemoryRange) -> Result<ProgramState, InterpreterError> {
debug_assert!(self.is_predicate());

let (start, end) = predicate.boundaries(self);
pub(crate) fn verify_predicate(&mut self) -> Result<ProgramState, InterpreterError> {
let (start, end) = self
.context
.predicate()
.map(|p| p.program().boundaries(self))
.ok_or(InterpreterError::PredicateFailure)?;

self.registers[REG_PC] = start;
self.registers[REG_IS] = start;
Expand Down
26 changes: 15 additions & 11 deletions src/interpreter/flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,19 @@ impl<S> Interpreter<S> {
.checked_add(frame.context_gas())
.ok_or(RuntimeError::halt_on_bug("CGAS would overflow"))?;

frame
.registers()
.iter()
.enumerate()
.zip(self.registers.iter_mut())
.for_each(|((i, frame), current)| {
if i != REG_CGAS && i != REG_GGAS && i != REG_RET && i != REG_RETL {
*current = *frame;
}
});
let cgas = self.registers[REG_CGAS];
let ggas = self.registers[REG_GGAS];
let ret = self.registers[REG_RET];
let retl = self.registers[REG_RETL];

self.registers.copy_from_slice(&frame.registers());

self.registers[REG_CGAS] = cgas;
self.registers[REG_GGAS] = ggas;
self.registers[REG_RET] = ret;
self.registers[REG_RETL] = retl;

self.set_frame_pointer(self.registers[REG_FP]);
}

self.append_receipt(receipt);
Expand Down Expand Up @@ -182,7 +185,8 @@ where

let id = self.internal_contract_or_default();

self.registers[REG_FP] = self.registers[REG_SP];
self.set_frame_pointer(self.registers[REG_SP]);

self.registers[REG_SP] += len;
self.registers[REG_SSP] = self.registers[REG_SP];

Expand Down
19 changes: 13 additions & 6 deletions src/interpreter/initialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,9 @@ use std::io;

impl<S> Interpreter<S> {
/// Initialize the VM with a given transaction
pub fn init(&mut self, predicate: bool, block_height: u32, tx: CheckedTransaction) -> Result<(), InterpreterError> {
pub fn init(&mut self, tx: CheckedTransaction) -> Result<(), InterpreterError> {
self.tx = tx;

self.block_height = block_height;
self.context = if predicate { Context::Predicate } else { Context::Script };

self.frames.clear();
self.receipts.clear();

Expand Down Expand Up @@ -53,6 +50,15 @@ impl<S> Interpreter<S> {

Ok(())
}

/// Initialize the VM for a predicate context
pub fn init_predicate(&mut self, tx: CheckedTransaction) -> bool {
self.context = Context::Predicate {
program: Default::default(),
Copy link
Member

@Voxelot Voxelot Jul 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why use a default value here instead of actually setting the real predicate from an idx?

};

self.init(tx).is_ok()
}
}

impl<S> Interpreter<S>
Expand All @@ -64,9 +70,10 @@ where
///
/// For predicate verification, check [`Self::init`]
pub fn init_with_storage(&mut self, tx: CheckedTransaction) -> Result<(), InterpreterError> {
let predicate = false;
let block_height = self.storage.block_height().map_err(InterpreterError::from_io)?;

self.init(predicate, block_height, tx)
self.context = Context::Script { block_height };

self.init(tx)
}
}
20 changes: 9 additions & 11 deletions src/interpreter/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ impl<S> Interpreter<S> {
Ok(())
}

pub(crate) const fn block_height(&self) -> u32 {
self.block_height
}

pub(crate) fn set_flag(&mut self, a: Word) -> Result<(), RuntimeError> {
self.registers[REG_FLAG] = a;

Expand All @@ -55,12 +51,8 @@ impl<S> Interpreter<S> {
.map(|pc| self.registers[REG_PC] = pc)
}

pub(crate) const fn context(&self) -> Context {
if self.registers[REG_FP] == 0 {
self.context
} else {
Context::Call
}
pub(crate) const fn context(&self) -> &Context {
&self.context
}

pub(crate) const fn is_external_context(&self) -> bool {
Expand All @@ -72,7 +64,7 @@ impl<S> Interpreter<S> {
}

pub(crate) const fn is_predicate(&self) -> bool {
matches!(self.context, Context::Predicate)
matches!(self.context, Context::Predicate { .. })
}

pub(crate) const fn is_register_writable(ra: RegisterId) -> Result<(), RuntimeError> {
Expand Down Expand Up @@ -194,6 +186,12 @@ impl<S> Interpreter<S> {
// Safety: vm parameters guarantees enough space for txid
unsafe { Bytes32::as_ref_unchecked(&self.memory[..Bytes32::LEN]) }
}

pub(crate) fn set_frame_pointer(&mut self, fp: Word) {
self.context.update_from_frame_pointer(fp);

self.registers[REG_FP] = fp;
}
}

#[cfg(all(test, feature = "random"))]
Expand Down
10 changes: 10 additions & 0 deletions src/interpreter/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ pub struct MemoryRange {
len: Word,
}

impl Default for MemoryRange {
fn default() -> Self {
Self {
start: ops::Bound::Included(0),
end: ops::Bound::Excluded(0),
len: 0,
}
}
}

impl MemoryRange {
/// Create a new memory range represented as `[address, address + size[`.
pub const fn new(address: Word, size: Word) -> Self {
Expand Down
Loading