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

Add GlobalCfg, cli vm-backtrace option #79

Merged
merged 6 commits into from
Jan 26, 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
7 changes: 4 additions & 3 deletions fuel-client/tests/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use fuel_core::{
database::Database,
executor::Executor,
model::fuel_block::FuelBlock,
service::{Config, FuelService},
service::{Config, FuelService, VMConfig},
};
use fuel_gql_client::client::types::TransactionStatus;
use fuel_gql_client::client::{FuelClient, PageDirection, PaginationRequest};
Expand Down Expand Up @@ -287,8 +287,9 @@ async fn get_transactions_from_manual_blcoks() {
};

// process blocks and save block height
executor.execute(&first_test_block).await.unwrap();
executor.execute(&second_test_block).await.unwrap();
let config = VMConfig::default();
executor.execute(&first_test_block, &config).await.unwrap();
executor.execute(&second_test_block, &config).await.unwrap();

// Query for first 3: [0,1,2]
let page_request_forwards = PaginationRequest {
Expand Down
10 changes: 9 additions & 1 deletion fuel-core/src/args.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::service::{Config, DbType};
use crate::service::{Config, DbType, VMConfig};
use std::{env, io, net, path::PathBuf, string::ToString};
use structopt::StructOpt;
use strum::VariantNames;
Expand Down Expand Up @@ -30,6 +30,10 @@ pub struct Opt {
/// Specify either an alias to a built-in configuration or filepath to a JSON file.
#[structopt(name = "CHAIN_CONFIG", long = "chain", default_value = "local_testnet")]
pub chain_config: String,

/// Specify if backtraces are going to be traced in logs (Default false)
#[structopt(long = "vm-backtrace")]
pub vm_backtrace: bool,
}

impl Opt {
Expand All @@ -50,6 +54,7 @@ impl Opt {
database_path,
database_type,
chain_config,
vm_backtrace,
} = self;

let addr = net::SocketAddr::new(ip, port);
Expand All @@ -59,6 +64,9 @@ impl Opt {
database_path,
database_type,
chain_conf: chain_config.as_str().parse()?,
vm: VMConfig {
backtrace: vm_backtrace,
},
})
}
}
39 changes: 32 additions & 7 deletions fuel-core/src/executor.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::database::transaction::TransactionIndex;
use crate::service::VMConfig;
use crate::{
database::{Database, KvStoreError},
model::{
Expand All @@ -10,17 +11,22 @@ use crate::{
use fuel_asm::Word;
use fuel_storage::Storage;
use fuel_tx::{Address, Bytes32, Color, Input, Output, Receipt, Transaction, UtxoId};
use fuel_vm::prelude::{Interpreter, InterpreterError};
use fuel_vm::{
consts::REG_SP,
prelude::{Backtrace as FuelBacktrace, InterpreterError},
transactor::Transactor,
};
use std::error::Error as StdError;
use std::ops::DerefMut;
use thiserror::Error;
use tracing::warn;

pub struct Executor {
pub database: Database,
}

impl Executor {
pub async fn execute(&self, block: &FuelBlock) -> Result<(), Error> {
pub async fn execute(&self, block: &FuelBlock, config: &VMConfig) -> Result<(), Error> {
let mut block_tx = self.database.transaction();
let block_id = block.id();
Storage::<Bytes32, FuelBlock>::insert(block_tx.deref_mut(), &block_id, block)?;
Expand All @@ -40,10 +46,9 @@ impl Executor {
self.persist_owners_index(block.fuel_height, &tx, tx_id, idx, block_tx.deref_mut())?;

// execute vm
let mut vm = Interpreter::with_storage(tx_db.clone());
let execution_result = vm.transact(tx);

match execution_result {
let mut vm = Transactor::new(tx_db.clone());
vm.transact(tx);
match vm.result() {
Ok(result) => {
// persist any outputs
self.persist_outputs(block.fuel_height, result.tx(), tx_db)?;
Expand All @@ -52,6 +57,18 @@ impl Executor {
self.persist_receipts(tx_id, result.receipts(), tx_db)?;

let status = if result.should_revert() {
if config.backtrace {
if let Some(backtrace) = vm.backtrace() {
warn!(
target = "vm",
"Backtrace on contract: 0x{:x}\nregisters: {:?}\ncall_stack: {:?}\nstack\n: {}",
backtrace.contract(),
backtrace.registers(),
backtrace.call_stack(),
hex::encode(&backtrace.memory()[..backtrace.registers()[REG_SP] as usize]), // print stack
);
}
}
// if script result exists, log reason
if let Some((script_result, _)) = result.receipts().iter().find_map(|r| {
if let Receipt::ScriptResult { result, gas_used } = r {
Expand Down Expand Up @@ -93,8 +110,8 @@ impl Executor {
sub_tx.commit()?;
}
}
// save error status on block_tx since the sub_tx changes are dropped
Err(e) => {
// save error status on block_tx since the sub_tx changes are dropped
block_tx.update_tx_status(
tx_id,
TransactionStatus::Failed {
Expand Down Expand Up @@ -291,6 +308,14 @@ pub enum Error {
},
#[error("VM execution error: {0:?}")]
VmExecution(fuel_vm::prelude::InterpreterError),
#[error("Execution error with backtrace")]
Backtrace(Box<FuelBacktrace>),
}

impl From<FuelBacktrace> for Error {
fn from(e: FuelBacktrace) -> Self {
Error::Backtrace(Box::new(e))
}
}

impl From<crate::database::KvStoreError> for Error {
Expand Down
7 changes: 5 additions & 2 deletions fuel-core/src/schema/tx.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::database::{transaction::OwnedTransactionIndexCursor, Database, KvStoreError};
use crate::model::fuel_block::{BlockHeight, FuelBlock};
use crate::schema::scalars::{HexString, HexString256, SortedTxCursor};
use crate::service::Config;
use crate::state::IterDirection;
use crate::tx_pool::TxPool;
use async_graphql::{
Expand Down Expand Up @@ -248,10 +249,11 @@ impl TxMutation {
tx: HexString,
) -> async_graphql::Result<Vec<receipt::Receipt>> {
let transaction = ctx.data_unchecked::<Database>().transaction();
let cfg = ctx.data_unchecked::<Config>();
let tx = FuelTx::from_bytes(&tx.0)?;
// make virtual txpool from transactional view
let tx_pool = TxPool::new(transaction.deref().clone());
let receipts = tx_pool.run_tx(tx).await?;
let receipts = tx_pool.run_tx(tx, cfg).await?;
Ok(receipts.into_iter().map(receipt::Receipt).collect())
}

Expand All @@ -262,8 +264,9 @@ impl TxMutation {
tx: HexString,
) -> async_graphql::Result<HexString256> {
let tx_pool = ctx.data::<Arc<TxPool>>().unwrap();
let cfg = ctx.data_unchecked::<Config>();
let tx = FuelTx::from_bytes(&tx.0)?;
let id = tx_pool.submit_tx(tx).await?;
let id = tx_pool.submit_tx(tx, cfg).await?;

Ok(id.into())
}
Expand Down
7 changes: 7 additions & 0 deletions fuel-core/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub struct Config {
pub database_path: PathBuf,
pub database_type: DbType,
pub chain_conf: ChainConfig,
pub vm: VMConfig,
}

impl Config {
Expand All @@ -36,10 +37,16 @@ impl Config {
database_path: Default::default(),
database_type: DbType::InMemory,
chain_conf: ChainConfig::local_testnet(),
vm: Default::default(),
}
}
}

#[derive(Clone, Debug, Default)]
pub struct VMConfig {
pub backtrace: bool,
}

#[derive(Clone, Debug, Display, PartialEq, EnumString, EnumVariantNames)]
#[strum(serialize_all = "kebab_case")]
pub enum DbType {
Expand Down
5 changes: 3 additions & 2 deletions fuel-core/src/service/graph_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ pub async fn start_server(
db: Database,
tx_pool: Arc<TxPool>,
) -> Result<(SocketAddr, JoinHandle<Result<(), crate::service::Error>>), std::io::Error> {
let schema = build_schema().data(db).data(tx_pool);
let network_addr = config.addr;
let schema = build_schema().data(db).data(tx_pool).data(config);
let schema = dap::init(schema).finish();

let router = Router::new()
Expand All @@ -53,7 +54,7 @@ pub async fn start_server(
));

let (tx, rx) = tokio::sync::oneshot::channel();
let listener = TcpListener::bind(&config.addr)?;
let listener = TcpListener::bind(&network_addr)?;
let bound_addr = listener.local_addr().unwrap();

info!("Binding GraphQL provider to {}", bound_addr);
Expand Down
9 changes: 5 additions & 4 deletions fuel-core/src/tx_pool.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::database::{Database, KvStoreError};
use crate::executor::Executor;
use crate::model::fuel_block::FuelBlock;
use crate::service::Config;
use chrono::{DateTime, Utc};
use fuel_storage::Storage;
use fuel_tx::{Bytes32, Receipt};
Expand Down Expand Up @@ -64,7 +65,7 @@ impl TxPool {
}
}

pub async fn submit_tx(&self, tx: Transaction) -> Result<Bytes32, Error> {
pub async fn submit_tx(&self, tx: Transaction, config: &Config) -> Result<Bytes32, Error> {
let tx_id = tx.id();
// persist transaction to database
let mut db = self.db.clone();
Expand All @@ -82,14 +83,14 @@ impl TxPool {
};
// immediately execute block
self.executor
.execute(&block)
.execute(&block, &config.vm)
.await
.map_err(Error::Execution)?;
Ok(tx_id)
}

pub async fn run_tx(&self, tx: Transaction) -> Result<Vec<Receipt>, Error> {
let id = self.submit_tx(tx).await?;
pub async fn run_tx(&self, tx: Transaction, config: &Config) -> Result<Vec<Receipt>, Error> {
let id = self.submit_tx(tx, config).await?;
// note: we'll need to await tx completion once it's not instantaneous
let db = &self.db;
let receipts = Storage::<Bytes32, Vec<Receipt>>::get(db, &id)?.unwrap_or_default();
Expand Down