From 509bc7ceec1054f652ddda8fb99be57e191d5e14 Mon Sep 17 00:00:00 2001 From: szotov Date: Sun, 7 Apr 2019 15:02:24 +0300 Subject: [PATCH] Test example of avoiding some compiler changes --- .../eth/contracts/base/test_dispatcher.py | 78 ++++- .../contracts/contracts/proxy/ContractV1.sol | 28 +- .../contracts/contracts/proxy/ContractV2.sol | 28 +- .../contracts/contracts/proxy/ContractV3.sol | 2 +- .../contracts/contracts/proxy/ContractV4.sol | 295 ++++++++++++++++++ 5 files changed, 388 insertions(+), 43 deletions(-) create mode 100644 tests/blockchain/eth/contracts/contracts/proxy/ContractV4.sol diff --git a/tests/blockchain/eth/contracts/base/test_dispatcher.py b/tests/blockchain/eth/contracts/base/test_dispatcher.py index a65cf22e1..8e6a36a47 100644 --- a/tests/blockchain/eth/contracts/base/test_dispatcher.py +++ b/tests/blockchain/eth/contracts/base/test_dispatcher.py @@ -50,6 +50,7 @@ def test_dispatcher(testerchain): contract1_lib, _ = testerchain.interface.deploy_contract('ContractV1', 1) contract2_lib, _ = testerchain.interface.deploy_contract('ContractV2', 1) contract3_lib, _ = testerchain.interface.deploy_contract('ContractV3', 2) + contract4_lib, _ = testerchain.interface.deploy_contract('ContractV4', 3) contract2_bad_storage_lib, _ = testerchain.interface.deploy_contract('ContractV2BadStorage') dispatcher, _ = testerchain.interface.deploy_contract('Dispatcher', contract1_lib.address, secret_hash) assert contract1_lib.address == dispatcher.functions.target().call() @@ -322,7 +323,7 @@ def test_dispatcher(testerchain): tx = dispatcher.functions.upgrade(contract3_lib.address, secret, secret2_hash).transact({'from': creator}) testerchain.wait_for_receipt(tx) contract_instance = testerchain.interface.w3.eth.contract( - abi=contract2_lib.abi, + abi=contract3_lib.abi, address=dispatcher.address, ContractFactoryClass=Contract) assert contract3_lib.address == dispatcher.functions.target().call() @@ -344,6 +345,9 @@ def test_dispatcher(testerchain): assert 13 == contract_instance.functions.getStructureArrayValue2(0, 1).call() assert 2 == contract_instance.functions.storageValueToCheck().call() assert 'Hello' == contract_instance.functions.dynamicallySizedValue().call() + tx = contract_instance.functions.setAnotherStorageValue(77).transact({'from': creator}) + testerchain.wait_for_receipt(tx) + assert 77 == contract_instance.functions.anotherStorageValue().call() events = upgrades.get_all_entries() assert 4 == len(events) @@ -393,6 +397,78 @@ def test_dispatcher(testerchain): assert 1 == len(events) assert 33 == events[0]['args']['value'] + # Check upgrading to the contract with explicit storage slots + tx = dispatcher.functions.upgrade(contract4_lib.address, secret2, secret3_hash).transact({'from': creator}) + testerchain.wait_for_receipt(tx) + contract_instance = testerchain.interface.w3.eth.contract( + abi=contract4_lib.abi, + address=dispatcher.address, + ContractFactoryClass=Contract) + assert contract4_lib.address == dispatcher.functions.target().call() + assert 30 == contract_instance.functions.returnValue().call() + assert 5 == contract_instance.functions.storageValue().call() + assert 2 == contract_instance.functions.getArrayValueLength().call() + assert 12 == contract_instance.functions.arrayValues(0).call() + assert 232 == contract_instance.functions.arrayValues(1).call() + assert 41 == contract_instance.functions.mappingValues(14).call() + assert 31 == contract_instance.functions.mappingValues(13).call() + assert 3 == contract_instance.functions.arrayStructures(0).call() + assert 4 == contract_instance.functions.arrayStructures(1).call() + assert 11 == contract_instance.functions.getStructureArrayValue1(0, 0).call() + assert 111 == contract_instance.functions.getStructureArrayValue1(0, 1).call() + assert 12 == contract_instance.functions.getStructureArrayValue1(0, 2).call() + assert [4, 55] == contract_instance.functions.mappingStructures(0).call() + assert [5, 0] == contract_instance.functions.mappingStructures(1).call() + assert 12 == contract_instance.functions.getStructureArrayValue2(0, 0).call() + assert 13 == contract_instance.functions.getStructureArrayValue2(0, 1).call() + assert 3 == contract_instance.functions.storageValueToCheck().call() + assert 'Hello' == contract_instance.functions.dynamicallySizedValue().call() + assert 77 == contract_instance.functions.anotherStorageValue().call() + + events = state_verifications.get_all_entries() + assert 10 == len(events) + event_args = events[8]['args'] + assert contract4_lib.address == event_args['testTarget'] + assert creator == event_args['sender'] + assert event_args == events[9]['args'] + + events = upgrade_finishings.get_all_entries() + assert 6 == len(events) + event_args = events[5]['args'] + assert contract4_lib.address == event_args['target'] + assert creator == event_args['sender'] + + # Upgrade to the previous version - check that new `verifyState` can handle old contract + tx = dispatcher.functions.upgrade(contract3_lib.address, secret3, secret_hash).transact({'from': creator}) + testerchain.wait_for_receipt(tx) + assert contract3_lib.address == dispatcher.functions.target().call() + assert 20 == contract_instance.functions.returnValue().call() + assert 5 == contract_instance.functions.storageValue().call() + assert 2 == contract_instance.functions.getArrayValueLength().call() + assert 12 == contract_instance.functions.arrayValues(0).call() + assert 232 == contract_instance.functions.arrayValues(1).call() + assert 41 == contract_instance.functions.mappingValues(14).call() + assert 31 == contract_instance.functions.mappingValues(13).call() + assert 3 == contract_instance.functions.arrayStructures(0).call() + assert 4 == contract_instance.functions.arrayStructures(1).call() + assert 11 == contract_instance.functions.getStructureArrayValue1(0, 0).call() + assert 111 == contract_instance.functions.getStructureArrayValue1(0, 1).call() + assert 12 == contract_instance.functions.getStructureArrayValue1(0, 2).call() + assert [4, 55] == contract_instance.functions.mappingStructures(0).call() + assert [5, 0] == contract_instance.functions.mappingStructures(1).call() + assert 12 == contract_instance.functions.getStructureArrayValue2(0, 0).call() + assert 13 == contract_instance.functions.getStructureArrayValue2(0, 1).call() + assert 2 == contract_instance.functions.storageValueToCheck().call() + assert 'Hello' == contract_instance.functions.dynamicallySizedValue().call() + assert 77 == contract_instance.functions.anotherStorageValue().call() + + events = state_verifications.get_all_entries() + assert 12 == len(events) + event_args = events[10]['args'] + assert contract3_lib.address == event_args['testTarget'] + assert creator == event_args['sender'] + assert event_args == events[11]['args'] + @pytest.mark.slow def test_selfdestruct(testerchain): diff --git a/tests/blockchain/eth/contracts/contracts/proxy/ContractV1.sol b/tests/blockchain/eth/contracts/contracts/proxy/ContractV1.sol index 25b32c0ee..cc270cf6a 100644 --- a/tests/blockchain/eth/contracts/contracts/proxy/ContractV1.sol +++ b/tests/blockchain/eth/contracts/contracts/proxy/ContractV1.sol @@ -107,6 +107,8 @@ contract ContractV1 is Upgradeable { super.verifyState(_testTarget); require(delegateGet(_testTarget, "storageValue()") == storageValue); bytes memory value = delegateGetBytes(_testTarget, "dynamicallySizedValue()"); + // WARNING: sometimes execution of keccak256(string storage) on a long string (more than 31 bytes) + // leads to out of gas or exception require(value.length == bytes(dynamicallySizedValue).length && keccak256(value) == keccak256(bytes(dynamicallySizedValue))); @@ -121,8 +123,8 @@ contract ContractV1 is Upgradeable { require(delegateGet(_testTarget, "getStructureLength1()") == arrayStructures.length); for (uint256 i = 0; i < arrayStructures.length; i++) { - Structure1 memory structure1 = delegateGetStructure1(_testTarget, "arrayStructures(uint256)", bytes32(i)); - require(structure1.value == arrayStructures[i].value); + require(delegateGet(_testTarget, "arrayStructures(uint256)", bytes32(i)) == arrayStructures[i].value); + require(delegateGet(_testTarget, "getStructureArrayLength1(uint256)", bytes32(i)) == arrayStructures[i].arrayValues.length); for (uint256 j = 0; j < arrayStructures[i].arrayValues.length; j++) { @@ -134,8 +136,8 @@ contract ContractV1 is Upgradeable { require(delegateGet(_testTarget, "getStructureLength2()") == mappingStructuresLength); for (uint256 i = 0; i < mappingStructuresLength; i++) { - Structure2 memory structure2 = delegateGetStructure2(_testTarget, "mappingStructures(uint256)", bytes32(i)); - require(structure2.value == mappingStructures[i].value); + require(delegateGet(_testTarget, "mappingStructures(uint256)", bytes32(i)) == mappingStructures[i].value); + require(delegateGet(_testTarget, "getStructureArrayLength2(uint256)", bytes32(i)) == mappingStructures[i].arrayValues.length); for (uint256 j = 0; j < mappingStructures[i].arrayValues.length; j++) { @@ -146,24 +148,6 @@ contract ContractV1 is Upgradeable { } } - function delegateGetStructure1(address _target, string memory _signature, bytes32 _argument) - internal returns (Structure1 memory result) - { - bytes32 memoryAddress = delegateGetData(_target, _signature, 1, _argument, 0); - assembly { - result := memoryAddress - } - } - - function delegateGetStructure2(address _target, string memory _signature, bytes32 _argument) - internal returns (Structure2 memory result) - { - bytes32 memoryAddress = delegateGetData(_target, _signature, 1, _argument, 0); - assembly { - result := memoryAddress - } - } - function delegateGetBytes(address _target, string memory _signature) internal returns (bytes memory result) { diff --git a/tests/blockchain/eth/contracts/contracts/proxy/ContractV2.sol b/tests/blockchain/eth/contracts/contracts/proxy/ContractV2.sol index ae9df8601..601bea4ad 100644 --- a/tests/blockchain/eth/contracts/contracts/proxy/ContractV2.sol +++ b/tests/blockchain/eth/contracts/contracts/proxy/ContractV2.sol @@ -127,41 +127,31 @@ contract ContractV2 is Upgradeable { mappingValues[index]); } - require(uint256(delegateGet(_testTarget, "getStructureLength1()")) == arrayStructures.length); + require(delegateGet(_testTarget, "getStructureLength1()") == arrayStructures.length); for (uint256 i = 0; i < arrayStructures.length; i++) { - Structure1 memory structure1 = delegateGetStructure1(_testTarget, "arrayStructures(uint256)", bytes32(i)); - require(structure1.value == arrayStructures[i].value); + require(delegateGet(_testTarget, "arrayStructures(uint256)", bytes32(i)) == arrayStructures[i].value); - bytes32[] memory values = delegateGetArray(_testTarget, "getStructure1ArrayValues(uint256)", bytes32(i)); + uint256[] memory values = delegateGetArray(_testTarget, "getStructure1ArrayValues(uint256)", bytes32(i)); require(values.length == arrayStructures[i].arrayValues.length); for (uint256 j = 0; j < arrayStructures[i].arrayValues.length; j++) { - require(uint256(values[j]) == arrayStructures[i].arrayValues[j]); + require(values[j] == arrayStructures[i].arrayValues[j]); } } - require(uint256(delegateGet(_testTarget, "getStructureLength2()")) == mappingStructuresLength); + require(delegateGet(_testTarget, "getStructureLength2()") == mappingStructuresLength); for (uint256 i = 0; i < mappingStructuresLength; i++) { Structure2 memory structure2 = delegateGetStructure2(_testTarget, "mappingStructures(uint256)", bytes32(i)); require(structure2.value == mappingStructures[i].value); require(structure2.valueToCheck == mappingStructures[i].valueToCheck); - bytes32[] memory values = delegateGetArray(_testTarget, "getStructure2ArrayValues(uint256)", bytes32(i)); + uint256[] memory values = delegateGetArray(_testTarget, "getStructure2ArrayValues(uint256)", bytes32(i)); require(values.length == mappingStructures[i].arrayValues.length); for (uint256 j = 0; j < mappingStructures[i].arrayValues.length; j++) { - require(uint256(values[j]) == mappingStructures[i].arrayValues[j]); + require(values[j] == mappingStructures[i].arrayValues[j]); } } - require(uint256(delegateGet(_testTarget, "storageValueToCheck()")) == storageValueToCheck); - } - - function delegateGetStructure1(address _target, string memory _signature, bytes32 _argument) - internal returns (Structure1 memory result) - { - bytes32 memoryAddress = delegateGetData(_target, _signature, 1, _argument, 0); - assembly { - result := memoryAddress - } + require(delegateGet(_testTarget, "storageValueToCheck()") == storageValueToCheck); } function delegateGetStructure2(address _target, string memory _signature, bytes32 _argument) @@ -192,7 +182,7 @@ contract ContractV2 is Upgradeable { string memory _signature, bytes32 _argument ) - public returns (bytes32[] memory result) + public returns (uint256[] memory result) { bytes32 memoryAddress = delegateGetData(_target, _signature, 1, _argument, 0); assembly { diff --git a/tests/blockchain/eth/contracts/contracts/proxy/ContractV3.sol b/tests/blockchain/eth/contracts/contracts/proxy/ContractV3.sol index 489e28c17..4b3674589 100644 --- a/tests/blockchain/eth/contracts/contracts/proxy/ContractV3.sol +++ b/tests/blockchain/eth/contracts/contracts/proxy/ContractV3.sol @@ -15,7 +15,7 @@ contract ContractV3 is ContractV2 { } function setAnotherStorageValue(uint256 _value) public { - anotherStorageValue = _value * 2; + anotherStorageValue = _value; } function verifyState(address _testTarget) public { diff --git a/tests/blockchain/eth/contracts/contracts/proxy/ContractV4.sol b/tests/blockchain/eth/contracts/contracts/proxy/ContractV4.sol new file mode 100644 index 000000000..46023fbca --- /dev/null +++ b/tests/blockchain/eth/contracts/contracts/proxy/ContractV4.sol @@ -0,0 +1,295 @@ +pragma solidity ^0.5.3; + + +import "contracts/proxy/Upgradeable.sol"; + + +/** +* @dev Same test contract with explicit storage slots: +* The contract uses assembler code to access some storage variables instead of relying on the compiler +* This demonstrates how to mitigate possible changes in the compiler while using the proxy pattern +* Many methods are not optimized on purpose to increase readability +**/ +contract ContractV4 is Upgradeable { + + // slot allocation costs nothing + /// uint256 public storageValue; + uint256 reservedSlot5; + /// string public dynamicallySizedValue; + uint256 reservedSlot6; + /// uint256[] public arrayValues; + uint256 reservedSlot7; + /// mapping (uint256 => uint256) public mappingValues; + uint256 reservedSlot8; + /// uint256[] public mappingIndices; + uint256 reservedSlot9; + + struct Structure1 { + uint256 value; + uint256[] arrayValues; + } + /// Structure1[] public arrayStructures; + uint256 reservedSlot10; + + struct Structure2 { + uint256 value; + uint256[] arrayValues; + uint256 valueToCheck; + } + /// mapping (uint256 => Structure2) public mappingStructures; + uint256 reservedSlot11; + /// uint256 public mappingStructuresLength; + uint256 reservedSlot12; + + /// uint256 public storageValueToCheck; + uint256 reservedSlot13; + uint256 public anotherStorageValue; + + constructor(uint256 _storageValueToCheck) public { + setStorageValueToCheck(_storageValueToCheck); + } + + function returnValue() public pure returns (uint256) { + return 30; + } + + + /// @dev Get data from the specified slot + function getValue(uint256 _slotNumber) public view returns (uint256 value) { + assembly { + value := sload(_slotNumber) + } + } + + /// @dev Array data is in the slot keccak256(_slotNumber)+_index + function getArraySlot(uint256 _slotNumber, uint256 _index) private pure returns (uint256 slot) { + assembly { + // put data for hash into a free memory + let memoryAddress := mload(0x40) + mstore(memoryAddress, _slotNumber) + let baseSlot := keccak256(memoryAddress, 32) + slot := add(baseSlot, _index) + } + } + function getArrayValue(uint256 _slotNumber, uint256 _index) private view returns (uint256 value) { + return getValue(getArraySlot(_slotNumber, _index)); + } + + /// @dev Mapping data is in the slot keccak256(concat(_index, _slotNumber)) + function getMappingSlot(uint256 _slotNumber, uint256 _index) private pure returns (uint256 slot) { + assembly { + let memoryAddress := mload(0x40) + mstore(memoryAddress, _index) + mstore(add(memoryAddress, 32), _slotNumber) + slot := keccak256(memoryAddress, 64) + } + } + function getMappingValue(uint256 _slotNumber, uint256 _index) private view returns (uint256 value) { + return getValue(getMappingSlot(_slotNumber, _index)); + } + + + function storageValue() public view returns (uint256 value) { + // storageValue in the slot number 5 + return getValue(5); + } + + function dynamicallySizedValue() public view returns (string memory value) { + uint256 slotValue = getValue(6); + // https://solidity.readthedocs.io/en/latest/miscellaneous.html#bytes-and-string + uint8 lowestBit = uint8(slotValue & 1); + if (lowestBit == 0) { + uint8 length = uint8(bytes32(slotValue)[31]) / 2; + value = new string(length); + assembly { + mstore(add(value, 32), slotValue) + mstore8(add(value, 63), 0) + } + } else { + uint256 length = (slotValue - 1) / 2; + value = new string(length); + uint256 wordsCount = (length - 1) / 32 + 1; + for (uint256 i = 0; i < wordsCount; i++) { + uint256 word = getArrayValue(6, i); + uint256 offset = 32 * (i + 1); + assembly { + mstore(add(value, offset), word) + } + } + } + } + + function getArrayValueLength() public view returns (uint256) { + // length of the array in the slot number 7 + return getValue(7); + } + function arrayValues(uint256 _index) public view returns (uint256) { + require(_index < getArrayValueLength()); + // base slot for this array is 7 + return getArrayValue(7, _index); + } + + + function mappingValues(uint256 _index) public view returns (uint256) { + // base slot for this mapping is 8 + return getMappingValue(8, _index); + } + function getMappingIndicesLength() public view returns (uint256) { + // length of the array in the slot number 9 + return getValue(9); + } + function mappingIndices(uint256 _index) public view returns (uint256) { + require(_index < getMappingIndicesLength()); + return getArrayValue(9, _index); + } + + + function getStructureLength1() public view returns (uint256) { + // length of the array in the slot number 10 + return getValue(10); + } + function arrayStructures(uint256 _index) public view returns (uint256) { + require(_index < getStructureLength1()); + // base slot for this array is 10 + // one value in this array is `value` and the length of the inner `arrayValues` + // so each index represents 2 slots + return getArrayValue(10, 2 * _index); + } + function getStructureArrayLength1(uint256 _index) public view returns (uint256) { + require(_index < getStructureLength1()); + // same as above except accessing second part of the value + return getArrayValue(10, 2 * _index + 1); + } + /// @dev Array data is in the slot keccak256(keccak256(10) + 2 * _index + 1) + _arrayIndex + function getStructureArrayValue1(uint256 _index, uint256 _arrayIndex) public view returns (uint256) { + require(_arrayIndex < getStructureArrayLength1(_index)); + uint256 baseSlot = getArraySlot(10, 2 * _index + 1); + return getArrayValue(baseSlot, _arrayIndex); + } + function getStructure1ArrayValues(uint256 _index) public view returns (uint256[] memory result) { + result = new uint256[](getStructureArrayLength1(_index)); + uint256 baseSlot = getArraySlot(10, 2 * _index + 1); + for (uint256 i = 0; i < result.length; i++) { + result[i] = getArrayValue(baseSlot, i); + } + } + + function getStructureLength2() public view returns (uint256) { + return getValue(12); + } + function mappingStructures(uint256 _index) public view returns (uint256 value, uint256 valueToCheck) { + uint256 baseMappingSlot = getMappingSlot(11, _index); + // one mapping value is `value`, the length of the inner `arrayValues` and `valueToCheck` + value = getValue(baseMappingSlot); + valueToCheck = getValue(baseMappingSlot + 2); + } + function getStructureArrayLength2(uint256 _index) public view returns (uint256) { + return getValue(getMappingSlot(11, _index) + 1); + } + /// @dev Array data is in the slot keccak256(keccak256(concat(_index, 11)) + 1) + _arrayIndex + function getStructureArrayValue2(uint256 _index, uint256 _arrayIndex) public view returns (uint256) { + require(_arrayIndex < getStructureArrayLength2(_index)); + uint256 baseArraySlot = getMappingSlot(11, _index) + 1; + return getArrayValue(baseArraySlot, _arrayIndex); + } + function getStructure2ArrayValues(uint256 _index) public view returns (uint256[] memory result) { + result = new uint256[](getStructureArrayLength2(_index)); + uint256 baseArraySlot = getMappingSlot(11, _index) + 1; + for (uint256 i = 0; i < result.length; i++) { + result[i] = getArrayValue(baseArraySlot, i); + } + } + + function storageValueToCheck() public view returns (uint256) { + return getValue(13); + } + function setStorageValueToCheck(uint256 _value) public { + assembly { + sstore(13, _value) + } + } + + function verifyState(address _testTarget) public { + super.verifyState(_testTarget); + require(delegateGet(_testTarget, "storageValue()") == storageValue()); + bytes memory value = delegateGetBytes(_testTarget, "dynamicallySizedValue()"); + bytes memory originalValue = bytes(dynamicallySizedValue()); + require(value.length == originalValue.length && + keccak256(value) == keccak256(originalValue)); + + uint256 length = getArrayValueLength(); + require(delegateGet(_testTarget, "getArrayValueLength()") == length); + for (uint256 i = 0; i < length; i++) { + require(delegateGet(_testTarget, "arrayValues(uint256)", bytes32(i)) == arrayValues(i)); + } + length = getMappingIndicesLength(); + for (uint256 i = 0; i < length; i++) { + uint256 index = mappingIndices(i); + require(delegateGet(_testTarget, "mappingValues(uint256)", bytes32(index)) == mappingValues(index)); + } + + length = getStructureLength1(); + require(delegateGet(_testTarget, "getStructureLength1()") == length); + for (uint256 i = 0; i < length; i++) { + require(delegateGet(_testTarget, "arrayStructures(uint256)", bytes32(i)) == arrayStructures(i)); + + uint256 structuresLength = getStructureArrayLength1(i); + require(delegateGet(_testTarget, "getStructureArrayLength1(uint256)", bytes32(i)) == structuresLength); + for (uint256 j = 0; j < structuresLength; j++) { + require(delegateGet( + _testTarget, "getStructureArrayValue1(uint256,uint256)", bytes32(i), bytes32(j)) == + getStructureArrayValue1(i, j)); + } + } + + length = getStructureLength2(); + require(delegateGet(_testTarget, "getStructureLength2()") == length); + for (uint256 i = 0; i < length; i++) { + Structure2 memory structure2 = delegateGetStructure2(_testTarget, "mappingStructures(uint256)", bytes32(i)); + (uint256 structureValue, uint256 structureValueToCheck) = mappingStructures(i); + require(structureValue == structure2.value && structureValueToCheck == structure2.valueToCheck); + + uint256 structuresLength = getStructureArrayLength2(i); + require(delegateGet(_testTarget, "getStructureArrayLength2(uint256)", bytes32(i)) == structuresLength); + for (uint256 j = 0; j < structuresLength; j++) { + require(delegateGet( + _testTarget, "getStructureArrayValue2(uint256,uint256)", bytes32(i), bytes32(j)) == + getStructureArrayValue2(i, j)); + } + } + + require(delegateGet(_testTarget, "storageValueToCheck()") == storageValueToCheck()); + require(delegateGet(_testTarget, "anotherStorageValue()") == anotherStorageValue); + } + + function delegateGetStructure2(address _target, string memory _signature, bytes32 _argument) + internal returns (Structure2 memory result) + { + bytes32 memoryAddress = delegateGetData(_target, _signature, 1, _argument, 0); + assembly { + mstore(result, mload(memoryAddress)) + mstore(add(result, 64), mload(add(memoryAddress, 32))) + } + } + + function delegateGetBytes(address _target, string memory _signature) + internal returns (bytes memory result) + { + bytes32 memoryAddress = delegateGetData(_target, _signature, 0, 0, 0); + assembly { + // weird problems with copying the memory pointer: + // result := add(memoryAddress, mload(memoryAddress)) + + // quick fix for tests - copy the beginning of the array + let start := add(memoryAddress, mload(memoryAddress)) + mstore(result, mload(start)) + mstore(add(result, 32), mload(add(start, 32))) + } + } + + function finishUpgrade(address _target) public { + super.finishUpgrade(_target); + setStorageValueToCheck(ContractV4(_target).storageValueToCheck()); + } + +}