Skip to content

Commit

Permalink
Enable storage initializers and emit a storage initialization JSON (F…
Browse files Browse the repository at this point in the history
…uelLabs#2078)

* Enable storage initializers and dump out a JSON file

* Move the new functions into a separate storage module

* Use StorageSlot directly from fuel-tx

* forc deploy now uses the storage slots

* add some tests

* lint, change initializers -> slots, and fixing some tests

* enhance a comment

* Revert unneeded changes to sway-types

* add a failing test

* Fix failing test

* renaming some functions

* Test the storage slots JSON in e2e tests and add forc json-storage-slots command

* ignore *_output.json

* forc documenter changes

* Remove forc json-storage-slots and stop relying on forc json-abi

* Enhance some comments

* Remove unnecessary oracle
  • Loading branch information
mohammadfawaz authored Jun 27, 2022
1 parent b83a56f commit 8421507
Show file tree
Hide file tree
Showing 59 changed files with 940 additions and 198 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# will have compiled files and executables
**/*/target/
**/*/json_abi_output.json
**/*/json_storage_slots_output.json
target

# These are backup files generated by rustfmt
Expand Down
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions forc-pkg/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ description = "Building, locking, fetching and updating Sway projects as Forc pa
[dependencies]
anyhow = "1"
forc-util = { version = "0.16.2", path = "../forc-util" }
fuel-tx = "0.12"
fuels-types = "0.12"
git2 = { version = "0.14", features = ["vendored-libgit2", "vendored-openssl"] }
petgraph = { version = "0.6", features = ["serde-1"] }
Expand Down
8 changes: 8 additions & 0 deletions forc-pkg/src/pkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use forc_util::{
find_file_name, git_checkouts_directory, kebab_to_snake_case, print_on_failure,
print_on_success, print_on_success_library, println_yellow_err,
};
use fuel_tx::StorageSlot;
use fuels_types::JsonABI;
use petgraph::{
self,
Expand Down Expand Up @@ -45,6 +46,7 @@ pub struct PinnedId(u64);
/// The result of successfully compiling a package.
pub struct Compiled {
pub json_abi: JsonABI,
pub storage_slots: Vec<StorageSlot>,
pub bytecode: Vec<u8>,
pub tree_type: TreeType,
}
Expand Down Expand Up @@ -1503,6 +1505,7 @@ pub fn compile(
warnings,
} => {
let json_abi = time_expr!("generate JSON ABI", typed_program.kind.generate_json_abi());
let storage_slots = typed_program.storage_slots.clone();
let tree_type = typed_program.kind.tree_type();
match tree_type {
// If we're compiling a library, we don't need to compile any further.
Expand All @@ -1513,6 +1516,7 @@ pub fn compile(
let lib_namespace = typed_program.root.namespace.clone();
let compiled = Compiled {
json_abi,
storage_slots,
bytecode,
tree_type,
};
Expand All @@ -1535,6 +1539,7 @@ pub fn compile(
let bytecode = bytes;
let compiled = Compiled {
json_abi,
storage_slots,
bytecode,
tree_type,
};
Expand Down Expand Up @@ -1567,6 +1572,7 @@ pub fn build(
let mut namespace_map = Default::default();
let mut source_map = SourceMap::new();
let mut json_abi = vec![];
let mut storage_slots = vec![];
let mut bytecode = vec![];
let mut tree_type = None;
for &node in &plan.compilation_order {
Expand All @@ -1581,6 +1587,7 @@ pub fn build(
namespace_map.insert(node, namespace.into());
}
json_abi.extend(compiled.json_abi);
storage_slots.extend(compiled.storage_slots);
bytecode = compiled.bytecode;
tree_type = Some(compiled.tree_type);
source_map.insert_dependency(path.clone());
Expand All @@ -1590,6 +1597,7 @@ pub fn build(
let compiled = Compiled {
bytecode,
json_abi,
storage_slots,
tree_type,
};
Ok((compiled, source_map))
Expand Down
4 changes: 4 additions & 0 deletions forc/src/cli/commands/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ pub struct Command {
/// output will be "minified", i.e. all on one line without whitespace.
#[clap(long)]
pub minify_json_abi: bool,
/// By default the JSON for initial storage slots is formatted for human readability. By using
/// this option JSON output will be "minified", i.e. all on one line without whitespace.
#[clap(long)]
pub minify_json_storage_slots: bool,
/// Requires that the Forc.lock file is up-to-date. If the lock file is missing, or it
/// needs to be updated, Forc will exit with an error
#[clap(long)]
Expand Down
4 changes: 4 additions & 0 deletions forc/src/cli/commands/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ pub struct Command {
/// output will be "minified", i.e. all on one line without whitespace.
#[clap(long)]
pub minify_json_abi: bool,
/// By default the JSON for initial storage slots is formatted for human readability. By using
/// this option JSON output will be "minified", i.e. all on one line without whitespace.
#[clap(long)]
pub minify_json_storage_slots: bool,
/// Requires that the Forc.lock file is up-to-date. If the lock file is missing, or it
/// needs to be updated, Forc will exit with an error
#[clap(long)]
Expand Down
5 changes: 5 additions & 0 deletions forc/src/cli/commands/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ pub struct Command {
#[clap(long)]
pub minify_json_abi: bool,

/// By default the JSON for initial storage slots is formatted for human readability. By using
/// this option JSON output will be "minified", i.e. all on one line without whitespace.
#[clap(long)]
pub minify_json_storage_slots: bool,

/// Set the transaction byte price. Defaults to 0.
#[clap(long)]
pub byte_price: Option<u64>,
Expand Down
30 changes: 23 additions & 7 deletions forc/src/ops/forc_build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub fn build(command: BuildCommand) -> Result<pkg::Compiled> {
silent_mode,
output_directory,
minify_json_abi,
minify_json_storage_slots,
locked,
build_profile,
release,
Expand Down Expand Up @@ -116,14 +117,21 @@ pub fn build(command: BuildCommand) -> Result<pkg::Compiled> {

info!(" Bytecode size is {} bytes.", compiled.bytecode.len());

// Additional ops required depending on the program type
match compiled.tree_type {
TreeType::Script => {
// hash the bytecode for scripts and store the result in a file in the output directory
let bytecode_hash = format!("0x{}", fuel_crypto::Hasher::hash(&compiled.bytecode));
let hash_file_name = format!("{}{}", &manifest.project.name, SWAY_BIN_HASH_SUFFIX);
let hash_path = output_dir.join(hash_file_name);
fs::write(hash_path, &bytecode_hash)?;
info!(" Script bytecode hash: {}", bytecode_hash);
TreeType::Contract => {
// For contracts, emit a JSON file with all the initialized storage slots.
let json_storage_slots_stem = format!("{}-storage_slots", manifest.project.name);
let json_storage_slots_path = output_dir
.join(&json_storage_slots_stem)
.with_extension("json");
let file = File::create(json_storage_slots_path)?;
let res = if minify_json_storage_slots {
serde_json::to_writer(&file, &compiled.storage_slots)
} else {
serde_json::to_writer_pretty(&file, &compiled.storage_slots)
};
res?;
}
TreeType::Predicate => {
// get the root hash of the bytecode for predicates and store the result in a file in the output directory
Expand All @@ -133,6 +141,14 @@ pub fn build(command: BuildCommand) -> Result<pkg::Compiled> {
fs::write(root_path, &root)?;
info!(" Predicate root: {}", root);
}
TreeType::Script => {
// hash the bytecode for scripts and store the result in a file in the output directory
let bytecode_hash = format!("0x{}", fuel_crypto::Hasher::hash(&compiled.bytecode));
let hash_file_name = format!("{}{}", &manifest.project.name, SWAY_BIN_HASH_SUFFIX);
let hash_path = output_dir.join(hash_file_name);
fs::write(hash_path, &bytecode_hash)?;
info!(" Script bytecode hash: {}", bytecode_hash);
}
_ => (),
}

Expand Down
15 changes: 12 additions & 3 deletions forc/src/ops/forc_deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
use anyhow::{bail, Result};
use forc_pkg::ManifestFile;
use fuel_gql_client::client::FuelClient;
use fuel_tx::{Output, Salt, Transaction};
use fuel_tx::{Output, Salt, StorageSlot, Transaction};
use fuel_vm::prelude::*;
use std::path::PathBuf;
use sway_core::TreeType;
Expand All @@ -33,6 +33,7 @@ pub async fn deploy(command: DeployCommand) -> Result<fuel_tx::ContractId> {
silent_mode,
output_directory,
minify_json_abi,
minify_json_storage_slots,
locked,
url,
build_profile,
Expand All @@ -51,6 +52,7 @@ pub async fn deploy(command: DeployCommand) -> Result<fuel_tx::ContractId> {
silent_mode,
output_directory,
minify_json_abi,
minify_json_storage_slots,
locked,
build_profile,
release,
Expand All @@ -62,6 +64,7 @@ pub async fn deploy(command: DeployCommand) -> Result<fuel_tx::ContractId> {
compiled.bytecode,
Vec::<fuel_tx::Input>::new(),
Vec::<fuel_tx::Output>::new(),
compiled.storage_slots,
);

let node_url = match &manifest.network {
Expand Down Expand Up @@ -89,6 +92,7 @@ fn create_contract_tx(
compiled_contract: Vec<u8>,
inputs: Vec<Input>,
outputs: Vec<Output>,
storage_slots: Vec<StorageSlot>,
) -> (Transaction, fuel_tx::ContractId) {
let gas_price = 0;
let gas_limit = fuel_tx::default_parameters::MAX_GAS_PER_TX;
Expand All @@ -99,11 +103,16 @@ fn create_contract_tx(

let salt = Salt::new([0; 32]);
let static_contracts = vec![];
let storage_slots = vec![];

let contract = Contract::from(compiled_contract);
let root = contract.root();
let state_root = Contract::default_state_root();

// The VM currently requires that storage slots are sorted but this shouldn't be neessary.
// Downstream tooling should do the sorting themselves.
// Ref: https://github.com/FuelLabs/fuel-tx/issues/153
let mut storage_slots = storage_slots;
storage_slots.sort();
let state_root = Contract::initial_state_root(storage_slots.iter());
let id = contract.id(&salt, &root, &state_root);
info!("Contract id: 0x{}", hex::encode(id));
let outputs = [
Expand Down
1 change: 1 addition & 0 deletions forc/src/ops/forc_run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub async fn run(command: RunCommand) -> Result<Vec<fuel_tx::Receipt>> {
silent_mode: command.silent_mode,
output_directory: command.output_directory,
minify_json_abi: command.minify_json_abi,
minify_json_storage_slots: command.minify_json_storage_slots,
locked: command.locked,
build_profile: None,
release: false,
Expand Down
1 change: 1 addition & 0 deletions sway-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ dirs = "3.0"
either = "1.6"
fuel-asm = "0.5"
fuel-crypto = "0.5"
fuel-tx = "0.12"
fuel-types = "0.5"
fuel-vm = "0.11"
fuels-types = "0.12"
Expand Down
5 changes: 4 additions & 1 deletion sway-core/src/convert_parse_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1867,7 +1867,10 @@ fn storage_field_to_storage_field(
let storage_field = StorageField {
name: storage_field.name,
type_info: ty_to_type_info(ec, storage_field.ty)?,
//initializer: expr_to_expression(storage_field.expr),
initializer: storage_field
.initializer
.map(|initializer| expr_to_expression(ec, initializer.1))
.transpose()?,
};
Ok(storage_field)
}
Expand Down
5 changes: 3 additions & 2 deletions sway-core/src/ir_generation.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
mod compile;
mod const_eval;
pub mod const_eval;
mod convert;
mod function;
mod lexical_map;
mod purity;
pub mod storage;
mod types;

use crate::{
Expand All @@ -17,7 +18,7 @@ use sway_types::span::Span;
pub(crate) use purity::PurityChecker;

pub(crate) fn compile_program(program: TypedProgram) -> Result<Context, CompileError> {
let TypedProgram { kind, root } = program;
let TypedProgram { kind, root, .. } = program;

let mut ctx = Context::default();
match kind {
Expand Down
22 changes: 18 additions & 4 deletions sway-core/src/ir_generation/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
},
};

use super::types::*;
use super::{convert::convert_literal_to_constant, types::*};

use sway_ir::{
constant::{Constant, ConstantValue},
Expand All @@ -26,6 +26,20 @@ pub(super) fn compile_constant_expression(
const_expr: &TypedExpression,
) -> Result<Value, CompileError> {
let span_id_idx = MetadataIndex::from_span(context, &const_expr.span);

let constant_evaluated = compile_constant_expression_to_constant(context, module, const_expr)?;
Ok(Value::new_constant(
context,
constant_evaluated,
span_id_idx,
))
}

pub(crate) fn compile_constant_expression_to_constant(
context: &mut Context,
module: Module,
const_expr: &TypedExpression,
) -> Result<Constant, CompileError> {
let err = match &const_expr.expression {
// Special case functions because the span in `const_expr` is to the inlined function
// definition, rather than the actual call site.
Expand All @@ -39,8 +53,8 @@ pub(super) fn compile_constant_expression(
}),
};
let mut known_consts = MappedStack::<Ident, Constant>::new();
const_eval_typed_expr(context, module, &mut known_consts, const_expr)
.map_or(err, |c| Ok(Value::new_constant(context, c, span_id_idx)))

const_eval_typed_expr(context, module, &mut known_consts, const_expr).map_or(err, Ok)
}

// A HashMap that can hold multiple values and
Expand Down Expand Up @@ -97,7 +111,7 @@ fn const_eval_typed_expr(
expr: &TypedExpression,
) -> Option<Constant> {
match &expr.expression {
TypedExpressionVariant::Literal(l) => Some(super::convert::convert_literal_to_constant(l)),
TypedExpressionVariant::Literal(l) => Some(convert_literal_to_constant(l)),
TypedExpressionVariant::FunctionApplication {
arguments,
function_body,
Expand Down
17 changes: 0 additions & 17 deletions sway-core/src/ir_generation/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,20 +140,3 @@ fn convert_resolved_type(
TypeInfo::Storage { .. } => reject_type!("Storage"),
})
}

use uint::construct_uint;

#[allow(
// These two warnings are generated by the `construct_uint!()` macro below.
clippy::assign_op_pattern,
clippy::ptr_offset_with_cast
)]
pub(super) fn add_to_b256(x: fuel_types::Bytes32, y: u64) -> fuel_types::Bytes32 {
construct_uint! {
struct U256(4);
}
let x = U256::from(*x);
let y = U256::from(y);
let res: [u8; 32] = (x + y).into();
fuel_types::Bytes32::from(res)
}
Loading

0 comments on commit 8421507

Please sign in to comment.