This repository has been archived by the owner on Dec 7, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 16
/
TierOps.sol
129 lines (123 loc) · 5.78 KB
/
TierOps.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
// SPDX-License-Identifier: CAL
pragma solidity =0.8.10;
import {State} from "../RainVM.sol";
import "../../tier/libraries/TierReport.sol";
import "../../tier/libraries/TierwiseCombine.sol";
/// @title TierOps
/// @notice RainVM opcode pack to operate on tier reports.
/// The opcodes all map to functions from `ITier` and associated libraries such
/// as `TierConstants`, `TierwiseCombine`, and `TierReport`. For each, the
/// order of consumed values on the stack corresponds to the order of arguments
/// to interface/library functions.
library TierOps {
/// Opcode to call `report` on an `ITier` contract.
uint256 private constant REPORT = 0;
/// Opcode to stack a report that has never been held for all tiers.
uint256 private constant NEVER = 1;
/// Opcode to stack a report that has always been held for all tiers.
uint256 private constant ALWAYS = 2;
/// Opcode to calculate the tierwise diff of two reports.
uint256 private constant SATURATING_DIFF = 3;
/// Opcode to update the blocks over a range of tiers for a report.
uint256 private constant UPDATE_BLOCKS_FOR_TIER_RANGE = 4;
/// Opcode to tierwise select the best block lte a reference block.
uint256 private constant SELECT_LTE = 5;
/// Number of provided opcodes for `TierOps`.
uint256 internal constant OPS_LENGTH = 6;
function applyOp(
bytes memory,
State memory state_,
uint256 opcode_,
uint256 operand_
) internal view {
unchecked {
require(opcode_ < OPS_LENGTH, "MAX_OPCODE");
uint256 baseIndex_;
// Stack the report returned by an `ITier` contract.
// Top two stack vals are used as `ITier` contract and address
// to check the report for.
if (opcode_ == REPORT) {
state_.stackIndex -= 2;
baseIndex_ = state_.stackIndex;
state_.stack[baseIndex_] = ITier(
address(uint160(state_.stack[baseIndex_]))
).report(address(uint160(state_.stack[baseIndex_ + 1])));
state_.stackIndex++;
}
// Stack a report that has never been held at any tier.
else if (opcode_ == NEVER) {
state_.stack[state_.stackIndex] = TierConstants.NEVER_REPORT;
state_.stackIndex++;
}
// Stack a report that has always been held at every tier.
else if (opcode_ == ALWAYS) {
state_.stack[state_.stackIndex] = TierConstants.ALWAYS;
state_.stackIndex++;
}
// Stack the tierwise saturating subtraction of two reports.
// If the older report is newer than newer report the result will
// be `0`, else a tierwise diff in blocks will be obtained.
// The older and newer report are taken from the stack.
else if (opcode_ == SATURATING_DIFF) {
state_.stackIndex -= 2;
baseIndex_ = state_.stackIndex;
uint256 newerReport_ = state_.stack[baseIndex_];
uint256 olderReport_ = state_.stack[baseIndex_ + 1];
state_.stack[baseIndex_] = TierwiseCombine.saturatingSub(
newerReport_,
olderReport_
);
state_.stackIndex++;
}
// Stacks a report with updated blocks over tier range.
// The start and end tier are taken from the low and high bits of
// the `operand_` respectively.
// The report to update and block number to update to are both
// taken from the stack.
else if (opcode_ == UPDATE_BLOCKS_FOR_TIER_RANGE) {
uint256 startTier_ = operand_ & 0x0f; // & 00001111
uint256 endTier_ = (operand_ >> 4) & 0x0f; // & 00001111
state_.stackIndex -= 2;
baseIndex_ = state_.stackIndex;
uint256 report_ = state_.stack[baseIndex_];
uint256 blockNumber_ = state_.stack[baseIndex_ + 1];
state_.stack[baseIndex_] = TierReport.updateBlocksForTierRange(
report_,
startTier_,
endTier_,
blockNumber_
);
state_.stackIndex++;
}
// Stacks the result of a `selectLte` combinator.
// All `selectLte` share the same stack and argument handling.
// Takes the `logic_` and `mode_` from the `operand_` high bits.
// `logic_` is the highest bit.
// `mode_` is the 2 highest bits after `logic_`.
// The other bits specify how many values to take from the stack
// as reports to compare against each other and the block number.
else if (opcode_ == SELECT_LTE) {
uint256 logic_ = operand_ >> 7;
uint256 mode_ = (operand_ >> 5) & 0x3; // & 00000011
uint256 reportsLength_ = operand_ & 0x1F; // & 00011111
// Need one more than reports length to include block number.
state_.stackIndex -= reportsLength_ + 1;
baseIndex_ = state_.stackIndex;
uint256 cursor_ = baseIndex_;
uint256[] memory reports_ = new uint256[](reportsLength_);
for (uint256 a_ = 0; a_ < reportsLength_; a_++) {
reports_[a_] = state_.stack[cursor_];
cursor_++;
}
uint256 blockNumber_ = state_.stack[cursor_];
state_.stack[baseIndex_] = TierwiseCombine.selectLte(
reports_,
blockNumber_,
logic_,
mode_
);
state_.stackIndex++;
}
}
}
}