Skip to content

Commit

Permalink
Add GlobalCfg, cli vm-backtrace option (#79)
Browse files Browse the repository at this point in the history
* Add GlobalCfg, cli vm-backtrace option
* proper call with vm-backtrace
* Check backtrace even if return is Ok
* Use full config
* Moved backtrace inside revert
  • Loading branch information
rakita authored Jan 26, 2022
1 parent 53452cf commit 5ad631a
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 19 deletions.
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

0 comments on commit 5ad631a

Please sign in to comment.