openethereum / pwasm-tutorial

A step-by-step tutorial on how to write contracts in Wasm for Kovan
GNU General Public License v3.0
229 stars 34 forks source link

Add real life example in tutorial #29

Open Pzixel opened 6 years ago

Pzixel commented 6 years ago

I'm currently trying to port some Solidity code into wasm using pwasm. But I have a problem that I don't see enough examples or documentation to perform such a transform. For example, here is a simple Solidity contract:

contract Example {    
    string[] values;

    function addValue(string value) public onlyOwner {
        values.push(value);
    }

    function getValue(uint i) public returns(string) {
        return values[i];
    }

    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    }
}

it's very simple but it shows all properties that real contracts have: it has onlyOwner modifier, it works with dynamically-sized data and it push values into internal storage (which is not prealocated). Token examples are too simple and don't show how to perform such operations that are crucial for most contracts.

I'm playing with these storage_keys!/pwasm_ethereum::write and so on, but it doesn't look I'm even approaching the solution. Currently It's actually a blocker to me, without stretching storage I cannot migrate my code to wasm.


I'd appreciate any help because I'm currently stuck with that - I only see how I can write into preallocated fixed size storage, but I don't see how to dynamically increase it.

Pzixel commented 6 years ago

I managed to emulate an array, but I only could save ony fixed-sizd items smaller than 32 bytes. It won't work for string or any arbitrary-sized type. Stuck with that currently Code is based on step-3 sample

pub mod token {
    use pwasm_std::Vec;
    use pwasm_ethereum;
    use parity_hash::H256;
    use bigint::U256;

    macro_rules! storage_keys {
        () => {};
        ($($name:ident),*) => {
            storage_keys!(0u8, $($name),*);
        };
        ($count:expr, $name:ident) => {
            static $name: H256 = H256([$count, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
        };
        ($count:expr, $name:ident, $($tail:ident),*) => {
            static $name: H256 = H256([$count, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
            storage_keys!($count + 1u8, $($tail),*);
        };
    }

    // eth_abi is a procedural macros https://doc.rust-lang.org/book/first-edition/procedural-macros.html
    use pwasm_abi_derive::eth_abi;

    #[eth_abi(TokenEndpoint, TokenClient)]
    pub trait TokenInterface {
        /// The constructor
        fn constructor(&mut self, _total_supply: U256);
        /// Total amount of tokens
        #[constant]
        fn totalSupply(&mut self) -> U256;

        #[constant]
        fn getNumbers(&mut self) -> Vec<U256>;

        fn addNumber(&mut self, value: U256);
    }

    storage_keys!(
        TOTAL_SUPPLY_KEY,
        DATA_LEN,
        DATA_KEY
    );

    pub struct TokenContract;

    impl TokenInterface for TokenContract {
        fn constructor(&mut self, total_supply: U256) {
            // Set up the total supply for the token
            pwasm_ethereum::write(&TOTAL_SUPPLY_KEY, &total_supply.into());
        }

        fn totalSupply(&mut self) -> U256 {
            pwasm_ethereum::read(&TOTAL_SUPPLY_KEY).into()
        }

        fn getNumbers(&mut self) -> Vec<U256> {
            let num: u8 = pwasm_ethereum::read(&DATA_LEN)[31];
            let mut result = Vec::with_capacity(num as usize);
            for i in 0..num {
                let mut key = DATA_KEY.clone();
                key[31] = i;
                result.push(pwasm_ethereum::read(&key).into())
            }
            result
        }

        fn addNumber(&mut self, value: U256) {
            let num: u8 = pwasm_ethereum::read(&DATA_LEN)[31];
            let new_num: U256 = (num + 1).into();
            pwasm_ethereum::write(&DATA_LEN, &new_num.into());

            let mut key = DATA_KEY.clone();
            key[31] = num;
            pwasm_ethereum::write(&key, &value.into());
        }
    }
}