2

Code

I defined a field in my contract like this

messages: Mapping<String, Vec<Message>>

Message is struct

pub struct Message {
    pub id: u128,
    pub key: String,
    pub p: String,
    pub b: String,
    pub s: Vec<String>,
    pub addr: AccountId,
    pub sel: [u8;4],
    pub data: Vec<u8>,
}

This is a method with pseudo-code

fn push_message(&mut self, message: Message) -> Result<(), Error> {
            let mut c_message = self.messages.get(&message.key).unwrap_or(Vec::<Message>::new());
            c_message.push(message);
            self.received_message_table.insert(message.key, &c_message);
            Ok(())
}

Operation

Repeatedly call push_message with the same key value to test the upper limit of Vec size.

Expectation

I can call push_message successfully every time until the weight exceeds the maxExtrinsic weight.

Result

I only pushed 67 message into the Vec, then I got the error contracts.ContractTraped. In the node terminal, I saw the output "range end index 16388 out of range for slice of length 16384".

What does this mean? I think the transaction fail is because of the storage limit, instead of gas limit, is it?

2

1 Answer 1

1

The source of the problem is that ink! internally uses a static buffer to avoid expensive heap allocations. The limitation of this static buffer is 16 KiB, which is 16384 bytes, so the limit that you are hitting:

https://github.com/paritytech/ink/blob/master/crates/env/src/engine/on_chain/buffer.rs#L22-L23

The limit was originally chosen to be ideal for the underlying database used in Substrate. There have been changes in the meantime now though, so that we're contemplating increasing the limit. There is also an open PR for ink! to make this limit configurable: https://github.com/paritytech/ink/pull/1279.

What I want to question though is why you are pushing the Message in its entirety to the Vec. It's typically an anti-pattern for smart contracts to use big vectors, as many operations become more gas-expensive on them instead of on e.g. a Mapping.

Could you use something along those lines, for example?

messages: Mapping<String, Vec<MessageId>>
messageid_to_message: Mapping<MessageId, Message>

This would decrease the amount stored in the Vec significantly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.