Getting MemoryOverflow at ccp instruction on local node

Hi,

I’m trying to read contract’s bytecode from another contract and I’m getting a “MemoryOverflow” revert on the my TX.

This is my contract:

contract;

abi Counter {
    fn get_first_8_bytes(contract_id: ContractId) -> [u8; 8];
}

impl Counter for Contract {
    fn get_first_8_bytes(contract_id: ContractId) -> [u8; 8] {
        let mut bytecode_array: [u8; 8] = [0; 8];
        let mut counter = 0;
       while counter < 2 {
            bytecode_array[counter] = _get_bytecode_byte_at(contract_id, counter);
            counter += 1;
        }
        bytecode_array
    }
}

fn _get_bytecode_byte_at(contract_id: ContractId, pos: u64) -> u8 {
    let mut bytecode: u8 = 0;
    let amount = 1;
    asm(target: contract_id, bt: &bytecode, pos: pos, amount: amount) {
        ccp bt target pos amount;
    }
    bytecode
}

It only doesn’t work on my local node and I’m getting the following error:

Transaction(Reverted { reason: "MemoryOverflow", revert_id: 0, receipts: [Call { id: 0000000000000000000000000000000000000000000000000000000000000000, to: 39f7d6002550e17e713ea3e329fbcd967848f3d7e6b959786f929aefb5b4f165, amount: 0, asset_id: f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07, gas: 377745, param1: 10480, param2: 10505, pc: 11960, is: 11960 }, Panic { id: 39f7d6002550e17e713ea3e329fbcd967848f3d7e6b959786f929aefb5b4f165, reason: PanicInstruction { reason: MemoryOverflow, instruction: CCP { dst_addr: 0x13, contract_id_addr: 0x10, offset: 0x11, len: 0x14 } (bytes: 2e 4d 04 54) }, pc: 18632, is: 11960, contract_id: None }, ScriptResult { result: Panic, gas_used: 24382 }] })

We can see that it points to the CCP instruction above.

When I run the harness test on the testnet the TX succeeds and I’m getting the value out:

running 1 test
Bytecode: [26, 240, 0, 0, 0, 0, 0, 0]
Gas used: 21243

This is my harness function:

    #[tokio::test]
    async fn test_bytecode() {
        let (_instance1, id1) = get_contract_instance().await;
        let (instance2, _id2) = get_contract_instance().await;
        let bech32_id1 = Bech32ContractId::from(id1);

        let result = instance2
            .methods()
            .get_first_8_bytes(id1)
            .with_tx_policies(
                TxPolicies::default()
                .with_script_gas_limit(400000)
            )
            .with_contract_ids(&[bech32_id1])
            .call()
            .await
            .unwrap();

        println!("Bytecode: {:?}", result.value);
        println!("Gas used: {}", result.gas_used);
    }

Could this be something wrong with my local node configuration?

yes, this could be due to the local node config. please ensure you are using the correct snapshot and all the versions are updated. ALso, can you please share the repo, so that I can debug it locally?

Here is the repo:

you just need to change one line:

and add your secret key.

I’m also updating fuel-core as I was using 0.35.0 and the latest is 0.36.0 so maybe that fixes the issue. [EDIT: same thing on 0.36.0]

Let me also give you broader context why I am trying this. I noticed there is a following issue:

In Sway there is no create opcode hence if we have a Factory contract, the only thing that the Factory contract can do is to verify the bytecode root of a provided deployed manually contract against stored bytecode root. While this is ok, it is a bit problematic if the configurables block is used, because bytecode hash, of course, changes if the configurables are set differently in the newly deployed contract that needs to be “attached” to the factory.

I was thinking to solve this, by creating a library that would do the following:

  1. copy to memory the bytecode of a model contract whose ContractId is stored at the Factory,
  2. put it into a Vec<u8>
  3. leverage the existing _swap_configurables() function (from sway-libs/libs/src/bytecode/utils.sw) to change the configurables as they should be
  4. calculate the bytecode root of that
  5. compare the calculated bytecode root with the bytecode root of the newly deployed contract that is supposed to be “attached” to the Factory.

Do you think this would make sense? I think the issue is that it might be quite expensive in terms of gas, but provides better protection than the case when you’re not using configurables (because the current solution is to not use configurables, but have a permissionless initialize() to set the regular storage variables) - which is e.g. prone to front-running attacks.

Also do let me know if you are working on other solution to this problem, so that I don’t reinvent the wheel.

Where could I check if I’m using latest-and-greatest snapshot? [EDIT: It’s ok, I found it!]

[EDIT 2: After getting latest config + upgrade to 0.36.0 it started working! But would still like to hear your opinion on the above Factory issue!]


jecikpo

1 Like

Thanks @jecikpo, it’s great to hear that it worked for you. regarding the factory issue, I’ll have a look at it and if required, discuss it internally and get back to you :slight_smile:

1 Like