9

I have written a contract that has the mapping storage variable A & B. These variables get initialized with some values in constructor. A method getBalance returns the balance from mapping A & B.

In the method I declared another mapping - thinking that it would give a compilation error (since mapping allowed for storage only). Instead it lead to a strange behavior - appears that the new local mapping overwrites mappingA.

Result without local mapping (100, 200) as expected Result with local mapping (500, 200) <<< Why?

/** Tested in Truffle/TestRPC **/

contract Mapping {

  mapping(address => uint)  balancesA;
  mapping(address => uint)  balancesB;

  function Mapping() {
    // constructor
    balancesA[msg.sender] = 100;
    balancesB[msg.sender] = 200;
  }

  function getBalance(address addr) returns(uint, uint) {

    // If these 2 lines are commented then behavior is as expected
    mapping(address => uint)  balancers;
    balancers[msg.sender] = 500;

    return (balancesA[addr], balancesB[addr]);

  }
}
6
  • I can confirm that. The mapping in the function seems to collide with the first mapping that was declared properly. Commented Mar 1, 2017 at 13:20
  • Have you tried running it on testnet (or private network)? Sometimes TestRPC behaves differently then "real" network - maybe thats the case.
    – radmen
    Commented Mar 1, 2017 at 13:38
  • 1
    I can reproduce in Geth too. Commented Mar 1, 2017 at 18:08
  • 1
    @ACloudFan It looks like it's bug report time. Do you want to do the honors? Commented Mar 2, 2017 at 4:46
  • 1
    Bug aside, the return parameters aren't being used correctly. Should be returns (uint, uint) rather than being named, or balanceA = balancesA[addr]; etc
    – o0ragman0o
    Commented Mar 3, 2017 at 0:50

3 Answers 3

5

Opened an issue here, github, ethereum, solidity, issue 1731: https://github.com/ethereum/solidity/issues/1731

If I understand their process correctly, original issue 1731 is closed by issue 1737 which is now merged. Possibly the compiler is going to emit an error for this sort of thing in the future.

Both thumbs up for the devs!

https://github.com/ethereum/solidity/pull/1737

0
2

I'm afraid this is not a bug. I very much agree that it's a problem and presents a huge trap, though.

Please see the documentation here: https://solidity.readthedocs.io/en/v0.4.9/types.html#data-location

As mentioned in max taldykin's answer: By declaring the local variable balancers inside the function you're merely creating a new reference with a different name that points to a mapping in storage. I guess it just links to the first one it finds declared on the global level.

Declaring the local variable as memory would prevent the overwrite.

mapping(address => uint) memory balancers;

To be clear: this problem only applies to non-primitives (structs, mappings, and arrays) when declared locally inside functions as their default location is storage as explained in the doc linked above!

3
  • 1
    the bug is that compiler does not warn about using uninitialized variable. Commented Mar 3, 2017 at 19:31
  • Thanks. Interesting, and scary. Maybe not a "bug" but not helpful or wanted in an unforgiving environment. Sort of like this: vessenes.com/solidity-frustrations-references-and-mapping reference vars and weird mapping overlaps; I do not like them Sam I Am. Commented Mar 4, 2017 at 6:17
  • @maxtaldykin Seems like the compile folks agree with you. They've added issue 1737 (1731) to the issue I reported and they seem to be working on it. github.com/ethereum/solidity/pull/1737 Commented Mar 4, 2017 at 6:22
1

Aside from that it is a bug in solidity (as it should notice that uninitialized variable is used), it worth mentioning that the code is not correct also.

The problem is that mapping(address => uint) balancers in function body does not allocate new mapping. It just introduces variable balancers which can reference some mapping (allocated elsewhere).

In the example balancers is not initialized and coincidentially references balancesA. Hence the unexpected behavior.

From Solidity docs:

Mappings are only allowed for state variables (or as storage reference types in internal functions).

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.