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

Change Outputs + TR + TRO #74

Merged
merged 32 commits into from
Jan 22, 2022
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
cf506dd
dedupe setting gas register
Voxelot Jan 5, 2022
faeff2b
add initial free_balance tracking
Voxelot Jan 6, 2022
fa3a8ae
incorporate new byte price model
Voxelot Jan 8, 2022
47d1080
add missing byte_price field. Update initialization
Voxelot Jan 10, 2022
23bb859
fix offset calculation, since we don't need to track free balances in…
Voxelot Jan 10, 2022
f642e58
fixup other tests
Voxelot Jan 11, 2022
763a2aa
move back to using vm memory to track available balances
Voxelot Jan 11, 2022
f539851
get basic tests working
Voxelot Jan 11, 2022
c14b575
actually use up gas
Voxelot Jan 11, 2022
bf6981e
more tests
Voxelot Jan 11, 2022
65cb4b1
cleanup tests
Voxelot Jan 12, 2022
1e6926e
implement TR opcode
Voxelot Jan 12, 2022
c967add
use stabilized const-panic
Voxelot Jan 12, 2022
8052ce8
use stabilized const-panic
Voxelot Jan 12, 2022
20922bb
stub tro internal method
Voxelot Jan 13, 2022
adb520f
add tests for transfer opcode
Voxelot Jan 14, 2022
bf7fcf2
refactor tests
Voxelot Jan 14, 2022
e6225a8
fix tests with re-addition of gas price and gas limit
Voxelot Jan 14, 2022
c421bab
refactor script_with_data_offset macro to crate root
Voxelot Jan 14, 2022
4cdb91d
make test builder available to all tests, add example for script offs…
Voxelot Jan 14, 2022
85d48cd
functional tests for transfer out
Voxelot Jan 19, 2022
e9c9257
finished tests for internal TR
Voxelot Jan 20, 2022
15334c9
fixup macro hygiene issue
Voxelot Jan 20, 2022
c99b871
update dep path
Voxelot Jan 20, 2022
a084ac1
fix import ordering
Voxelot Jan 20, 2022
7e9fde3
refactor contract setup for tests
Voxelot Jan 22, 2022
b0b3ce8
verify contracts in tx inputs actually exist
Voxelot Jan 22, 2022
1555e75
fix profiler tests with invalid contracts
Voxelot Jan 22, 2022
6e6e4b0
normalize gas_refund across create and script tx
Voxelot Jan 22, 2022
17702c1
Apply suggestions from code review
Voxelot Jan 22, 2022
fea72cf
use LEN instead of 32
Voxelot Jan 22, 2022
b5ed91d
use published version of fuel-tx
Voxelot Jan 22, 2022
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
9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,27 @@ dyn-clone = { version = "1.0", optional = true }
fuel-asm = "0.1"
fuel-merkle = "0.1"
fuel-storage = "0.1"
fuel-tx = "0.2"
fuel-tx = { git = "https://github.com/fuellabs/fuel-tx.git", branch = "master"}
Voxelot marked this conversation as resolved.
Show resolved Hide resolved
fuel-types = "0.1"
itertools = "0.10"
secp256k1 = { version = "0.20", features = ["recovery"] }
serde = { version = "1.0", features = ["derive"], optional = true }
sha3 = "0.9"
tracing = "0.1"
rand = {version = "0.8", optional = true }
adlerjohn marked this conversation as resolved.
Show resolved Hide resolved

[dev-dependencies]
rand = "0.8"
fuel-tx = { git = "https://github.com/fuellabs/fuel-tx.git", branch = "master", features = ["random"]}
fuel-vm = { path = ".", default-features = false, features = ["test-helpers"]}

[features]
debug = []
profile-gas = ["profile-any"]
profile-coverage = ["profile-any"]
profile-any = ["dyn-clone"] # All profiling features should depend on this
random = ["fuel-types/random", "fuel-tx/random"]
random = ["fuel-types/random", "fuel-tx/random", "rand"]
serde-types = ["fuel-asm/serde-types", "fuel-types/serde-types", "fuel-tx/serde-types", "serde"]
test-helpers = ["random"]

[[test]]
name = "test-backtrace"
Expand Down
9 changes: 9 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,15 @@ impl From<InstructionResult> for InterpreterError {
}
}

impl From<RuntimeError> for InterpreterError {
fn from(error: RuntimeError) -> Self {
match error {
RuntimeError::Recoverable(e) => Self::Panic(e),
RuntimeError::Halt(e) => Self::Io(e),
}
}
}

#[derive(Debug)]
#[cfg_attr(feature = "serde-types-minimal", derive(serde::Serialize, serde::Deserialize))]
/// Runtime error description that should either be specified in the protocol or
Expand Down
6 changes: 5 additions & 1 deletion src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ use crate::call::CallFrame;
use crate::consts::*;
use crate::context::Context;
use crate::state::Debugger;
use std::collections::HashMap;

use fuel_tx::{Receipt, Transaction};
use fuel_types::Word;
use fuel_types::{Color, Word};

mod alu;
mod blockchain;
Expand All @@ -22,6 +23,7 @@ mod internal;
mod log;
mod memory;
mod metadata;
mod post_execution;
mod transaction;

#[cfg(feature = "debug")]
Expand Down Expand Up @@ -54,6 +56,8 @@ pub struct Interpreter<S> {
block_height: u32,
#[cfg(feature = "profile-any")]
profiler: Profiler,
// track the offset for each unused balance in memory
unused_balance_index: HashMap<Color, usize>,
}

impl<S> Interpreter<S> {
Expand Down
1 change: 1 addition & 0 deletions src/interpreter/constructors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ impl<S> Interpreter<S> {
block_height: 0,
#[cfg(feature = "profile-any")]
profiler: Profiler::default(),
unused_balance_index: Default::default(),
}
}

Expand Down
125 changes: 124 additions & 1 deletion src/interpreter/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use crate::error::RuntimeError;
use crate::storage::InterpreterStorage;

use fuel_asm::{PanicReason, RegisterId, Word};
use fuel_types::{Color, ContractId};
use fuel_tx::Receipt;
use fuel_types::{Address, Color, ContractId};

use std::borrow::Cow;

Expand Down Expand Up @@ -44,6 +45,98 @@ where
self.inc_pc()
}

pub(crate) fn transfer(&mut self, a: Word, b: Word, c: Word) -> Result<(), RuntimeError> {
let (ax, overflow) = a.overflowing_add(32);
let (cx, of) = c.overflowing_add(32);
Voxelot marked this conversation as resolved.
Show resolved Hide resolved
let overflow = overflow || of;

if overflow || ax > VM_MAX_RAM || cx > VM_MAX_RAM {
return Err(PanicReason::MemoryOverflow.into());
}

let amount = b;
let destination =
ContractId::try_from(&self.memory[a as usize..ax as usize]).expect("Unreachable! Checked memory range");
let asset_id =
Color::try_from(&self.memory[c as usize..cx as usize]).expect("Unreachable! Checked memory range");

if !self.tx.input_contracts().any(|contract| &destination == contract) {
return Err(PanicReason::ContractNotInInputs.into());
}

if amount == 0 {
return Err(PanicReason::NotEnoughBalance.into());
}

let internal_context = match self.internal_contract() {
// optimistically attempt to load the internal contract id
Ok(source_contract) => Some(*source_contract),
// revert to external context if no internal contract is set
Err(RuntimeError::Recoverable(PanicReason::ExpectedInternalContext)) => None,
// bubble up any other kind of errors
Err(e) => return Err(e),
};

if let Some(source_contract) = internal_context {
// debit funding source (source contract balance)
self.balance_decrease(&source_contract, &asset_id, amount)?;
} else {
// debit external funding source (i.e. UTXOs)
adlerjohn marked this conversation as resolved.
Show resolved Hide resolved
self.external_color_balance_sub(&asset_id, amount)?;
}
// credit destination contract
self.balance_increase(&destination, &asset_id, amount)?;

self.receipts.push(Receipt::transfer(
internal_context.unwrap_or_default(),
destination,
amount,
asset_id,
self.registers[REG_PC],
self.registers[REG_IS],
));

self.inc_pc()
}

pub(crate) fn transfer_output(&mut self, a: Word, b: Word, c: Word, d: Word) -> Result<(), RuntimeError> {
let (ax, overflow) = a.overflowing_add(32);
let (dx, of) = d.overflowing_add(32);
let overflow = overflow || of;
let out_idx = b as usize;

if overflow || ax > VM_MAX_RAM || dx > VM_MAX_RAM {
return Err(PanicReason::MemoryOverflow.into());
}

let to = Address::try_from(&self.memory[a as usize..ax as usize]).expect("Unreachable! Checked memory range");
let asset_id =
Color::try_from(&self.memory[d as usize..dx as usize]).expect("Unreachable! Checked memory range");
let amount = c;

let internal_context = match self.internal_contract() {
// optimistically attempt to load the internal contract id
Ok(source_contract) => Some(*source_contract),
// revert to external context if no internal contract is set
Err(RuntimeError::Recoverable(PanicReason::ExpectedInternalContext)) => None,
// bubble up any other kind of errors
Err(e) => return Err(e),
};

if let Some(source_contract) = internal_context {
// debit funding source (source contract balance)
self.balance_decrease(&source_contract, &asset_id, amount)?;
} else {
// debit external funding source (i.e. UTXOs)
self.external_color_balance_sub(&asset_id, amount)?;
}

// credit variable output
self.set_variable_output(out_idx, asset_id, amount, to)?;

self.inc_pc()
}

pub(crate) fn check_contract_exists(&self, contract: &ContractId) -> Result<bool, RuntimeError> {
self.storage
.storage_contract_exists(contract)
Expand All @@ -57,4 +150,34 @@ where
.map_err(RuntimeError::from_io)?
.unwrap_or_default())
}

/// Increase the asset balance for a contract
pub(crate) fn balance_increase(
&mut self,
contract: &ContractId,
asset_id: &Color,
amount: Word,
) -> Result<Word, RuntimeError> {
let balance = self.balance(&contract, &asset_id)?;
let balance = balance.checked_add(amount).ok_or(PanicReason::ArithmeticOverflow)?;
self.storage
.merkle_contract_color_balance_insert(&contract, &asset_id, balance)
.map_err(RuntimeError::from_io)?;
Ok(balance)
}

/// Decrease the asset balance for a contract
pub(crate) fn balance_decrease(
&mut self,
contract: &ContractId,
asset_id: &Color,
amount: Word,
) -> Result<Word, RuntimeError> {
let balance = self.balance(&contract, &asset_id)?;
let balance = balance.checked_sub(amount).ok_or(PanicReason::NotEnoughBalance)?;
self.storage
.merkle_contract_color_balance_insert(&contract, &asset_id, balance)
.map_err(RuntimeError::from_io)?;
Ok(balance)
}
}
13 changes: 12 additions & 1 deletion src/interpreter/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ fn breakpoint_script() {

let gas_price = 0;
let gas_limit = 1_000_000;
let byte_price = 0;
let maturity = 0;

let script = vec![
Expand All @@ -55,7 +56,17 @@ fn breakpoint_script() {
.copied()
.collect();

let tx = Transaction::script(gas_price, gas_limit, maturity, script, vec![], vec![], vec![], vec![]);
let tx = Transaction::script(
gas_price,
gas_limit,
byte_price,
maturity,
script,
vec![],
vec![],
vec![],
vec![],
);

let suite = vec![
(
Expand Down
12 changes: 11 additions & 1 deletion src/interpreter/executors/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -469,8 +469,18 @@ where
self.metadata(ra, imm as Immediate18)?;
}

OpcodeRepr::TR => {
self.gas_charge(GAS_TR)?;
self.transfer(a, b, c)?;
}

OpcodeRepr::TRO => {
self.gas_charge(GAS_TRO)?;
self.transfer_output(a, b, c, d)?;
}

// list of currently unimplemented opcodes
OpcodeRepr::SLDC | OpcodeRepr::TR | OpcodeRepr::TRO | _ => {
OpcodeRepr::SLDC | _ => {
return Err(PanicReason::ErrorFlag.into());
}
}
Expand Down
16 changes: 12 additions & 4 deletions src/interpreter/executors/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ where
pub(crate) fn run(&mut self) -> Result<ProgramState, InterpreterError> {
let mut state: ProgramState;

let mut gas_refund = 0;

match &self.tx {
Transaction::Create {
salt, static_contracts, ..
Expand Down Expand Up @@ -84,18 +86,21 @@ where
}
}

Transaction::Script { .. } => {
Transaction::Script {
gas_limit, gas_price, ..
} => {
let gas_limit = *gas_limit;
let gas_price = *gas_price;
let offset = (VM_TX_MEMORY + Transaction::script_offset()) as Word;

self.registers[REG_PC] = offset;
self.registers[REG_IS] = offset;
self.registers[REG_GGAS] = self.tx.gas_limit();
self.registers[REG_CGAS] = self.tx.gas_limit();
adlerjohn marked this conversation as resolved.
Show resolved Hide resolved

// TODO set tree balance

let program = self.run_program();
let gas_used = self.tx.gas_limit() - self.registers[REG_GGAS];
let gas_used = gas_limit - self.registers[REG_GGAS];
gas_refund = self.registers[REG_GGAS] * gas_price;

// Catch VM panic and don't propagate, generating a receipt
let (status, program) = match program {
Expand Down Expand Up @@ -141,6 +146,9 @@ where
self.tx.set_receipts_root(receipts_root);
}

let revert = matches!(state, ProgramState::Revert(_));
self.update_change_amounts(gas_refund, revert)?;

Ok(state)
}

Expand Down
9 changes: 3 additions & 6 deletions src/interpreter/gas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ impl<S> Interpreter<S> {
.join(GasUnit::Branching(1))
.join(GasUnit::RegisterWrite(1)),

// TODO Compile-time panic didn't land in stable yet
// https://github.com/rust-lang/rust/issues/51999
_ => loop {}, //panic!("Opcode is not gas constant"),
_ => panic!("Opcode is not gas constant"),
}
.cost()
}
Expand All @@ -48,9 +46,7 @@ impl<S> Interpreter<S> {

MCP | MCPI => GasUnit::Arithmetic(2).join(GasUnit::MemoryOwnership(1)),

// TODO Compile-time panic didn't land in stable yet
// https://github.com/rust-lang/rust/issues/51999
_ => loop {}, //panic!("Opcode is not variable gas"),
_ => panic!("Opcode is not variable gas"),
}
.cost()
}
Expand Down Expand Up @@ -85,6 +81,7 @@ impl<S> Interpreter<S> {
Err(PanicReason::OutOfGas.into())
} else {
self.registers[REG_CGAS] -= gas;
self.registers[REG_GGAS] -= gas;
Copy link
Member Author

Choose a reason for hiding this comment

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

we actually weren't decrementing global gas before this 😬


Ok(())
}
Expand Down
2 changes: 2 additions & 0 deletions src/interpreter/gas/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ pub const GAS_XWL: Word = Interpreter::<()>::gas_cost_const(OpcodeRepr::ADD);
pub const GAS_XWS: Word = Interpreter::<()>::gas_cost_const(OpcodeRepr::ADD);
pub const GAS_FLAG: Word = Interpreter::<()>::gas_cost_const(OpcodeRepr::ADD);
pub const GAS_GM: Word = Interpreter::<()>::gas_cost_const(OpcodeRepr::ADD);
pub const GAS_TR: Word = Interpreter::<()>::gas_cost_const(OpcodeRepr::ADD);
pub const GAS_TRO: Word = Interpreter::<()>::gas_cost_const(OpcodeRepr::ADD);

// Variable gas cost
const GAS_OP_MEMORY_WRITE: Word = GasUnit::MemoryWrite(0).unit_price();
Expand Down
Loading