-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathNullSettlementDisputeByPayment.sol
231 lines (203 loc) · 10.2 KB
/
NullSettlementDisputeByPayment.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
/*
* Hubii Nahmii
*
* Compliant with the Hubii Nahmii specification v0.12.
*
* Copyright (C) 2017-2019 Hubii AS
*/
pragma solidity >=0.4.25 <0.6.0;
pragma experimental ABIEncoderV2;
import {Ownable} from "./Ownable.sol";
import {Configurable} from "./Configurable.sol";
import {Validatable} from "./Validatable.sol";
import {SecurityBondable} from "./SecurityBondable.sol";
import {WalletLockable} from "./WalletLockable.sol";
import {BalanceTrackable} from "./BalanceTrackable.sol";
import {FraudChallengable} from "./FraudChallengable.sol";
import {Servable} from "./Servable.sol";
import {SafeMathIntLib} from "./SafeMathIntLib.sol";
import {SafeMathUintLib} from "./SafeMathUintLib.sol";
import {MonetaryTypesLib} from "./MonetaryTypesLib.sol";
import {PaymentTypesLib} from "./PaymentTypesLib.sol";
import {SettlementChallengeTypesLib} from "./SettlementChallengeTypesLib.sol";
import {NullSettlementChallengeState} from "./NullSettlementChallengeState.sol";
import {BalanceTracker} from "./BalanceTracker.sol";
import {BalanceTrackerLib} from "./BalanceTrackerLib.sol";
/**
* @title NullSettlementDisputeByPayment
* @notice The where payment related disputes of null settlement challenge happens
*/
contract NullSettlementDisputeByPayment is Ownable, Configurable, Validatable, SecurityBondable, WalletLockable,
BalanceTrackable, FraudChallengable, Servable {
using SafeMathIntLib for int256;
using SafeMathUintLib for uint256;
using BalanceTrackerLib for BalanceTracker;
//
// Constants
// -----------------------------------------------------------------------------------------------------------------
string constant public CHALLENGE_BY_PAYMENT_ACTION = "challenge_by_payment";
//
// Variables
// -----------------------------------------------------------------------------------------------------------------
NullSettlementChallengeState public nullSettlementChallengeState;
//
// Events
// -----------------------------------------------------------------------------------------------------------------
event SetNullSettlementChallengeStateEvent(NullSettlementChallengeState oldNullSettlementChallengeState,
NullSettlementChallengeState newNullSettlementChallengeState);
event ChallengeByPaymentEvent(address wallet, uint256 nonce, PaymentTypesLib.Payment payment,
address challenger);
//
// Constructor
// -----------------------------------------------------------------------------------------------------------------
constructor(address deployer) Ownable(deployer) public {
}
/// @notice Set the settlement challenge state contract
/// @param newNullSettlementChallengeState The (address of) NullSettlementChallengeState contract instance
function setNullSettlementChallengeState(NullSettlementChallengeState newNullSettlementChallengeState) public
onlyDeployer
notNullAddress(address(newNullSettlementChallengeState))
{
NullSettlementChallengeState oldNullSettlementChallengeState = nullSettlementChallengeState;
nullSettlementChallengeState = newNullSettlementChallengeState;
emit SetNullSettlementChallengeStateEvent(oldNullSettlementChallengeState, nullSettlementChallengeState);
}
/// @notice Challenge the settlement by providing payment candidate
/// @dev This challenges the payment sender's side of things
/// @param wallet The wallet whose settlement is being challenged
/// @param payment The payment candidate that challenges
/// @param challenger The address of the challenger
function challengeByPayment(address wallet, PaymentTypesLib.Payment memory payment, address challenger)
public
onlyEnabledServiceAction(CHALLENGE_BY_PAYMENT_ACTION)
onlySealedPayment(payment)
onlyPaymentSender(payment, wallet)
{
// Require that payment candidate is not labelled fraudulent
require(!fraudChallenge.isFraudulentPaymentHash(payment.seals.operator.hash), "Payment deemed fraudulent [NullSettlementDisputeByPayment.sol:86]");
// Require that proposal has been initiated
require(nullSettlementChallengeState.hasProposal(wallet, payment.currency), "No proposal found [NullSettlementDisputeByPayment.sol:89]");
// Require that proposal has not expired
require(!nullSettlementChallengeState.hasProposalExpired(wallet, payment.currency), "Proposal found expired [NullSettlementDisputeByPayment.sol:92]");
// Require that payment party's nonce is strictly greater than proposal's nonce and its current
// disqualification nonce
require(payment.sender.nonce > nullSettlementChallengeState.proposalNonce(
wallet, payment.currency
), "Payment nonce not strictly greater than proposal nonce [NullSettlementDisputeByPayment.sol:96]");
require(payment.sender.nonce > nullSettlementChallengeState.proposalDisqualificationNonce(
wallet, payment.currency
), "Payment nonce not strictly greater than proposal disqualification nonce [NullSettlementDisputeByPayment.sol:99]");
// Require overrun for this payment to be a valid challenge candidate
require(_overrun(wallet, payment), "No overrun found [NullSettlementDisputeByPayment.sol:104]");
// Reward challenger
_settleRewards(wallet, payment.sender.balances.current, payment.currency, challenger);
// Disqualify proposal, effectively overriding any previous disqualification
nullSettlementChallengeState.disqualifyProposal(
wallet, payment.currency, challenger, payment.blockNumber,
payment.sender.nonce, payment.seals.operator.hash, PaymentTypesLib.PAYMENT_KIND()
);
// Emit event
emit ChallengeByPaymentEvent(
wallet, nullSettlementChallengeState.proposalNonce(wallet, payment.currency), payment, challenger
);
}
//
// Private functions
// -----------------------------------------------------------------------------------------------------------------
function _overrun(address wallet, PaymentTypesLib.Payment memory payment)
private
view
returns (bool)
{
// Get the target balance amount from the proposal
int targetBalanceAmount = nullSettlementChallengeState.proposalTargetBalanceAmount(
wallet, payment.currency
);
// Get the change in active balance since the start of the challenge
int256 deltaBalanceSinceStart = balanceTracker.fungibleActiveBalanceAmount(
wallet, payment.currency
).sub(
balanceTracker.fungibleActiveBalanceAmountByBlockNumber(
wallet, payment.currency,
nullSettlementChallengeState.proposalReferenceBlockNumber(wallet, payment.currency)
)
);
// Get the cumulative transfer of the payment
int256 cumulativeTransfer = balanceTracker.fungibleActiveBalanceAmountByBlockNumber(
wallet, payment.currency, payment.blockNumber
).sub(payment.sender.balances.current);
return targetBalanceAmount.add(deltaBalanceSinceStart) < cumulativeTransfer;
}
// Lock wallet's balances or reward challenger by stake fraction
function _settleRewards(address wallet, int256 walletAmount, MonetaryTypesLib.Currency memory currency,
address challenger)
private
{
if (nullSettlementChallengeState.proposalWalletInitiated(wallet, currency))
_settleBalanceReward(wallet, walletAmount, currency, challenger);
else
_settleSecurityBondReward(wallet, walletAmount, currency, challenger);
}
function _settleBalanceReward(address wallet, int256 walletAmount, MonetaryTypesLib.Currency memory currency,
address challenger)
private
{
// Unlock wallet/currency for existing challenger if previously locked
if (SettlementChallengeTypesLib.Status.Disqualified == nullSettlementChallengeState.proposalStatus(
wallet, currency
))
walletLocker.unlockFungibleByProxy(
wallet,
nullSettlementChallengeState.proposalDisqualificationChallenger(
wallet, currency
),
currency.ct, currency.id
);
// Lock wallet for new challenger
walletLocker.lockFungibleByProxy(
wallet, challenger, walletAmount, currency.ct, currency.id, configuration.settlementChallengeTimeout()
);
}
// Settle the two-component reward from security bond.
// The first component is flat figure as obtained from Configuration
// The second component is progressive and calculated as
// min(walletAmount, fraction of SecurityBond's deposited balance)
// both amounts for the given currency
function _settleSecurityBondReward(address wallet, int256 walletAmount, MonetaryTypesLib.Currency memory currency,
address challenger)
private
{
// Deprive existing challenger of reward if previously locked
if (SettlementChallengeTypesLib.Status.Disqualified == nullSettlementChallengeState.proposalStatus(
wallet, currency
))
securityBond.depriveAbsolute(
nullSettlementChallengeState.proposalDisqualificationChallenger(
wallet, currency
),
currency.ct, currency.id
);
// Reward the flat component
MonetaryTypesLib.Figure memory flatReward = _flatReward();
securityBond.rewardAbsolute(
challenger, flatReward.amount, flatReward.currency.ct, flatReward.currency.id, 0
);
// Reward the progressive component
int256 progressiveRewardAmount = walletAmount.clampMax(
securityBond.depositedFractionalBalance(
currency.ct, currency.id, configuration.operatorSettlementStakeFraction()
)
);
securityBond.rewardAbsolute(
challenger, progressiveRewardAmount, currency.ct, currency.id, 0
);
}
function _flatReward()
private
view
returns (MonetaryTypesLib.Figure memory)
{
(int256 amount, address currencyCt, uint256 currencyId) = configuration.operatorSettlementStake();
return MonetaryTypesLib.Figure(amount, MonetaryTypesLib.Currency(currencyCt, currencyId));
}
}