-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(constructor): storage constructor instead of language feature (
#229) * fix(codegen): hardcoded creation bytecode * chore(codegen): temp remove constructor * feat(compiler): constructor with empty init code * chore(clippy): make clippy happy * chore(deps): remove depbot and update deps * ci(dep): make depbot invalid * feat(example): empty constructor * feat(zink): generate wasm handler for constructor * chore(codegen): get back stack balance check * refactor(compiler): remove constructor handler * feat(compiler): move constructor to artifact * feat(compiler): construct preset storage just in time * chore(clippy): make clippy happy * chore(zink): remove language item constructor
- Loading branch information
Showing
21 changed files
with
133 additions
and
196 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,118 +1,74 @@ | ||
//! Contract constructor. | ||
use crate::{wasm::ToLSBytes, Buffer, Function, JumpTable, MacroAssembler, Result}; | ||
use wasmparser::FuncType; | ||
use crate::{wasm::ToLSBytes, Buffer, MacroAssembler, Result}; | ||
use smallvec::SmallVec; | ||
use std::collections::HashMap; | ||
|
||
/// Initial storage of contracts | ||
pub type InitStorage = HashMap<SmallVec<[u8; 32]>, SmallVec<[u8; 32]>>; | ||
|
||
/// Contract constructor. | ||
/// | ||
/// # Bytecode | ||
/// - `CREATE` instruction | ||
/// - `INIT_CODE` | ||
/// - `INIT_LOGIC` | ||
/// - `RETURN RUNTIME_BYTECODE` | ||
/// - `RUNTIME_BYTECODE` | ||
/// | ||
/// TODO: introduce ABI for constructor | ||
#[derive(Default, Debug, Clone)] | ||
pub struct Constructor { | ||
/// Code generator. | ||
pub masm: MacroAssembler, | ||
/// Code buffer. | ||
pub init_code: Buffer, | ||
/// Runtime bytecode. | ||
pub runtime_bytecode: Buffer, | ||
masm: MacroAssembler, | ||
} | ||
|
||
impl Constructor { | ||
/// Create a new constructor. | ||
pub fn new(constructor: Option<FuncType>, runtime_bytecode: Buffer) -> Result<Self> { | ||
let mut init_code = Buffer::new(); | ||
if let Some(constructor) = constructor { | ||
let codegen = Function::new( | ||
Default::default(), | ||
constructor, | ||
// No `return` instruction in the generated code. | ||
false, | ||
)?; | ||
|
||
let mut jump_table = JumpTable::default(); | ||
init_code = codegen.finish(&mut jump_table, 0)?; | ||
jump_table.relocate(&mut init_code)?; | ||
}; | ||
/// preset storage for the contract | ||
pub fn storage(&mut self, mapping: InitStorage) -> Result<()> { | ||
for (key, value) in mapping.into_iter() { | ||
self.masm.push(&value)?; | ||
self.masm.push(&key)?; | ||
self.masm._sstore()?; | ||
} | ||
|
||
Ok(Self { | ||
masm: MacroAssembler::default(), | ||
init_code, | ||
runtime_bytecode, | ||
}) | ||
Ok(()) | ||
} | ||
|
||
/// Concat the constructor code. | ||
/// | ||
/// Here we override the memory totally with | ||
/// the runtime bytecode. | ||
pub fn finish(&mut self) -> Result<Buffer> { | ||
let init_code_length = self.init_code.len(); | ||
let runtime_bytecode_length = self.runtime_bytecode.len(); | ||
let return_instr_length = | ||
Self::return_instr_length(init_code_length, runtime_bytecode_length); | ||
|
||
// Copy init code and runtime bytecode to memory from offset 0. | ||
// | ||
// 1. code size ( init_code + instr_return + runtime_bytecode ) | ||
// 2. byte offset of code which is fixed to N. | ||
// 3. destination offset which is fixed to 0. | ||
{ | ||
self.masm.push( | ||
&(init_code_length + return_instr_length + runtime_bytecode_length).to_ls_bytes(), | ||
)?; | ||
// # SAFETY | ||
// | ||
// The length of the most significiant bytes of | ||
// the bytecode offset is fixed to 1. | ||
self.masm | ||
.push(&((self.masm.pc_offset() as usize + 9).to_ls_bytes()))?; | ||
self.masm._push0()?; | ||
self.masm._codecopy()?; | ||
} | ||
pub fn finish(&self, runtime_bytecode: Buffer) -> Result<Buffer> { | ||
let init_code = self.masm.buffer(); | ||
let init_code_len = init_code.len(); | ||
let runtime_bytecode_len = runtime_bytecode.len(); | ||
let runtime_bytecode_size = runtime_bytecode_len.to_ls_bytes(); | ||
let runtime_bytecode_offset = | ||
Self::runtime_bytcode_offset(init_code_len, runtime_bytecode_size.len()); | ||
|
||
// Process instruction `CREATE` | ||
{ | ||
self.masm._push0()?; | ||
self.masm._push0()?; | ||
self.masm._push0()?; | ||
self.masm._calldataload()?; | ||
self.masm._create()?; | ||
} | ||
|
||
self.masm.buffer_mut().extend_from_slice(&self.init_code); | ||
let mut masm = self.masm.clone(); | ||
|
||
// Process `RETURN`. | ||
// | ||
// 1. size of the runtime bytecode | ||
// 2. offset of the runtime bytecode in memory | ||
{ | ||
self.masm.push(&runtime_bytecode_length.to_ls_bytes())?; | ||
self.masm | ||
.push(&(init_code_length + return_instr_length).to_ls_bytes())?; | ||
self.masm.asm._return()?; | ||
} | ||
// 1. copy runtime bytecode to memory | ||
masm.push(&runtime_bytecode_size)?; // code size | ||
masm.push(&runtime_bytecode_offset.to_ls_bytes())?; // code offset | ||
masm._push0()?; // dest offset in memory | ||
masm._codecopy()?; | ||
|
||
self.masm | ||
.buffer_mut() | ||
.extend_from_slice(&self.runtime_bytecode); | ||
// 2. return runtime bytecode | ||
masm.push(&runtime_bytecode_size)?; // code size | ||
masm._push0()?; // memory offset | ||
masm.asm._return()?; | ||
masm.buffer_mut().extend_from_slice(&runtime_bytecode); | ||
|
||
Ok(self.masm.buffer().into()) | ||
Ok(masm.buffer().into()) | ||
} | ||
|
||
/// Returns the length of instructions. | ||
fn return_instr_length(init_code_length: usize, runtime_bytecode_length: usize) -> usize { | ||
let mut expected_length = | ||
runtime_bytecode_length.to_ls_bytes().len() + init_code_length.to_ls_bytes().len() + 3; | ||
|
||
if init_code_length < 0xff && init_code_length + expected_length > 0xff { | ||
expected_length += 1; | ||
/// Returns the offset of runtime bytecode. | ||
/// | ||
/// [ | ||
/// init_code, | ||
/// pushn, runtime_bytecode_size, pushn + <offset>, push0, code_copy | ||
/// pushn, runtime_bytecode_size, push0, return, | ||
/// <OFFSET> | ||
/// ] | ||
fn runtime_bytcode_offset(init_code_len: usize, runtime_bytecode_size_len: usize) -> usize { | ||
let mut offset = init_code_len + runtime_bytecode_size_len * 2 + 8; | ||
if (offset <= 0xff) && (offset + offset.to_ls_bytes().len() > 0xff) { | ||
offset += 1; | ||
} | ||
|
||
expected_length | ||
offset | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,46 +1,16 @@ | ||
//! Zink compiler artifact | ||
use crate::{Compiler, Config}; | ||
use wasmparser::FuncType; | ||
use crate::Config; | ||
use zabi::Abi; | ||
use zingen::Constructor; | ||
|
||
/// Zink compiler artifact | ||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||
#[derive(Default, Debug)] | ||
pub struct Artifact { | ||
/// Contract ABIs | ||
pub abi: Vec<Abi>, | ||
/// Bytecode of the contract. | ||
pub bytecode: Vec<u8>, | ||
/// Compiler configuration. | ||
pub config: Config, | ||
/// Runtime bytecode of the contract. | ||
pub runtime_bytecode: Vec<u8>, | ||
} | ||
|
||
impl TryFrom<(Compiler, Option<FuncType>)> for Artifact { | ||
type Error = anyhow::Error; | ||
|
||
fn try_from( | ||
(compiler, constructor): (Compiler, Option<FuncType>), | ||
) -> Result<Self, Self::Error> { | ||
let Compiler { | ||
abi, | ||
buffer, | ||
config, | ||
.. | ||
} = compiler; | ||
|
||
let bytecode = Constructor::new(constructor, buffer.clone())? | ||
.finish()? | ||
.to_vec(); | ||
|
||
Ok(Self { | ||
abi, | ||
bytecode, | ||
config, | ||
runtime_bytecode: buffer.to_vec(), | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.