Added events to verifyState and finishUpgrade

pull/873/head
szotov 2019-03-21 18:47:43 +03:00
parent 6ecec014e7
commit d95ba9345e
18 changed files with 174 additions and 30 deletions

View File

@ -173,7 +173,8 @@ contract Issuer is Upgradeable {
return totalSupply - Math.max(currentSupply1, currentSupply2); return totalSupply - Math.max(currentSupply1, currentSupply2);
} }
function verifyState(address _testTarget) public onlyWhileUpgrading { function verifyState(address _testTarget) public {
super.verifyState(_testTarget);
require(address(uint160(delegateGet(_testTarget, "token()"))) == address(token)); require(address(uint160(delegateGet(_testTarget, "token()"))) == address(token));
require(delegateGet(_testTarget, "miningCoefficient()") == miningCoefficient); require(delegateGet(_testTarget, "miningCoefficient()") == miningCoefficient);
require(delegateGet(_testTarget, "lockedPeriodsCoefficient()") == lockedPeriodsCoefficient); require(delegateGet(_testTarget, "lockedPeriodsCoefficient()") == lockedPeriodsCoefficient);
@ -185,7 +186,8 @@ contract Issuer is Upgradeable {
require(delegateGet(_testTarget, "totalSupply()") == totalSupply); require(delegateGet(_testTarget, "totalSupply()") == totalSupply);
} }
function finishUpgrade(address _target) public onlyWhileUpgrading { function finishUpgrade(address _target) public {
super.finishUpgrade(_target);
Issuer issuer = Issuer(_target); Issuer issuer = Issuer(_target);
token = issuer.token(); token = issuer.token();
miningCoefficient = issuer.miningCoefficient(); miningCoefficient = issuer.miningCoefficient();

View File

@ -1165,7 +1165,7 @@ contract MinersEscrow is Issuer {
} }
} }
function verifyState(address _testTarget) public onlyWhileUpgrading { function verifyState(address _testTarget) public {
super.verifyState(_testTarget); super.verifyState(_testTarget);
require(uint16(delegateGet(_testTarget, "minLockedPeriods()")) == minLockedPeriods); require(uint16(delegateGet(_testTarget, "minLockedPeriods()")) == minLockedPeriods);
require(delegateGet(_testTarget, "minAllowableLockedTokens()") == minAllowableLockedTokens); require(delegateGet(_testTarget, "minAllowableLockedTokens()") == minAllowableLockedTokens);
@ -1211,7 +1211,7 @@ contract MinersEscrow is Issuer {
} }
} }
function finishUpgrade(address _target) public onlyWhileUpgrading { function finishUpgrade(address _target) public {
super.finishUpgrade(_target); super.finishUpgrade(_target);
MinersEscrow escrow = MinersEscrow(_target); MinersEscrow escrow = MinersEscrow(_target);
minLockedPeriods = escrow.minLockedPeriods(); minLockedPeriods = escrow.minLockedPeriods();

View File

@ -160,7 +160,8 @@ contract MiningAdjudicator is Upgradeable {
} }
} }
function verifyState(address _testTarget) public onlyWhileUpgrading { function verifyState(address _testTarget) public {
super.verifyState(_testTarget);
require(address(delegateGet(_testTarget, "escrow()")) == address(escrow)); require(address(delegateGet(_testTarget, "escrow()")) == address(escrow));
require(SignatureVerifier.HashAlgorithm(uint256(delegateGet(_testTarget, "hashAlgorithm()"))) == hashAlgorithm); require(SignatureVerifier.HashAlgorithm(uint256(delegateGet(_testTarget, "hashAlgorithm()"))) == hashAlgorithm);
require(delegateGet(_testTarget, "basePenalty()") == basePenalty); require(delegateGet(_testTarget, "basePenalty()") == basePenalty);
@ -174,7 +175,8 @@ contract MiningAdjudicator is Upgradeable {
require(delegateGet(_testTarget, "evaluatedCFrags(bytes32)", evaluationCFragHash) != 0); require(delegateGet(_testTarget, "evaluatedCFrags(bytes32)", evaluationCFragHash) != 0);
} }
function finishUpgrade(address _target) public onlyWhileUpgrading { function finishUpgrade(address _target) public {
super.finishUpgrade(_target);
MiningAdjudicator targetContract = MiningAdjudicator(_target); MiningAdjudicator targetContract = MiningAdjudicator(_target);
escrow = targetContract.escrow(); escrow = targetContract.escrow();
hashAlgorithm = targetContract.hashAlgorithm(); hashAlgorithm = targetContract.hashAlgorithm();

View File

@ -499,7 +499,8 @@ contract PolicyManager is Upgradeable {
} }
} }
function verifyState(address _testTarget) public onlyWhileUpgrading { function verifyState(address _testTarget) public {
super.verifyState(_testTarget);
require(address(uint160(delegateGet(_testTarget, "escrow()"))) == address(escrow)); require(address(uint160(delegateGet(_testTarget, "escrow()"))) == address(escrow));
require(uint32(delegateGet(_testTarget, "secondsPerPeriod()")) == secondsPerPeriod); require(uint32(delegateGet(_testTarget, "secondsPerPeriod()")) == secondsPerPeriod);
Policy storage policy = policies[RESERVED_POLICY_ID]; Policy storage policy = policies[RESERVED_POLICY_ID];
@ -531,7 +532,8 @@ contract PolicyManager is Upgradeable {
bytes32(bytes20(RESERVED_NODE)), bytes32(uint256(11)))) == nodeInfo.rewardDelta[11]); bytes32(bytes20(RESERVED_NODE)), bytes32(uint256(11)))) == nodeInfo.rewardDelta[11]);
} }
function finishUpgrade(address _target) public onlyWhileUpgrading { function finishUpgrade(address _target) public {
super.finishUpgrade(_target);
PolicyManager policyManager = PolicyManager(_target); PolicyManager policyManager = PolicyManager(_target);
escrow = policyManager.escrow(); escrow = policyManager.escrow();
secondsPerPeriod = policyManager.secondsPerPeriod(); secondsPerPeriod = policyManager.secondsPerPeriod();

View File

@ -61,6 +61,7 @@ contract Dispatcher is Upgradeable {
require(address(uint160(delegateGet(_testTarget, "target()"))) == target); require(address(uint160(delegateGet(_testTarget, "target()"))) == target);
require(address(uint160(delegateGet(_testTarget, "previousTarget()"))) == previousTarget); require(address(uint160(delegateGet(_testTarget, "previousTarget()"))) == previousTarget);
require(bytes32(delegateGet(_testTarget, "secretHash()")) == secretHash); require(bytes32(delegateGet(_testTarget, "secretHash()")) == secretHash);
require(uint8(delegateGet(_testTarget, "isUpgrade()")) == isUpgrade);
} }
/** /**

View File

@ -12,6 +12,9 @@ import "zeppelin/ownership/Ownable.sol";
**/ **/
contract Upgradeable is Ownable { contract Upgradeable is Ownable {
event StateVerified(address indexed testTarget, address sender);
event UpgradeFinished(address indexed target, address sender);
/** /**
* @dev Contracts at the target must reserve the same location in storage for this address as in Dispatcher * @dev Contracts at the target must reserve the same location in storage for this address as in Dispatcher
* Stored data actually lives in the Dispatcher * Stored data actually lives in the Dispatcher
@ -32,7 +35,7 @@ contract Upgradeable is Ownable {
/** /**
* @dev Upgrade status. Explicit `uint8` type is used instead of `bool` to save gas by excluding 0 value * @dev Upgrade status. Explicit `uint8` type is used instead of `bool` to save gas by excluding 0 value
**/ **/
uint8 isUpgrade; uint8 public isUpgrade;
/** Constants for `isUpgrade` field **/ /** Constants for `isUpgrade` field **/
uint8 constant UPGRADE_FALSE = 1; uint8 constant UPGRADE_FALSE = 1;
@ -52,13 +55,17 @@ contract Upgradeable is Ownable {
* @dev Method for verifying storage state. * @dev Method for verifying storage state.
* Should check that new target contract returns right storage value * Should check that new target contract returns right storage value
**/ **/
function verifyState(address _testTarget) public /*onlyWhileUpgrading*/; function verifyState(address _testTarget) public onlyWhileUpgrading {
emit StateVerified(_testTarget, msg.sender);
}
/** /**
* @dev Copy values from the new target to the current storage * @dev Copy values from the new target to the current storage
* @param _target New target contract address * @param _target New target contract address
**/ **/
function finishUpgrade(address _target) public /*onlyWhileUpgrading*/; function finishUpgrade(address _target) public onlyWhileUpgrading {
emit UpgradeFinished(_target, msg.sender);
}
/** /**
* @dev Base method to get data * @dev Base method to get data

View File

@ -44,9 +44,11 @@ def test_dispatcher(testerchain):
contract3_lib, _ = testerchain.interface.deploy_contract('ContractV3', 2) contract3_lib, _ = testerchain.interface.deploy_contract('ContractV3', 2)
contract2_bad_lib, _ = testerchain.interface.deploy_contract('ContractV2Bad') contract2_bad_lib, _ = testerchain.interface.deploy_contract('ContractV2Bad')
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()
upgrades = dispatcher.events.Upgraded.createFilter(fromBlock=0) upgrades = dispatcher.events.Upgraded.createFilter(fromBlock=0)
assert contract1_lib.address == dispatcher.functions.target().call() state_verifications = dispatcher.events.StateVerified.createFilter(fromBlock=0)
upgrade_finishings = dispatcher.events.UpgradeFinished.createFilter(fromBlock=0)
events = upgrades.get_all_entries() events = upgrades.get_all_entries()
assert 1 == len(events) assert 1 == len(events)
@ -55,6 +57,12 @@ def test_dispatcher(testerchain):
assert contract1_lib.address == event_args['to'] assert contract1_lib.address == event_args['to']
assert creator == event_args['owner'] assert creator == event_args['owner']
events = upgrade_finishings.get_all_entries()
assert 1 == len(events)
event_args = events[0]['args']
assert contract1_lib.address == event_args['target']
assert creator == event_args['sender']
# Assign dispatcher address as contract. # Assign dispatcher address as contract.
# In addition to the interface can be used ContractV1, ContractV2 or ContractV3 ABI # In addition to the interface can be used ContractV1, ContractV2 or ContractV3 ABI
contract_instance = testerchain.interface.w3.eth.contract( contract_instance = testerchain.interface.w3.eth.contract(
@ -137,12 +145,23 @@ def test_dispatcher(testerchain):
events = upgrades.get_all_entries() events = upgrades.get_all_entries()
assert 2 == len(events) assert 2 == len(events)
event_args = events[1]['args'] event_args = events[1]['args']
assert contract1_lib.address == event_args['from'] assert contract1_lib.address == event_args['from']
assert contract2_lib.address == event_args['to'] assert contract2_lib.address == event_args['to']
assert creator == event_args['owner'] assert creator == event_args['owner']
events = state_verifications.get_all_entries()
assert 1 == len(events)
event_args = events[0]['args']
assert contract2_lib.address == event_args['testTarget']
assert creator == event_args['sender']
events = upgrade_finishings.get_all_entries()
assert 2 == len(events)
event_args = events[1]['args']
assert contract2_lib.address == event_args['target']
assert creator == event_args['sender']
# Check values and methods after upgrade # Check values and methods after upgrade
assert 20 == contract_instance.functions.returnValue().call() assert 20 == contract_instance.functions.returnValue().call()
assert 5 == contract_instance.functions.getStorageValue().call() assert 5 == contract_instance.functions.getStorageValue().call()
@ -237,6 +256,18 @@ def test_dispatcher(testerchain):
assert contract1_lib.address == event_args['to'] assert contract1_lib.address == event_args['to']
assert creator == event_args['owner'] assert creator == event_args['owner']
events = state_verifications.get_all_entries()
assert 2 == len(events)
event_args = events[1]['args']
assert contract2_lib.address == event_args['testTarget']
assert creator == event_args['sender']
events = upgrade_finishings.get_all_entries()
assert 3 == len(events)
event_args = events[2]['args']
assert contract1_lib.address == event_args['target']
assert creator == event_args['sender']
# Can't upgrade to the bad version # Can't upgrade to the bad version
with pytest.raises((TransactionFailed, ValueError)): with pytest.raises((TransactionFailed, ValueError)):
tx = dispatcher.functions.upgrade(contract2_bad_lib.address, secret3, secret_hash).transact({'from': creator}) tx = dispatcher.functions.upgrade(contract2_bad_lib.address, secret3, secret_hash).transact({'from': creator})
@ -286,8 +317,6 @@ def test_dispatcher(testerchain):
assert 2 == contract_instance.functions.storageValueToCheck().call() assert 2 == contract_instance.functions.storageValueToCheck().call()
assert 'Hello' == contract_instance.functions.getDynamicallySizedValue().call() assert 'Hello' == contract_instance.functions.getDynamicallySizedValue().call()
# bug? with duplicate entries
upgrades = dispatcher.events.Upgraded.createFilter(fromBlock=0)
events = upgrades.get_all_entries() events = upgrades.get_all_entries()
assert 4 == len(events) assert 4 == len(events)
event_args = events[2]['args'] event_args = events[2]['args']
@ -299,6 +328,24 @@ def test_dispatcher(testerchain):
assert contract3_lib.address == event_args['to'] assert contract3_lib.address == event_args['to']
assert creator == event_args['owner'] assert creator == event_args['owner']
events = state_verifications.get_all_entries()
assert 4 == len(events)
event_args = events[2]['args']
assert contract2_lib.address == event_args['testTarget']
assert creator == event_args['sender']
event_args = events[3]['args']
assert contract3_lib.address == event_args['testTarget']
assert creator == event_args['sender']
events = upgrade_finishings.get_all_entries()
assert 5 == len(events)
event_args = events[3]['args']
assert contract2_lib.address == event_args['target']
assert creator == event_args['sender']
event_args = events[4]['args']
assert contract3_lib.address == event_args['target']
assert creator == event_args['sender']
# Create and check events # Create and check events
tx = contract_instance.functions.createEvent(22).transact({'from': creator}) tx = contract_instance.functions.createEvent(22).transact({'from': creator})
testerchain.wait_for_receipt(tx) testerchain.wait_for_receipt(tx)

View File

@ -235,3 +235,24 @@ def test_upgrading(testerchain, token):
tx = dispatcher.functions.upgrade(contract_library_bad.address, secret, secret2_hash)\ tx = dispatcher.functions.upgrade(contract_library_bad.address, secret, secret2_hash)\
.transact({'from': creator}) .transact({'from': creator})
testerchain.wait_for_receipt(tx) testerchain.wait_for_receipt(tx)
events = dispatcher.events.StateVerified.createFilter(fromBlock=0).get_all_entries()
assert 2 == len(events)
event_args = events[0]['args']
assert contract_library_v2.address == event_args['testTarget']
assert creator == event_args['sender']
event_args = events[1]['args']
assert contract_library_v2.address == event_args['testTarget']
assert creator == event_args['sender']
events = dispatcher.events.UpgradeFinished.createFilter(fromBlock=0).get_all_entries()
assert 3 == len(events)
event_args = events[0]['args']
assert contract_library_v1.address == event_args['target']
assert creator == event_args['sender']
event_args = events[1]['args']
assert contract_library_v2.address == event_args['target']
assert creator == event_args['sender']
event_args = events[2]['args']
assert contract_library_v1.address == event_args['target']
assert creator == event_args['sender']

View File

@ -67,9 +67,6 @@ contract IssuerBad is Upgradeable {
// uint256 public currentSupply1; // uint256 public currentSupply1;
uint256 public currentSupply2; uint256 public currentSupply2;
function verifyState(address) public {}
function finishUpgrade(address) public {}
} }
@ -102,7 +99,7 @@ contract IssuerV2Mock is Issuer {
valueToCheck = _valueToCheck; valueToCheck = _valueToCheck;
} }
function verifyState(address _testTarget) public onlyWhileUpgrading { function verifyState(address _testTarget) public {
super.verifyState(_testTarget); super.verifyState(_testTarget);
require(delegateGet(_testTarget, "valueToCheck()") == valueToCheck); require(delegateGet(_testTarget, "valueToCheck()") == valueToCheck);
} }

View File

@ -78,7 +78,7 @@ contract MinersEscrowV2Mock is MinersEscrow {
valueToCheck = _valueToCheck; valueToCheck = _valueToCheck;
} }
function verifyState(address _testTarget) public onlyWhileUpgrading { function verifyState(address _testTarget) public {
super.verifyState(_testTarget); super.verifyState(_testTarget);
require(delegateGet(_testTarget, "valueToCheck()") == valueToCheck); require(delegateGet(_testTarget, "valueToCheck()") == valueToCheck);
} }
@ -86,6 +86,7 @@ contract MinersEscrowV2Mock is MinersEscrow {
function finishUpgrade(address _target) public onlyWhileUpgrading { function finishUpgrade(address _target) public onlyWhileUpgrading {
MinersEscrowV2Mock escrow = MinersEscrowV2Mock(_target); MinersEscrowV2Mock escrow = MinersEscrowV2Mock(_target);
valueToCheck = escrow.valueToCheck(); valueToCheck = escrow.valueToCheck();
emit UpgradeFinished(_target, msg.sender);
} }
} }

View File

@ -57,9 +57,6 @@ contract MiningAdjudicatorBad is Upgradeable {
mapping (bytes32 => bool) public evaluatedCFrags; mapping (bytes32 => bool) public evaluatedCFrags;
mapping (address => uint256) public penaltyHistory; mapping (address => uint256) public penaltyHistory;
function verifyState(address) public {}
function finishUpgrade(address) public {}
} }
@ -94,7 +91,7 @@ contract MiningAdjudicatorV2Mock is MiningAdjudicator {
valueToCheck = _valueToCheck; valueToCheck = _valueToCheck;
} }
function verifyState(address _testTarget) public onlyWhileUpgrading { function verifyState(address _testTarget) public {
super.verifyState(_testTarget); super.verifyState(_testTarget);
require(uint256(delegateGet(_testTarget, "valueToCheck()")) == valueToCheck); require(uint256(delegateGet(_testTarget, "valueToCheck()")) == valueToCheck);
} }

View File

@ -34,7 +34,7 @@ contract PolicyManagerV2Mock is PolicyManager {
valueToCheck = _valueToCheck; valueToCheck = _valueToCheck;
} }
function verifyState(address _testTarget) public onlyWhileUpgrading { function verifyState(address _testTarget) public {
super.verifyState(_testTarget); super.verifyState(_testTarget);
require(delegateGet(_testTarget, "valueToCheck()") == valueToCheck); require(delegateGet(_testTarget, "valueToCheck()") == valueToCheck);
} }

View File

@ -124,7 +124,8 @@ contract ContractV1 is ContractInterface, Upgradeable {
return mappingStructures[_index].arrayValues[_arrayIndex]; return mappingStructures[_index].arrayValues[_arrayIndex];
} }
function verifyState(address _testTarget) public onlyWhileUpgrading { function verifyState(address _testTarget) public {
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()");
require(value.length == bytes(dynamicallySizedValue).length && require(value.length == bytes(dynamicallySizedValue).length &&
@ -193,7 +194,8 @@ contract ContractV1 is ContractInterface, Upgradeable {
} }
} }
function finishUpgrade(address _target) public onlyWhileUpgrading { function finishUpgrade(address _target) public {
super.finishUpgrade(_target);
storageValue = ContractV1(_target).storageValue(); storageValue = ContractV1(_target).storageValue();
} }

View File

@ -133,7 +133,8 @@ contract ContractV2 is ContractInterface, Upgradeable {
return mappingStructures[_index].arrayValues; return mappingStructures[_index].arrayValues;
} }
function verifyState(address _testTarget) public onlyWhileUpgrading { function verifyState(address _testTarget) public {
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()");
require(value.length == bytes(dynamicallySizedValue).length && require(value.length == bytes(dynamicallySizedValue).length &&
@ -222,7 +223,8 @@ contract ContractV2 is ContractInterface, Upgradeable {
} }
} }
function finishUpgrade(address _target) public onlyWhileUpgrading { function finishUpgrade(address _target) public {
super.finishUpgrade(_target);
storageValueToCheck = ContractV2(_target).storageValueToCheck(); storageValueToCheck = ContractV2(_target).storageValueToCheck();
} }

View File

@ -18,7 +18,7 @@ contract ContractV3 is ContractV2 {
anotherStorageValue = _value * 2; anotherStorageValue = _value * 2;
} }
function verifyState(address _testTarget) public onlyWhileUpgrading { function verifyState(address _testTarget) public {
super.verifyState(_testTarget); super.verifyState(_testTarget);
require(delegateGet(_testTarget, "anotherStorageValue()") == anotherStorageValue); require(delegateGet(_testTarget, "anotherStorageValue()") == anotherStorageValue);
} }

View File

@ -150,6 +150,27 @@ def test_upgrading(testerchain, token):
.transact({'from': creator}) .transact({'from': creator})
testerchain.wait_for_receipt(tx) testerchain.wait_for_receipt(tx)
events = dispatcher.events.StateVerified.createFilter(fromBlock=0).get_all_entries()
assert 2 == len(events)
event_args = events[0]['args']
assert contract_library_v2.address == event_args['testTarget']
assert creator == event_args['sender']
event_args = events[1]['args']
assert contract_library_v2.address == event_args['testTarget']
assert creator == event_args['sender']
events = dispatcher.events.UpgradeFinished.createFilter(fromBlock=0).get_all_entries()
assert 3 == len(events)
event_args = events[0]['args']
assert contract_library_v1.address == event_args['target']
assert creator == event_args['sender']
event_args = events[1]['args']
assert contract_library_v2.address == event_args['target']
assert creator == event_args['sender']
event_args = events[2]['args']
assert contract_library_v1.address == event_args['target']
assert creator == event_args['sender']
@pytest.mark.slow @pytest.mark.slow
def test_re_stake(testerchain, token, escrow_contract): def test_re_stake(testerchain, token, escrow_contract):

View File

@ -560,3 +560,24 @@ def test_upgrading(testerchain):
tx = dispatcher.functions.upgrade(contract_library_bad.address, secret, secret2_hash) \ tx = dispatcher.functions.upgrade(contract_library_bad.address, secret, secret2_hash) \
.transact({'from': creator}) .transact({'from': creator})
testerchain.wait_for_receipt(tx) testerchain.wait_for_receipt(tx)
events = dispatcher.events.StateVerified.createFilter(fromBlock=0).get_all_entries()
assert 2 == len(events)
event_args = events[0]['args']
assert contract_library_v2.address == event_args['testTarget']
assert creator == event_args['sender']
event_args = events[1]['args']
assert contract_library_v2.address == event_args['testTarget']
assert creator == event_args['sender']
events = dispatcher.events.UpgradeFinished.createFilter(fromBlock=0).get_all_entries()
assert 3 == len(events)
event_args = events[0]['args']
assert contract_library_v1.address == event_args['target']
assert creator == event_args['sender']
event_args = events[1]['args']
assert contract_library_v2.address == event_args['target']
assert creator == event_args['sender']
event_args = events[2]['args']
assert contract_library_v1.address == event_args['target']
assert creator == event_args['sender']

View File

@ -404,3 +404,24 @@ def test_upgrading(testerchain):
tx = dispatcher.functions.upgrade(contract_library_bad.address, secret, secret2_hash)\ tx = dispatcher.functions.upgrade(contract_library_bad.address, secret, secret2_hash)\
.transact({'from': creator}) .transact({'from': creator})
testerchain.wait_for_receipt(tx) testerchain.wait_for_receipt(tx)
events = dispatcher.events.StateVerified.createFilter(fromBlock=0).get_all_entries()
assert 2 == len(events)
event_args = events[0]['args']
assert contract_library_v2.address == event_args['testTarget']
assert creator == event_args['sender']
event_args = events[1]['args']
assert contract_library_v2.address == event_args['testTarget']
assert creator == event_args['sender']
events = dispatcher.events.UpgradeFinished.createFilter(fromBlock=0).get_all_entries()
assert 3 == len(events)
event_args = events[0]['args']
assert contract_library_v1.address == event_args['target']
assert creator == event_args['sender']
event_args = events[1]['args']
assert contract_library_v2.address == event_args['target']
assert creator == event_args['sender']
event_args = events[2]['args']
assert contract_library_v1.address == event_args['target']
assert creator == event_args['sender']