Implement missing host functions
pallet-revive has added a number of host functions that we don't support in ink! yet. I've just compiled the list and am creating this tracking issue for them.
- [ ]
fn get_immutable_data(output: &mut &mut [u8]); - [ ]
fn set_immutable_data(data: &[u8]); - [x]
fn balance_of(addr: &[u8; 20], output: &mut [u8; 32]); - [x]
fn chain_id(output: &mut [u8; 32]); - [x]
fn gas_price() -> u64; - [x]
fn base_fee(output: &mut [u8; 32]); - [x]
fn call_data_size() -> u64; - [x]
fn origin(output: &mut [u8; 20]); - [x]
fn code_size(addr: &[u8; 20]) -> u64; - [ ]
fn call_data_load(output: &mut [u8; 32], offset: u32); - [x]
fn gas_limit() -> u64; - [ ]
fn set_storage_or_clear(flags: StorageFlags, key: &[u8; 32], value: &[u8; 32]) -> Option<u32>; - [ ]
fn get_storage_or_zero(flags: StorageFlags, key: &[u8; 32], output: &mut [u8; 32]); - [x]
fn return_data_size() -> u64; - [ ]
fn return_data_copy(output: &mut &mut [u8], offset: u32); - [x]
fn ref_time_left() -> u64; - [x]
fn block_author(output: &mut [u8; 20]); - [x]
fn block_hash(block_number: &[u8; 32], output: &mut [u8; 32]);
Having the host functions supported is not a requirement for an ink! v6 release. Some are easy to implement, while others (e.g. immutable data) require more thought about the design.
@LucasGrasso This is a tracking issue, so you don't have to implement all of these functions at all. Let's just start with a single one: gas_limit. This is a host function that pallet-revive exposes to us, it already exists there. It just doesn't exist in ink! yet.
You can take a look at the function transferred_value and how we've implemented it (just full-text search the codebase). It's a similar function, it just gets some value from pallet-revive and returns it to the smart contract.
You'll find a number of places where transferred_value is used and you can implement gas_limit by taking the transferred_value code as a blueprint.
We use transferred_value in a bunch of contract examples (those are in integration-tests). For gas_limit it's best if you add a new contract example integration-tests/internal/gas. This contract should have a contract function that uses gas_limit + an E2E test that tests this contract function.
Feel free to ask questions in case you have questions!
Edit: Some pointers
- https://github.com/paritytech/polkadot-sdk/blob/master/substrate/frame/revive/src/vm/pvm/env.rs#L625-L631 This is where
pallet-revivedefinesgas_limit. - https://github.com/use-ink/ink/blob/master/crates/env/src/engine/on_chain/pallet_revive.rs#L890-L896 This is how
transferred_valuecallspallet-revive. There are traits and trait implementations that call this function, hence you're best of doing a full-text search.
Thanks for the introduction and help. How should one implement the hostfn in the off-chain engine? I suppose that the gas limit value should be stored in the Engine Database as one can not access revive? How are those db values configured?
@LucasGrasso We are in the process of removing the off-chain testing environment. Hence you can just add the method there, but panic inside of it.
gas_limitshould be marked as completed.- I will continue with the implementation of the rest of the 0-ary functions
fn gas_price() -> u64fn call_data_size() -> u64fn return_data_size() -> u64fn ref_time_left() -> u64
I will continue implementing the getters akin to EVM opcodes and block_author.
- fn chain_id(output: &mut [u8; 32]);
- fn balance_of(addr: &[u8; 20], output: &mut [u8; 32]);
- fn base_fee(output: &mut [u8; 32]);
- fn origin(output: &mut [u8; 20]);
- fn code_size(addr: &[u8; 20]) -> u64;
- fn block_hash(block_number: &[u8; 32], output: &mut [u8; 32]);
- fn block_author(output: &mut [u8; 20]);
Please mark gas_price, call_data_size, return_data_size, ref_time_left as completed.
@LucasGrasso That's great! I've marked the others as ☑️.
Now that #2719 is merged, please mark chain_id, balance_of, base_fee, origin, code_size, block_hash, block_author as ☑️.
Will continue implementing the two "easy" hostnfs(return_data_copy and call_data_load) and the storage ones. Would you rather have two separate storage getters / setters based on the flag (Transient Storage or not)? That way we can be more assimilated to the SSTORE-SLOAD and TSTORE-TLOAD opcodes.
To begin thinking about the inmutable data hostfns: what are some designs considerations to take into account?