3

Working on contract optimization and found that I actually do not need use uint256 everywhere. Mostly I am using uint32 or even uint16, even payment amount could be packed into 32 bits with reasonable limitations.

But generally I see that I cannot do anything with mapping keys - despite of data size, absolutely any uint less then 256 bits makes insert operation more expensive then with uint256. For instance, insertion to the following mappings cause next gas usage:

mapping (uint => uint) vals;       // Insertion cost 25669
mapping (uint64 => uint) vals64;   // Insertion cost 26061
mapping (uint12 => uint) vals;     // Insertion cost 25885

Why this happens?

Does it makes sense at least to change data types in structs so make it fit into 256 bits?

UPD: Yeah, it looks like INSERT (SSTORE) operation always reserve 256 bits for inserted data type. But if data stored in arrays decreasing its size gives small benefit:

contract A4Contract {
    uint256[] vals;
    uint64[] vals64;

    // 1: 40544
    // 2: 25544
    // 3: 25544
    function insert(uint256 value) public {
        vals.push(value);
    }

    // 1: 40820 creates array, reserves 256 bits chunk and insert 64 bits
    // 2: 10870 - insert 64 bits into already reserved 256 bits
    // 3: 10870 - insert 64 bits into already reserved 256 bits
    // 4: 10870 - insert 64 bits into already reserved 256 bits
    // 5: 25820 reserves next 256 bits
    // 6: 10870 - insert 64 bits into already reserved 256 bits 
    // 7: 10870 - insert 64 bits into already reserved 256 bits
    // 8: 10870 - insert 64 bits into already reserved 256 bits
    // 9: 25820 reserves next 256 bits
    function insert64(uint64 value) public {
        vals64.push(value);
    }
}

So, finally mapping is suitable only to hold something by address or something packed into uint256 and accessed with full set of packed data (to re-create key and get value from mapping).

  • Hey, thanks for the update, it's logical what you are saying, looking for example at the EVM which works with 256-bit memory blocks. – CPereez19 May 20 '18 at 15:21
1

Is true you can use almost any type for the mapping key, except for a mapping, a dynamically sized array, a contract, an enum and a struct.

But what happens under the hood is that your actual key is not stored in blockchain, but rather his hash (SHA3 / Keccak256), so, at end, you'll always have a uint256 has your mapping key.

The hash key is calculated by taking the SHA3 of your key, appending it to the SHA3 of your contract, and taking SHA3 of that, which then becomes the memory storage location of your value.

By the way, your mappings are sharing the same memory space as all other mappings in the world. Your contract has access to a huge 2^256 bit virtual address space.

The entire EVM memory space is a near infinite and globally shared hash table.

  • I assume if one can produce a hash collision they can modify storage of other contracts? – Mikko Ohtamaa Jan 17 '19 at 15:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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