Test example of avoiding some compiler changes

pull/873/head
szotov 2019-04-07 15:02:24 +03:00
parent da3ce7b185
commit 509bc7ceec
5 changed files with 388 additions and 43 deletions

View File

@ -50,6 +50,7 @@ def test_dispatcher(testerchain):
contract1_lib, _ = testerchain.interface.deploy_contract('ContractV1', 1) contract1_lib, _ = testerchain.interface.deploy_contract('ContractV1', 1)
contract2_lib, _ = testerchain.interface.deploy_contract('ContractV2', 1) contract2_lib, _ = testerchain.interface.deploy_contract('ContractV2', 1)
contract3_lib, _ = testerchain.interface.deploy_contract('ContractV3', 2) 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') contract2_bad_storage_lib, _ = testerchain.interface.deploy_contract('ContractV2BadStorage')
dispatcher, _ = testerchain.interface.deploy_contract('Dispatcher', contract1_lib.address, secret_hash) dispatcher, _ = testerchain.interface.deploy_contract('Dispatcher', contract1_lib.address, secret_hash)
assert contract1_lib.address == dispatcher.functions.target().call() 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}) tx = dispatcher.functions.upgrade(contract3_lib.address, secret, secret2_hash).transact({'from': creator})
testerchain.wait_for_receipt(tx) testerchain.wait_for_receipt(tx)
contract_instance = testerchain.interface.w3.eth.contract( contract_instance = testerchain.interface.w3.eth.contract(
abi=contract2_lib.abi, abi=contract3_lib.abi,
address=dispatcher.address, address=dispatcher.address,
ContractFactoryClass=Contract) ContractFactoryClass=Contract)
assert contract3_lib.address == dispatcher.functions.target().call() 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 13 == contract_instance.functions.getStructureArrayValue2(0, 1).call()
assert 2 == contract_instance.functions.storageValueToCheck().call() assert 2 == contract_instance.functions.storageValueToCheck().call()
assert 'Hello' == contract_instance.functions.dynamicallySizedValue().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() events = upgrades.get_all_entries()
assert 4 == len(events) assert 4 == len(events)
@ -393,6 +397,78 @@ def test_dispatcher(testerchain):
assert 1 == len(events) assert 1 == len(events)
assert 33 == events[0]['args']['value'] 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 @pytest.mark.slow
def test_selfdestruct(testerchain): def test_selfdestruct(testerchain):

View File

@ -107,6 +107,8 @@ contract ContractV1 is Upgradeable {
super.verifyState(_testTarget); super.verifyState(_testTarget);
require(delegateGet(_testTarget, "storageValue()") == storageValue); require(delegateGet(_testTarget, "storageValue()") == storageValue);
bytes memory value = delegateGetBytes(_testTarget, "dynamicallySizedValue()"); 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 && require(value.length == bytes(dynamicallySizedValue).length &&
keccak256(value) == keccak256(bytes(dynamicallySizedValue))); keccak256(value) == keccak256(bytes(dynamicallySizedValue)));
@ -121,8 +123,8 @@ contract ContractV1 is Upgradeable {
require(delegateGet(_testTarget, "getStructureLength1()") == arrayStructures.length); require(delegateGet(_testTarget, "getStructureLength1()") == arrayStructures.length);
for (uint256 i = 0; i < arrayStructures.length; i++) { for (uint256 i = 0; i < arrayStructures.length; i++) {
Structure1 memory structure1 = delegateGetStructure1(_testTarget, "arrayStructures(uint256)", bytes32(i)); require(delegateGet(_testTarget, "arrayStructures(uint256)", bytes32(i)) == arrayStructures[i].value);
require(structure1.value == arrayStructures[i].value);
require(delegateGet(_testTarget, "getStructureArrayLength1(uint256)", bytes32(i)) == require(delegateGet(_testTarget, "getStructureArrayLength1(uint256)", bytes32(i)) ==
arrayStructures[i].arrayValues.length); arrayStructures[i].arrayValues.length);
for (uint256 j = 0; j < arrayStructures[i].arrayValues.length; j++) { for (uint256 j = 0; j < arrayStructures[i].arrayValues.length; j++) {
@ -134,8 +136,8 @@ contract ContractV1 is Upgradeable {
require(delegateGet(_testTarget, "getStructureLength2()") == mappingStructuresLength); require(delegateGet(_testTarget, "getStructureLength2()") == mappingStructuresLength);
for (uint256 i = 0; i < mappingStructuresLength; i++) { for (uint256 i = 0; i < mappingStructuresLength; i++) {
Structure2 memory structure2 = delegateGetStructure2(_testTarget, "mappingStructures(uint256)", bytes32(i)); require(delegateGet(_testTarget, "mappingStructures(uint256)", bytes32(i)) == mappingStructures[i].value);
require(structure2.value == mappingStructures[i].value);
require(delegateGet(_testTarget, "getStructureArrayLength2(uint256)", bytes32(i)) == require(delegateGet(_testTarget, "getStructureArrayLength2(uint256)", bytes32(i)) ==
mappingStructures[i].arrayValues.length); mappingStructures[i].arrayValues.length);
for (uint256 j = 0; j < mappingStructures[i].arrayValues.length; j++) { 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) function delegateGetBytes(address _target, string memory _signature)
internal returns (bytes memory result) internal returns (bytes memory result)
{ {

View File

@ -127,41 +127,31 @@ contract ContractV2 is Upgradeable {
mappingValues[index]); mappingValues[index]);
} }
require(uint256(delegateGet(_testTarget, "getStructureLength1()")) == arrayStructures.length); require(delegateGet(_testTarget, "getStructureLength1()") == arrayStructures.length);
for (uint256 i = 0; i < arrayStructures.length; i++) { for (uint256 i = 0; i < arrayStructures.length; i++) {
Structure1 memory structure1 = delegateGetStructure1(_testTarget, "arrayStructures(uint256)", bytes32(i)); require(delegateGet(_testTarget, "arrayStructures(uint256)", bytes32(i)) == arrayStructures[i].value);
require(structure1.value == 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); require(values.length == arrayStructures[i].arrayValues.length);
for (uint256 j = 0; j < arrayStructures[i].arrayValues.length; j++) { 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++) { for (uint256 i = 0; i < mappingStructuresLength; i++) {
Structure2 memory structure2 = delegateGetStructure2(_testTarget, "mappingStructures(uint256)", bytes32(i)); Structure2 memory structure2 = delegateGetStructure2(_testTarget, "mappingStructures(uint256)", bytes32(i));
require(structure2.value == mappingStructures[i].value); require(structure2.value == mappingStructures[i].value);
require(structure2.valueToCheck == mappingStructures[i].valueToCheck); 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); require(values.length == mappingStructures[i].arrayValues.length);
for (uint256 j = 0; j < mappingStructures[i].arrayValues.length; j++) { 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); require(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
}
} }
function delegateGetStructure2(address _target, string memory _signature, bytes32 _argument) function delegateGetStructure2(address _target, string memory _signature, bytes32 _argument)
@ -192,7 +182,7 @@ contract ContractV2 is Upgradeable {
string memory _signature, string memory _signature,
bytes32 _argument bytes32 _argument
) )
public returns (bytes32[] memory result) public returns (uint256[] memory result)
{ {
bytes32 memoryAddress = delegateGetData(_target, _signature, 1, _argument, 0); bytes32 memoryAddress = delegateGetData(_target, _signature, 1, _argument, 0);
assembly { assembly {

View File

@ -15,7 +15,7 @@ contract ContractV3 is ContractV2 {
} }
function setAnotherStorageValue(uint256 _value) public { function setAnotherStorageValue(uint256 _value) public {
anotherStorageValue = _value * 2; anotherStorageValue = _value;
} }
function verifyState(address _testTarget) public { function verifyState(address _testTarget) public {

View File

@ -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());
}
}