Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Commit

Permalink
Merge pull request #2948 from trufflesuite/immutable-fix-066
Browse files Browse the repository at this point in the history
Fix allocation of immutables for Solidity 0.6.6
  • Loading branch information
haltman-at authored Apr 10, 2020
2 parents 92b0781 + 6e31620 commit 02812b6
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 21 deletions.
22 changes: 20 additions & 2 deletions packages/codec/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,31 @@ which accept decodings in either mode and always return ABI mode.
multiple decodings (e.g. [[DecodedLog]]) may contain decodings in different
modes.

- You can only decode storage variables in full mode. If full mode fails
while decoding a storage variable, it will throw an exception.
- You can only decode state variables in full mode. If full mode fails
while decoding a state variable, it will throw an exception.

- If a contract `Base` declares an event `Event` and a contract `Derived`
inheriting from `Base` overrides `Event`, if `Derived` then emits
`Base.Event`, ABI mode may not be able to decode it.

#### Additional notes on decoding state variables

- While internal function pointers can only be decoded in full mode,
full mode still may not be able to determine all the information about
them. Thus, for internal function pointers, you may get a bare-bones
decoding, or you may get a decoding with more information.

- Solidity 0.6.5 contains a bug that may cause some state variables to
decode incorrectly if there is an immutable state variable which is
written to but never read from.

- In any version of Solidity, it is impossible to decode an immutable
state variable which is written to but never read from; these will
decode to an error.

- Not all constant state variables can presently be decoded; some of
these may simply decode to an error.

---

<p align="center">
Expand Down
2 changes: 1 addition & 1 deletion packages/codec/lib/ast/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export interface TypeDescriptions {

export interface AstNode {
constant?: boolean;
immutable?: boolean;
mutability?: "mutable" | "immutable" | "constant"; //variable, not function, mutability!
id: number;
name: string;
canonicalName?: string;
Expand Down
18 changes: 11 additions & 7 deletions packages/codec/lib/storage/allocate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,18 +322,22 @@ function allocateContractState(
})
);

//just in case the constant field ever gets removed
const isConstant = (definition: Ast.AstNode) =>
definition.constant || definition.mutability === "constant";

//now: we split the variables into storage, constant, and code
let [constantVariables, variableVariables] = partition(
variables,
variable => variable.definition.constant
let [constantVariables, variableVariables] = partition(variables, variable =>
isConstant(variable.definition)
);

//why use this function instead of just checking
//definition.immutable?
//because of a bug in Solidity 0.6.5 that causes the immutable flag
//definition.mutability?
//because of a bug in Solidity 0.6.5 that causes the mutability field
//not to exist. So, we also have to check against immutableReferences.
const isImmutable = (definition: Ast.AstNode) =>
definition.immutable || definition.id.toString() in immutableReferences;
definition.mutability === "immutable" ||
definition.id.toString() in immutableReferences;

let [immutableVariables, storageVariables] = partition(
variableVariables,
Expand Down Expand Up @@ -410,7 +414,7 @@ function allocateContractState(
//now, reweave the three together
let contractAllocation: StateVariableAllocation[] = [];
for (let variable of variables) {
let arrayToGrabFrom = variable.definition.constant
let arrayToGrabFrom = isConstant(variable.definition)
? constantVariableAllocations
: isImmutable(variable.definition)
? immutableVariableAllocations
Expand Down
11 changes: 9 additions & 2 deletions packages/debugger/test/data/immutable.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import * as Codec from "@truffle/codec";
import solidity from "lib/solidity/selectors";

const __IMMUTABLE = `
pragma solidity ^0.6.5;
pragma solidity ^0.6.6;
contract Base {
int8 immutable base = -37;
Expand All @@ -28,6 +28,7 @@ contract ImmutableTest is Base {
bool immutable truth;
address immutable self;
byte immutable secret;
uint8 immutable trulySecret;
event Done();
Expand All @@ -36,6 +37,7 @@ contract ImmutableTest is Base {
truth = true;
self = address(this);
secret = 0x88;
trulySecret = 23;
emit Done(); //BREAK CONSTRUCTOR
}
Expand Down Expand Up @@ -113,6 +115,10 @@ describe("Immutable state variables", function() {
};

assert.deepInclude(variables, expectedResult);

const trulySecret = await session.variable("trulySecret");
assert.strictEqual(trulySecret.kind, "error");
assert.strictEqual(trulySecret.error.kind, "UnusedImmutableError");
});

it("Decodes immutables properly in constructor", async function() {
Expand Down Expand Up @@ -145,7 +151,8 @@ describe("Immutable state variables", function() {
background: "ImmutableTest.Color.Blue",
truth: true,
self: address,
secret: "0x88"
secret: "0x88",
trulySecret: 23
};

assert.deepInclude(variables, expectedResult);
Expand Down
2 changes: 1 addition & 1 deletion packages/debugger/test/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export async function prepareContracts(provider, sources = {}, migrations) {

config.compilers = {
solc: {
version: "0.6.5",
version: "0.6.6",
settings: {
optimizer: { enabled: false, runs: 200 },
evmVersion: "constantinople"
Expand Down
12 changes: 4 additions & 8 deletions packages/decoder/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,20 +63,16 @@ The decoder outputs lossless, machine-readable [[Format.Values.Result]] objects
containing individual decoded values. See the [[Format|format documentation]]
for an overview and complete module listing.
Note that for technical reasons, the decoder cannot always fully decode
internal function pointers, but it will do its best even when information is
missing, and will still losslessly return what information it can. If you want
to make sure to get the full information, see the advice
[here](../#decoding-modes) about how to make sure "full mode" works; the same
applies to decoding of internal function pointers.
### Decoding modes and abification
### Decoding modes, abification, and caveats
The decoder runs in either of two modes: full mode or ABI mode. Full mode
requires some additional constraints but returns substantially more detailed
information. Please see the notes on [decoding modes](../#decoding-modes) for
more about this distinction.
See also the notes about [decoding state variables](../#additional-notes-on-decoding-state-variables) for additional
caveats about what may or may not be fully decodable.
### Basic usage examples
#### Decoding a log with the wire decoder
Expand Down

0 comments on commit 02812b6

Please sign in to comment.