zingen/codegen/
constructor.rs

1//! Contract constructor.
2
3use crate::{wasm::ToLSBytes, Buffer, MacroAssembler, Result};
4use smallvec::SmallVec;
5use std::collections::HashMap;
6
7/// Initial storage of contracts
8pub type InitStorage = HashMap<SmallVec<[u8; 32]>, SmallVec<[u8; 32]>>;
9
10/// Contract constructor.
11#[derive(Default, Debug, Clone)]
12pub struct Constructor {
13    /// Code generator.
14    masm: MacroAssembler,
15}
16
17impl Constructor {
18    /// preset storage for the contract
19    pub fn storage(&mut self, mapping: InitStorage) -> Result<()> {
20        tracing::debug!("Building storage in constructor ...");
21        for (key, value) in mapping.into_iter() {
22            self.masm.push(&value)?;
23            self.masm.push(&key)?;
24            self.masm._sstore()?;
25        }
26
27        Ok(())
28    }
29
30    /// Concat the constructor code.
31    ///
32    /// Here we override the memory totally with
33    /// the runtime bytecode.
34    pub fn finish(&self, runtime_bytecode: Buffer) -> Result<Buffer> {
35        let init_code = self.masm.buffer();
36        let init_code_len = init_code.len();
37        let runtime_bytecode_len = runtime_bytecode.len();
38        let runtime_bytecode_size = runtime_bytecode_len.to_ls_bytes();
39        let runtime_bytecode_offset =
40            Self::runtime_bytcode_offset(init_code_len, runtime_bytecode_size.len());
41
42        tracing::trace!("length of bytecode: {:?}", runtime_bytecode_len);
43        tracing::trace!(
44            "length of bytecode in hex: {:?}",
45            hex::encode(&runtime_bytecode_size)
46        );
47        let mut masm = self.masm.clone();
48
49        // 1. copy runtime bytecode to memory
50        masm.push(&runtime_bytecode_size)?; // code size
51        masm.push(&runtime_bytecode_offset.to_ls_bytes())?; // code offset
52        masm._push0()?; // dest offset in memory
53        masm._codecopy()?;
54
55        // 2. return runtime bytecode
56        masm.push(&runtime_bytecode_size)?; // code size
57        masm._push0()?; // memory offset
58        masm.asm._return()?;
59        masm.buffer_mut().extend_from_slice(&runtime_bytecode);
60
61        Ok(masm.buffer().into())
62    }
63
64    /// Returns the offset of runtime bytecode.
65    ///
66    /// [
67    ///   init_code,
68    ///   pushn, runtime_bytecode_size, pushn + <offset>, push0, code_copy
69    ///   pushn, runtime_bytecode_size, push0, return,
70    ///   <OFFSET>
71    /// ]
72    fn runtime_bytcode_offset(init_code_len: usize, runtime_bytecode_size_len: usize) -> usize {
73        let mut offset = init_code_len + runtime_bytecode_size_len * 2 + 8;
74        if (offset <= 0xff) && (offset + offset.to_ls_bytes().len() > 0xff) {
75            offset += 1;
76        }
77
78        offset
79    }
80}