ab_system_contract_code/
lib.rs

1#![no_std]
2
3use ab_contracts_common::env::{Env, MethodContext};
4use ab_contracts_common::{Address, ContractError, MAX_CODE_SIZE};
5use ab_contracts_io_type::trivial_type::TrivialType;
6use ab_contracts_io_type::variable_bytes::VariableBytes;
7use ab_contracts_macros::contract;
8use ab_system_contract_address_allocator::AddressAllocatorExt;
9
10#[derive(Debug, Copy, Clone, TrivialType)]
11#[repr(C)]
12pub struct Code;
13
14#[contract]
15impl Code {
16    /// Deploy a new contract with specified code
17    #[update]
18    pub fn deploy(
19        #[env] env: &mut Env<'_>,
20        #[input] code: &VariableBytes<MAX_CODE_SIZE>,
21    ) -> Result<Address, ContractError> {
22        let new_contract_address = env.address_allocator_allocate_address(
23            MethodContext::Replace,
24            Address::system_address_allocator(env.shard_index()),
25        )?;
26
27        env.code_store(
28            MethodContext::Replace,
29            env.own_address(),
30            &new_contract_address,
31            code,
32        )?;
33
34        Ok(new_contract_address)
35    }
36
37    /// Store contact's code overriding previous code that might have been there.
38    ///
39    /// Updates can only be done by the contract itself with direct calls.
40    // TODO: Some code validation?
41    #[update]
42    pub fn store(
43        #[env] env: &mut Env<'_>,
44        #[slot] (address, contract_code): (&Address, &mut VariableBytes<MAX_CODE_SIZE>),
45        #[input] new_code: &VariableBytes<MAX_CODE_SIZE>,
46    ) -> Result<(), ContractError> {
47        // TODO: Would it be helpful to allow indirect updates?
48        // Allow updates to system deploy contract (for initial deployment) and to contract itself
49        // for upgrades, but only direct calls
50        if !(env.caller() == Address::NULL
51            || env.caller() == env.own_address()
52            || env.caller() == address)
53        {
54            return Err(ContractError::Forbidden);
55        }
56
57        if !contract_code.copy_from(new_code) {
58            return Err(ContractError::BadInput);
59        }
60
61        Ok(())
62    }
63
64    /// Read contract's code
65    #[view]
66    pub fn read(
67        #[slot] contract_code: &VariableBytes<MAX_CODE_SIZE>,
68        #[output] code: &mut VariableBytes<MAX_CODE_SIZE>,
69    ) -> Result<(), ContractError> {
70        if code.copy_from(contract_code) {
71            Ok(())
72        } else {
73            Err(ContractError::BadInput)
74        }
75    }
76}