-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathScorchedEarth.sol
149 lines (124 loc) · 4.66 KB
/
ScorchedEarth.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
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.8;
pragma experimental ABIEncoderV2;
import '@statechannels/nitro-protocol/contracts/interfaces/ForceMoveApp.sol';
import '@statechannels/nitro-protocol/contracts/Outcome.sol';
import "@openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol";
contract ScorchedEarth is ForceMoveApp {
using SafeMath for uint256;
enum Phase {
Share,
React
}
enum Reaction {
None, // Not applicable in Share phase
Pay,
Burn
}
struct SEData {
uint256 payment;
uint256 suggesterBurn;
uint256 userBurn;
Phase phase;
Reaction reaction;
string suggestion;
}
constructor() public { }
function appData(bytes memory appDataBytes) internal pure returns (SEData memory) {
return abi.decode(appDataBytes, (SEData));
}
function validTransition(
VariablePart memory _fromPart,
VariablePart memory _toPart,
uint256, /* turnNumB */
uint256 /* nParticipants */
) public pure override returns (bool) {
Outcome.AllocationItem[] memory fromAllocation = extractAllocation(_fromPart);
Outcome.AllocationItem[] memory toAllocation = extractAllocation(_toPart);
requireDestinationsUnchanged(fromAllocation, toAllocation);
// decode ScorchedEarth specific data
SEData memory fromData = appData(_fromPart.appData);
SEData memory toData = appData(_toPart.appData);
requireInternalCoherence(fromData);
requireInternalCoherence(toData);
requireCoreParametersUnchanged(fromData, toData);
requirePhaseToggle(fromData, toData);
return true;
}
function extractAllocation(VariablePart memory _variablePart)
private
pure
returns (Outcome.AllocationItem[] memory)
{
Outcome.OutcomeItem[] memory outcome = abi.decode(_variablePart.outcome, (Outcome.OutcomeItem[]));
require(outcome.length == 1, 'ScorchedEarth: Only one asset allowed');
Outcome.AssetOutcome memory assetOutcome = abi.decode(
outcome[0].assetOutcomeBytes,
(Outcome.AssetOutcome)
);
require(
assetOutcome.assetOutcomeType == uint8(Outcome.AssetOutcomeType.Allocation),
'ScorchedEarth: AssetOutcomeType must be Allocation'
);
Outcome.AllocationItem[] memory allocation = abi.decode(
assetOutcome.allocationOrGuaranteeBytes,
(Outcome.AllocationItem[])
);
require(
allocation.length == 3,
'ScorchedEarth: Allocation length must equal number of participants + 1 (i.e. 3)'
);
return allocation;
}
function requireDestinationsUnchanged(
Outcome.AllocationItem[] memory _fromAllocation,
Outcome.AllocationItem[] memory _toAllocation
) private pure
{
require(
_toAllocation[0].destination == _fromAllocation[0].destination,
'ScorchedEarth: Destination participantA may not change'
);
require(
_toAllocation[1].destination == _fromAllocation[1].destination,
'ScorchedEarth: Destination participantB may not change'
);
require(
_toAllocation[2].destination == _fromAllocation[2].destination,
'ScorchedEarth: Destination burn beneficiary may not change'
);
}
function requireInternalCoherence(SEData memory _data) private pure {
if (_data.phase == Phase.Share) {
require(_data.reaction == Reaction.None,
'ScorchedEarth: Reaction not valid for Share phase');
} else if (_data.phase == Phase.React) {
require(_data.reaction != Reaction.None,
'ScorchedEarth: Reaction not valid for React phase');
require(bytes(_data.suggestion).length == 0,
'ScorchedEarth: Suggestion not valid for React phase');
} else {
require(false, 'ScorchedEarth: Invalid phase');
}
}
function requireCoreParametersUnchanged(
SEData memory _fromData,
SEData memory _toData
) private pure
{
require(
_fromData.payment == _toData.payment &&
_fromData.suggesterBurn == _toData.suggesterBurn &&
_fromData.userBurn == _toData.userBurn,
'ScorchedEarth: Core parameters my not change'
);
}
function requirePhaseToggle(
SEData memory fromData,
SEData memory toData
) private pure
{
require(fromData.phase != toData.phase,
'ScorchedEarth: Phase must toggle between rounds');
}
}