Skip to content

Commit

Permalink
Feat(standalone): thread-safety and serde (#496)
Browse files Browse the repository at this point in the history
  • Loading branch information
birchmd committed Jun 8, 2022
1 parent 55117ec commit 530800e
Show file tree
Hide file tree
Showing 10 changed files with 81 additions and 101 deletions.
35 changes: 2 additions & 33 deletions engine-standalone-storage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub mod diff;
pub mod engine_state;
pub mod error;
pub mod json_snapshot;
mod promise;
pub mod promise;
pub mod relayer_db;
/// Functions for receiving new blocks and transactions to keep the storage up to date.
pub mod sync;
Expand All @@ -38,20 +38,12 @@ pub enum StoragePrefix {

pub struct Storage {
db: DB,
engine_transaction: RefCell<Diff>,
engine_output: Cell<Vec<u8>>,
}

impl Storage {
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, rocksdb::Error> {
let db = DB::open_default(path)?;
let engine_transaction = RefCell::new(Diff::default());
let engine_output = Cell::new(Vec::new());
Ok(Self {
db,
engine_transaction,
engine_output,
})
Ok(Self { db })
}

pub fn get_latest_block(&self) -> Result<(H256, u64), error::Error> {
Expand Down Expand Up @@ -343,29 +335,6 @@ impl Storage {
Ok(result)
}

/// Get an object which represents the state of the engine at the given block hash,
/// after transactions up to (not including) the given transaction index.
/// The `input` is the bytes that would be present in the NEAR runtime (normally
/// not needed for standalone engine).
pub fn access_engine_storage_at_position<'db, 'input: 'db>(
&'db mut self,
block_height: u64,
transaction_position: u16,
input: &'input [u8],
) -> engine_state::EngineStateAccess<'db, 'db, 'db> {
self.engine_transaction.borrow_mut().clear();
self.engine_output.set(Vec::new());

engine_state::EngineStateAccess::new(
input,
block_height,
transaction_position,
&self.engine_transaction,
&self.engine_output,
&self.db,
)
}

/// Same as `access_engine_storage_at_position`, but does not modify `self`, hence the immutable
/// borrow instead of the mutable one. The use case for this function is to execute a transaction
/// with the engine, but not to make any immediate changes to storage; only return the diff and outcome.
Expand Down
57 changes: 30 additions & 27 deletions engine-standalone-storage/src/relayer_db/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,18 +100,18 @@ where
env.block_timestamp = block_metadata.timestamp;
env.random_seed = block_metadata.random_seed;

let io = storage.access_engine_storage_at_position(block_height, transaction_position, &[]);

let maybe_result = engine::submit(
io,
&env,
&transaction_bytes,
engine_state.clone(),
env.current_account_id(),
relayer_address,
&mut handler,
);
match maybe_result {
let result = storage.with_engine_access(block_height, transaction_position, &[], |io| {
engine::submit(
io,
&env,
&transaction_bytes,
engine_state.clone(),
env.current_account_id(),
relayer_address,
&mut handler,
)
});
match result.result {
// Engine errors would always turn into panics on the NEAR side, so we do not need to persist
// any diff. Therefore, even if the error was expected, we still continue to the next transaction.
Err(e) => {
Expand Down Expand Up @@ -140,7 +140,7 @@ where
}
}

let diff = io.get_transaction_diff();
let diff = result.diff;
let tx_msg = crate::TransactionMessage {
block_hash,
near_receipt_id: near_tx_hash,
Expand Down Expand Up @@ -227,20 +227,23 @@ mod test {
storage
.set_block_data(block_hash, block_height, block_metadata)
.unwrap();
let mut io = storage.access_engine_storage_at_position(block_height, 0, &[]);
engine::set_state(&mut io, engine_state.clone());
connector::EthConnectorContract::create_contract(
io,
engine_state.owner_id.clone(),
parameters::InitCallArgs {
prover_account: engine_state.bridge_prover_id.clone(),
eth_custodian_address: "6bfad42cfc4efc96f529d786d643ff4a8b89fa52".to_string(),
metadata: Default::default(),
},
)
.ok()
.unwrap();
let diff = io.get_transaction_diff();
let result = storage.with_engine_access(block_height, 0, &[], |io| {
let mut local_io = io.clone();
engine::set_state(&mut local_io, engine_state.clone());
connector::EthConnectorContract::create_contract(
io,
engine_state.owner_id.clone(),
parameters::InitCallArgs {
prover_account: engine_state.bridge_prover_id.clone(),
eth_custodian_address: "6bfad42cfc4efc96f529d786d643ff4a8b89fa52"
.to_string(),
metadata: Default::default(),
},
)
});

result.result.ok().unwrap();
let diff = result.diff;
storage
.set_transaction_included(
H256::zero(),
Expand Down
18 changes: 10 additions & 8 deletions engine-standalone-storage/src/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,17 @@ pub fn consume_message(
let block_hash = transaction_message.block_hash;
let block_height = storage.get_block_height_by_hash(block_hash)?;
let block_metadata = storage.get_block_metadata(block_hash)?;
let io =
storage.access_engine_storage_at_position(block_height, transaction_position, &[]);

let (tx_hash, diff, result) = execute_transaction(
transaction_message.as_ref(),
block_height,
&block_metadata,
io,
)?;
let (tx_hash, diff, result) = storage
.with_engine_access(block_height, transaction_position, &[], |io| {
execute_transaction(
transaction_message.as_ref(),
block_height,
&block_metadata,
io,
)
})
.result?;
match &result {
Some(TransactionExecutionResult::Submit(Err(_))) => (), // do not persist if Engine encounters an error
_ => storage.set_transaction_included(tx_hash, &transaction_message, &diff)?,
Expand Down
27 changes: 15 additions & 12 deletions engine-tests/src/test_utils/standalone/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,24 +239,27 @@ impl StandaloneRunner {
}

pub fn get_balance(&mut self, address: &Address) -> Wei {
let io = self
.storage
.access_engine_storage_at_position(self.env.block_height + 1, 0, &[]);
engine::get_balance(&io, address)
self.storage
.with_engine_access(self.env.block_height + 1, 0, &[], |io| {
engine::get_balance(&io, address)
})
.result
}

pub fn get_nonce(&mut self, address: &Address) -> U256 {
let io = self
.storage
.access_engine_storage_at_position(self.env.block_height + 1, 0, &[]);
engine::get_nonce(&io, address)
self.storage
.with_engine_access(self.env.block_height + 1, 0, &[], |io| {
engine::get_nonce(&io, address)
})
.result
}

pub fn get_code(&mut self, address: &Address) -> Vec<u8> {
let io = self
.storage
.access_engine_storage_at_position(self.env.block_height + 1, 0, &[]);
engine::get_code(&io, address)
self.storage
.with_engine_access(self.env.block_height + 1, 0, &[], |io| {
engine::get_code(&io, address)
})
.result
}

pub fn close(self) {
Expand Down
21 changes: 4 additions & 17 deletions engine-tests/src/tests/standalone/storage.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use aurora_engine::engine;
use aurora_engine_sdk::env::Timestamp;
use aurora_engine_types::types::{Address, Wei};
use aurora_engine_types::{H256, U256};
Expand Down Expand Up @@ -148,23 +147,11 @@ fn test_consume_transaction() {
let result = runner.submit_transaction(&signer.secret_key, tx).unwrap();
assert!(result.status.is_ok());

// Look at the engine state for the following block
let engine_io =
runner
.storage
.access_engine_storage_at_position(runner.env.block_height + 1, 0, &[]);

// Confirm the balances and nonces match the expected values (note the transfer has been applied)
assert_eq!(
engine::get_balance(&engine_io, &address),
balance - transfer_amount
);
assert_eq!(
engine::get_balance(&engine_io, &dest_address),
transfer_amount
);
assert_eq!(engine::get_nonce(&engine_io, &address), U256::one());
assert_eq!(engine::get_nonce(&engine_io, &dest_address), U256::zero());
assert_eq!(runner.get_balance(&address), balance - transfer_amount);
assert_eq!(runner.get_balance(&dest_address), transfer_amount);
assert_eq!(runner.get_nonce(&address), U256::one());
assert_eq!(runner.get_nonce(&dest_address), U256::zero());

runner.close();
}
Expand Down
9 changes: 6 additions & 3 deletions engine-tests/src/tests/standalone/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,13 @@ fn test_consume_deploy_erc20_message() {
)
.unwrap();

let io = runner
let erc20_address = runner
.storage
.access_engine_storage_at_position(runner.env.block_height + 1, 0, &[]);
let erc20_address = aurora_engine::engine::get_erc20_from_nep141(&io, &token).unwrap();
.with_engine_access(runner.env.block_height + 1, 0, &[], |io| {
aurora_engine::engine::get_erc20_from_nep141(&io, &token)
})
.result
.unwrap();

runner.env.block_height += 1;
runner.env.signer_account_id = "some_account.near".parse().unwrap();
Expand Down
7 changes: 7 additions & 0 deletions engine-transactions/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,20 @@ impl NormalizedEthTransaction {
}

#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub enum ParseTransactionError {
UnknownTransactionType,
// Per the EIP-2718 spec 0xff is a reserved value
ReservedSentinel,
#[cfg_attr(feature = "serde", serde(serialize_with = "decoder_err_to_str"))]
RlpDecodeError(DecoderError),
}

#[cfg(feature = "serde")]
fn decoder_err_to_str<S: serde::Serializer>(err: &DecoderError, ser: S) -> Result<S::Ok, S::Error> {
ser.serialize_str(&format!("{:?}", err))
}

impl ParseTransactionError {
pub fn as_str(&self) -> &'static str {
match self {
Expand Down
2 changes: 1 addition & 1 deletion engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,4 @@ mainnet = ["contract", "log"]
testnet = ["contract", "log"]
mainnet-test = ["meta-call"]
testnet-test = ["meta-call"]
impl-serde = ["aurora-engine-types/impl-serde", "serde", "aurora-engine-transactions/impl-serde"]
impl-serde = ["aurora-engine-types/impl-serde", "serde", "aurora-engine-transactions/impl-serde", "evm-core/with-serde"]
4 changes: 4 additions & 0 deletions engine/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ macro_rules! assert_or_finish {
}

#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct EngineError {
pub kind: EngineErrorKind,
pub gas_used: u64,
Expand All @@ -84,6 +85,7 @@ impl AsRef<[u8]> for EngineError {

/// Errors with the EVM engine.
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub enum EngineErrorKind {
/// Normal EVM errors.
EvmError(ExitError),
Expand Down Expand Up @@ -182,6 +184,7 @@ impl ExitIntoResult for ExitReason {
}

#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BalanceOverflow;

impl AsRef<[u8]> for BalanceOverflow {
Expand All @@ -192,6 +195,7 @@ impl AsRef<[u8]> for BalanceOverflow {

/// Errors resulting from trying to pay for gas
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum GasPaymentError {
/// Overflow adding ETH to an account balance (should never happen)
BalanceOverflow(BalanceOverflow),
Expand Down
2 changes: 2 additions & 0 deletions engine/src/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ impl From<Log> for ResultLog {

/// The status of a transaction.
#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TransactionStatus {
Succeed(Vec<u8>),
Revert(Vec<u8>),
Expand Down Expand Up @@ -109,6 +110,7 @@ impl AsRef<[u8]> for TransactionStatus {
/// Borsh-encoded parameters for the `call`, `call_with_args`, `deploy_code`,
/// and `deploy_with_input` methods.
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SubmitResult {
version: u8,
pub status: TransactionStatus,
Expand Down

0 comments on commit 530800e

Please sign in to comment.