ab_system_contract_code/
lib.rs

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