diff --git a/src/sol.ts b/src/sol.ts index 276df496..5420cf27 100644 --- a/src/sol.ts +++ b/src/sol.ts @@ -196,6 +196,9 @@ function solInst(inst: Inst): string { case 'SigCase': return `case when ${inst.condition} goto ${inst.offset} or fall ${inst.fallBranch.key}`; case 'SStore': { + const isLoad = (value: Expr) => + value.tag === 'SLoad' && solExpr(value.location) === solExpr(inst.location); + let varName = sol`storage[${inst.location}]`; if (inst.location.isVal() && inst.location.val.toString() in inst.variables) { const loc = inst.location.val.toString(); @@ -206,17 +209,11 @@ function solInst(inst: Inst): string { varName = `var${Object.keys(inst.variables).indexOf(loc) + 1}`; } } - if ( - inst.data.tag === 'Add' && - inst.data.left.tag === 'SLoad' && - solExpr(inst.data.left.location) === solExpr(inst.location) - ) { + if (inst.data.tag === 'Add' && isLoad(inst.data.left)) { return sol`${varName} += ${inst.data.right};`; - } else if ( - inst.data.tag === 'Sub' && - inst.data.left.tag === 'SLoad' && - solExpr(inst.data.left.location) === solExpr(inst.location) - ) { + } else if (inst.data.tag === 'Add' && isLoad(inst.data.right)) { + return sol`${varName} += ${inst.data.left};`; + } else if (inst.data.tag === 'Sub' && isLoad(inst.data.left)) { return sol`${varName} -= ${inst.data.right};`; } else { return sol`${varName} = ${inst.data};`; diff --git a/test/evm/storage.test.ts b/test/evm/storage.test.ts index 2edb3c8f..f0608d8f 100644 --- a/test/evm/storage.test.ts +++ b/test/evm/storage.test.ts @@ -1,6 +1,6 @@ import { expect } from 'chai'; -import { EVM, STEP, State, sol } from 'sevm'; +import { EVM, STEP, State, sol, yul } from 'sevm'; import type { Expr, Inst } from 'sevm/ast'; import { Add, Info, MappingLoad, MappingStore, Msg, Stop, Sub, Val } from 'sevm/ast'; @@ -8,8 +8,6 @@ import { compile } from '../utils/solc'; describe('evm::storage', function () { it('should store variable', function () { - // const store = { events: {}, variables: {}, mappings: {}, }; - // const evm = STORAGE(store); const step = STEP(); const state = new State(); @@ -29,7 +27,7 @@ describe('evm::storage', function () { val2 += 11; } }`; - const evm = new EVM(compile(src, '0.7.6', { context: this }).bytecode); + const evm = new EVM(compile(src, '0.7.6', this).bytecode); const state = new State(); evm.run(0, state); @@ -42,45 +40,82 @@ describe('evm::storage', function () { }); it.skip('should detect packed storage variable', function () { - const sol = `contract C { + const src = `contract C { uint256 val1 = 0; uint128 val2 = 7; uint128 val3 = 11; - fallback() external payable { + function() external payable { + // unchecked { + val1 = block.number + 5; - val2 = uint128(block.number) + 7; + // val2 = uint128(block.number) + 7; val3 = uint128(block.number) + 11; + + // uint64 val4 = 9; + // } } }`; - const evm = new EVM(compile(sol, '0.7.6', { context: this }).bytecode); + const evm = new EVM(compile(src, '0.5.5', this).bytecode); const state = new State(); evm.run(0, state); - // state.stmts.forEach((stmt) => console.log(`${stmt.eval()}`)); + // state.stmts.forEach(stmt => console.log(sol`${stmt.eval()}`)); + // state.stmts.forEach(stmt => console.log(yul`${stmt.eval()}`)); expect(Object.keys(evm.variables)).to.be.have.length(3); }); - it.skip('should find storage struct', function () { - const sol = `contract C { + it.skip('should find storage struct when no optimized', function () { + const src = `contract Test { struct T { uint256 val1; uint256 val2; } - T t; - + uint256 val3; fallback() external payable { t.val1 += 3; t.val2 += 11; + val3 += 7; } }`; - const evm = new EVM(compile(sol, '0.7.6', { context: this }).bytecode); - evm.start(); + const evm = new EVM(compile(src, '0.7.6', this, { enabled: false }).bytecode); + const state = new State(); + evm.run(0, state); + state.stmts.forEach(stmt => console.log(sol`${stmt}`)); + state.stmts.forEach(stmt => console.log(yul`${stmt}`)); + expect(evm.variables).to.be.have.keys('0x0', '0x1'); expect(evm.mappings).to.be.deep.equal({}); }); + it('should not find storage struct when optimized', function () { + const src = `contract Test { + struct T { uint256 val1; uint256 val2; } + T t; + fallback() external payable { + t.val1 += 3; + t.val2 += 11; + } + }`; + const evm = new EVM(compile(src, '0.7.6', this, { enabled: true }).bytecode); + const state = new State(); + evm.run(0, state); + + expect(state.stmts).to.be.have.length(3); + + expect(sol`${state.stmts[0]}`).to.be.equal('var1 += 0x3;'); + expect(sol`${state.stmts[1]}`).to.be.equal('var2 += 0xb;'); + expect(sol`${state.stmts[2]}`).to.be.equal('return;'); + + expect(yul`${state.stmts[0]}`).to.be.equal('sstore(0x0, add(0x3, sload(0x0)))'); + expect(yul`${state.stmts[1]}`).to.be.equal('sstore(0x1, add(0xb, sload(0x1)))'); + expect(yul`${state.stmts[2]}`).to.be.equal('stop()'); + + expect(evm.variables).to.be.have.keys('0', '1'); + expect(evm.mappings).to.be.deep.equal({}); + }); + describe('mappings', function () { it('should find mapping loads and stores', function () { const src = `contract C { @@ -94,7 +129,7 @@ describe('evm::storage', function () { allowance[address(this)][msg.sender] -= 11; } }`; - const evm = new EVM(compile(src, '0.7.6', { context: this }).bytecode); + const evm = new EVM(compile(src, '0.7.6', this).bytecode); const state = new State(); evm.run(0, state); @@ -134,9 +169,7 @@ describe('evm::storage', function () { expect(sol`${state.stmts[2]}`).to.be.equal( 'mapping3[address(this)][msg.sender] -= 0xb;' ); - expect(state.last).to.be.deep.equal(new Stop()); - expect(evm.variables).to.be.empty; }); });