Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: SWIP 21 #275

Merged
merged 43 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
db72550
add height to staking
0xCardinalError Sep 24, 2024
3d0f535
add height to redistribution contract and apply changes to proximity …
0xCardinalError Sep 24, 2024
56edaea
Fix all the staking tests
0xCardinalError Sep 27, 2024
287dd32
fix redis tests
0xCardinalError Sep 27, 2024
f0b0c98
fix stats
0xCardinalError Sep 27, 2024
d8988a0
add 2 new tests that use height 2 to see if commit and reveal still w…
0xCardinalError Oct 1, 2024
9fba1a2
add height to node effective stake calcualation
0xCardinalError Oct 9, 2024
1263581
remove comment
0xCardinalError Oct 9, 2024
0a545d4
change name of var and also make calc format different:
0xCardinalError Oct 9, 2024
4ca46bf
fix per PR
0xCardinalError Oct 10, 2024
b6b2e3d
Merge branch 'master' of https://github.com/ethersphere/storage-incen…
0xCardinalError Oct 10, 2024
144374f
add change of height test and that it is not failing
0xCardinalError Oct 10, 2024
dae9f0e
finalize tests
0xCardinalError Oct 10, 2024
16280a9
deployment of RC 0.9.2
0xCardinalError Oct 10, 2024
1f1f694
Merge branch 'master' of https://github.com/ethersphere/storage-incen…
0xCardinalError Oct 11, 2024
cdf9d19
add proper event value
0xCardinalError Oct 15, 2024
1bf3e2e
add default price for new postagestamp
0xCardinalError Oct 17, 2024
31d23c1
add new potential stake calculations
0xCardinalError Oct 24, 2024
c10479a
add minimum check
0xCardinalError Oct 24, 2024
d649602
add tests for height and amount checks
0xCardinalError Oct 24, 2024
beaa724
deployment with height calc changes
0xCardinalError Oct 24, 2024
9255242
add price adjustment status if it fails
0xCardinalError Oct 25, 2024
4637fbf
add price update checks
0xCardinalError Oct 25, 2024
1a9033f
add check on WD and that its not failing silently
0xCardinalError Oct 25, 2024
788a31f
order state changing func first
0xCardinalError Oct 25, 2024
1c942b9
comment change
0xCardinalError Oct 25, 2024
32a4bc7
normalize commited stake
0xCardinalError Oct 28, 2024
df15b21
adjust tests to new values
0xCardinalError Oct 28, 2024
e2dbfca
add latest deployment code with normalized commited stake
0xCardinalError Oct 28, 2024
3f53ca3
add for proper testnet deployed contracts
0xCardinalError Oct 28, 2024
2354486
add event for height in commit
0xCardinalError Oct 31, 2024
71c7776
add new redis
0xCardinalError Oct 31, 2024
e60d836
remove extra addy text
0xCardinalError Oct 31, 2024
e006ab2
add comment
0xCardinalError Nov 5, 2024
5c56b50
make a test for increased height and withdrawl
0xCardinalError Nov 6, 2024
e3d3976
test for WD when height changes
0xCardinalError Nov 6, 2024
32f9d4a
add testing for height changes
0xCardinalError Nov 7, 2024
025c379
change stake values only when amount is changed
0xCardinalError Nov 8, 2024
50ff8d2
add tests fixes
0xCardinalError Nov 8, 2024
9c96668
add latest deployment
0xCardinalError Nov 8, 2024
1bc80d4
add WD tests and results
0xCardinalError Nov 26, 2024
20bf3c0
write different scenarios for testing height and stake changes
0xCardinalError Nov 26, 2024
1d911a7
typo
0xCardinalError Dec 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions deploy/test/005_deploy_roles_postage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ const func: DeployFunction = async function ({ deployments, getNamedAccounts })
).address
);

// Set default price for postageStamp
const currentPrice = await read('PriceOracle', 'currentPrice');
await execute('PriceOracle', { from: deployer }, 'setPrice', currentPrice);

log('----------------------------------------------------');
};

Expand Down
87 changes: 62 additions & 25 deletions deployments/testnet/PriceOracle.json

Large diffs are not rendered by default.

183 changes: 117 additions & 66 deletions deployments/testnet/Redistribution.json

Large diffs are not rendered by default.

135 changes: 95 additions & 40 deletions deployments/testnet/StakeRegistry.json

Large diffs are not rendered by default.

118 changes: 118 additions & 0 deletions deployments/testnet/solcInputs/294e4684ec82046939090fa9e468f317.json

Large diffs are not rendered by default.

118 changes: 118 additions & 0 deletions deployments/testnet/solcInputs/99c9ef5f7ef53ea99ef6c816b26b1e5c.json

Large diffs are not rendered by default.

118 changes: 118 additions & 0 deletions deployments/testnet/solcInputs/be98d61f2910c0140b59702042017efa.json

Large diffs are not rendered by default.

44 changes: 22 additions & 22 deletions deployments/testnetlight/PostageStamp.json

Large diffs are not rendered by default.

89 changes: 63 additions & 26 deletions deployments/testnetlight/PriceOracle.json

Large diffs are not rendered by default.

190 changes: 110 additions & 80 deletions deployments/testnetlight/Redistribution.json

Large diffs are not rendered by default.

158 changes: 86 additions & 72 deletions deployments/testnetlight/StakeRegistry.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,15 +200,15 @@ const config: HardhatUserConfig = {
chainId: 11155111,
urls: {
apiURL: 'https://api-sepolia.etherscan.io/api',
browserURL: 'https://sepolia.etherscan.io/address/',
browserURL: 'https://sepolia.etherscan.io/',
},
},
{
network: 'testnetlight',
chainId: 11155111,
urls: {
apiURL: 'https://api-sepolia.etherscan.io/api',
browserURL: 'https://sepolia.etherscan.io/address/',
browserURL: 'https://sepolia.etherscan.io/',
},
},
{
Expand Down
30 changes: 24 additions & 6 deletions src/PriceOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ contract PriceOracle is AccessControl {
*@dev Emitted on every price update.
*/
event PriceUpdate(uint256 price);
event StampPriceUpdateFailed(uint256 attemptedPrice);

// ----------------------------- Custom Errors ------------------------------
error CallerNotAdmin(); // Caller is not the admin
Expand All @@ -72,7 +73,7 @@ contract PriceOracle is AccessControl {
* @notice Manually set the price.
* @dev Can only be called by the admin role.
* @param _price The new price.
*/ function setPrice(uint32 _price) external {
*/ function setPrice(uint32 _price) external returns (bool) {
if (!hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) {
revert CallerNotAdmin();
}
Expand All @@ -86,12 +87,19 @@ contract PriceOracle is AccessControl {
}
currentPriceUpScaled = _currentPriceUpScaled;

// Price in postagestamp is set at 256 so we need to upcast it
postageStamp.setPrice(uint256(currentPrice()));
// Check if the setting of price in postagestamp succeded
(bool success, ) = address(postageStamp).call(
abi.encodeWithSignature("setPrice(uint256)", uint256(currentPrice()))
);
if (!success) {
emit StampPriceUpdateFailed(currentPrice());
return false;
}
emit PriceUpdate(currentPrice());
return true;
}

function adjustPrice(uint16 redundancy) external {
function adjustPrice(uint16 redundancy) external returns (bool) {
if (isPaused == false) {
if (!hasRole(PRICE_UPDATER_ROLE, msg.sender)) {
revert CallerNotPriceUpdater();
Expand Down Expand Up @@ -119,7 +127,7 @@ contract PriceOracle is AccessControl {
uint64 _minimumPriceUpscaled = minimumPriceUpscaled;
uint32 _priceBase = priceBase;

// Set the number of rounds that were skipped
// Set the number of rounds that were skipped, we substract 1 as lastAdjustedRound is set below and default result is 1
uint64 skippedRounds = currentRoundNumber - lastAdjustedRound - 1;

// We first apply the increase/decrease rate for the current round
Expand All @@ -141,9 +149,19 @@ contract PriceOracle is AccessControl {

currentPriceUpScaled = _currentPriceUpScaled;
lastAdjustedRound = currentRoundNumber;
postageStamp.setPrice(uint256(currentPrice()));

// Check if the price set in postagestamp succeded
(bool success, ) = address(postageStamp).call(
abi.encodeWithSignature("setPrice(uint256)", uint256(currentPrice()))
);
if (!success) {
emit StampPriceUpdateFailed(currentPrice());
return false;
}
emit PriceUpdate(currentPrice());
return true;
}
return false;
}

function pause() external {
Expand Down
45 changes: 36 additions & 9 deletions src/Redistribution.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import "./Util/Signatures.sol";
import "./interface/IPostageStamp.sol";

interface IPriceOracle {
function adjustPrice(uint16 redundancy) external;
function adjustPrice(uint16 redundancy) external returns (bool);
}

interface IStakeRegistry {
Expand All @@ -24,6 +24,8 @@ interface IStakeRegistry {

function overlayOfAddress(address _owner) external view returns (bytes32);

function heightOfAddress(address _owner) external view returns (uint8);

function nodeEffectiveStake(address _owner) external view returns (uint256);
}

Expand Down Expand Up @@ -64,6 +66,7 @@ contract Redistribution is AccessControl, Pausable {
bytes32 overlay;
address owner;
bool revealed;
uint8 height;
uint256 stake;
bytes32 obfuscatedHash;
uint256 revealIndex;
Expand Down Expand Up @@ -181,7 +184,7 @@ contract Redistribution is AccessControl, Pausable {
/**
* @dev Logs that an overlay has committed
*/
event Committed(uint256 roundNumber, bytes32 overlay);
event Committed(uint256 roundNumber, bytes32 overlay, uint8 height);
/**
* @dev Emit from Postagestamp contract valid chunk count at the end of claim
*/
Expand All @@ -192,6 +195,16 @@ contract Redistribution is AccessControl, Pausable {
*/
event CurrentRevealAnchor(uint256 roundNumber, bytes32 anchor);

/**
* @dev Output external call status
*/
event PriceAdjustmentSkipped(uint16 redundancyCount);

/**
* @dev Withdraw not successful in claim
*/
event WithdrawFailed(address owner);

/**
* @dev Logs that an overlay has revealed
*/
Expand Down Expand Up @@ -281,6 +294,7 @@ contract Redistribution is AccessControl, Pausable {
bytes32 _overlay = Stakes.overlayOfAddress(msg.sender);
uint256 _stake = Stakes.nodeEffectiveStake(msg.sender);
uint256 _lastUpdate = Stakes.lastUpdatedBlockNumberOfAddress(msg.sender);
uint8 _height = Stakes.heightOfAddress(msg.sender);

if (!currentPhaseCommit()) {
revert NotCommitPhase();
Expand Down Expand Up @@ -329,13 +343,14 @@ contract Redistribution is AccessControl, Pausable {
overlay: _overlay,
owner: msg.sender,
revealed: false,
height: _height,
stake: _stake,
obfuscatedHash: _obfuscatedHash,
revealIndex: 0
})
);

emit Committed(_roundNumber, _overlay);
emit Committed(_roundNumber, _overlay, _height);
}

/**
Expand Down Expand Up @@ -372,9 +387,10 @@ contract Redistribution is AccessControl, Pausable {
bytes32 obfuscatedHash = wrapCommit(_overlay, _depth, _hash, _revealNonce);
uint256 id = findCommit(_overlay, obfuscatedHash);
Commit memory revealedCommit = currentCommits[id];
uint8 depthResponsibility = _depth - revealedCommit.height;
0xCardinalError marked this conversation as resolved.
Show resolved Hide resolved

// Check that commit is in proximity of the current anchor
if (!inProximity(revealedCommit.overlay, currentRevealRoundAnchor, _depth)) {
if (!inProximity(revealedCommit.overlay, currentRevealRoundAnchor, depthResponsibility)) {
revert OutOfDepthReveal(currentRevealRoundAnchor);
}
// Check that the commit has not already been revealed
Expand All @@ -391,7 +407,7 @@ contract Redistribution is AccessControl, Pausable {
owner: revealedCommit.owner,
depth: _depth,
stake: revealedCommit.stake,
stakeDensity: revealedCommit.stake * uint256(2 ** _depth),
stakeDensity: revealedCommit.stake * uint256(2 ** depthResponsibility),
hash: _hash
})
);
Expand All @@ -400,7 +416,7 @@ contract Redistribution is AccessControl, Pausable {
cr,
revealedCommit.overlay,
revealedCommit.stake,
revealedCommit.stake * uint256(2 ** _depth),
revealedCommit.stake * uint256(2 ** depthResponsibility),
_hash,
_depth
);
Expand Down Expand Up @@ -465,7 +481,14 @@ contract Redistribution is AccessControl, Pausable {

estimateSize(entryProofLast.proofSegments[0]);

PostageContract.withdraw(winnerSelected.owner);
// Do the check if the withdraw was success
(bool success, ) = address(PostageContract).call(
abi.encodeWithSignature("withdraw(address)", winnerSelected.owner)
);
if (!success) {
emit WithdrawFailed(winnerSelected.owner);
}

emit WinnerSelected(winnerSelected);
emit ChunkCount(PostageContract.validChunkCount());
}
Expand Down Expand Up @@ -549,7 +572,10 @@ contract Redistribution is AccessControl, Pausable {
}
}

OracleContract.adjustPrice(uint16(redundancyCount));
bool success = OracleContract.adjustPrice(uint16(redundancyCount));
if (!success) {
emit PriceAdjustmentSkipped(uint16(redundancyCount));
}
currentClaimRound = cr;
}

Expand Down Expand Up @@ -802,6 +828,7 @@ contract Redistribution is AccessControl, Pausable {
*/
function isParticipatingInUpcomingRound(address _owner, uint8 _depth) public view returns (bool) {
uint256 _lastUpdate = Stakes.lastUpdatedBlockNumberOfAddress(_owner);
uint8 _depthResponsibility = _depth - Stakes.heightOfAddress(_owner);
0xCardinalError marked this conversation as resolved.
Show resolved Hide resolved

if (currentPhaseReveal()) {
revert WrongPhase();
Expand All @@ -815,7 +842,7 @@ contract Redistribution is AccessControl, Pausable {
revert MustStake2Rounds();
}

return inProximity(Stakes.overlayOfAddress(_owner), currentRoundAnchor(), _depth);
return inProximity(Stakes.overlayOfAddress(_owner), currentRoundAnchor(), _depthResponsibility);
}

// ----------------------------- Reveal ------------------------------
Expand Down
63 changes: 48 additions & 15 deletions src/Staking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ contract StakeRegistry is AccessControl, Pausable {
uint256 potentialStake;
// Block height the stake was updated, also used as flag to check if the stake is set
uint256 lastUpdatedBlockNumber;
// Node indicating its increased reserve
uint8 height;
}

// Associate every stake id with node address data.
Expand All @@ -51,14 +53,15 @@ contract StakeRegistry is AccessControl, Pausable {
// ----------------------------- Events ------------------------------

/**
* @dev Emitted when a stake is created or updated by `owner` of the `overlay` by `committedStake`, and `potentialStake` during `lastUpdatedBlock`.
* @dev Emitted when a stake is created or updated by `owner` of the `overlay`.
*/
event StakeUpdated(
address indexed owner,
uint256 committedStake,
uint256 potentialStake,
bytes32 overlay,
uint256 lastUpdatedBlock
uint256 lastUpdatedBlock,
uint8 height
);

/**
Expand Down Expand Up @@ -112,33 +115,48 @@ contract StakeRegistry is AccessControl, Pausable {
* @dev At least `_initialBalancePerChunk*2^depth` number of tokens need to be preapproved for this contract.
* @param _setNonce Nonce that was used for overlay calculation.
* @param _addAmount Deposited amount of ERC20 tokens, equals to added Potential stake value
* @param _height increased reserve by registering the number of doublings
*/
function manageStake(bytes32 _setNonce, uint256 _addAmount) external whenNotPaused {
function manageStake(bytes32 _setNonce, uint256 _addAmount, uint8 _height) external whenNotPaused {
0xCardinalError marked this conversation as resolved.
Show resolved Hide resolved
bytes32 _previousOverlay = stakes[msg.sender].overlay;
uint256 _stakingSet = stakes[msg.sender].lastUpdatedBlockNumber;
bytes32 _newOverlay = keccak256(abi.encodePacked(msg.sender, reverse(NetworkId), _setNonce));
uint256 _addCommittedStake = _addAmount / OracleContract.currentPrice(); // losing some decimals from start 10n16 becomes 99999999999984000

// First time adding stake, check the minimum is added
if (_addAmount < MIN_STAKE && _stakingSet == 0) {
// First time adding stake, check the minimum is added, take into account height
if (_addAmount < MIN_STAKE * 2 ** _height && _stakingSet == 0) {
revert BelowMinimumStake();
}

if (_stakingSet != 0 && !addressNotFrozen(msg.sender)) revert Frozen();
uint256 updatedCommittedStake = stakes[msg.sender].committedStake + _addCommittedStake;
uint256 updatedPotentialStake = stakes[msg.sender].potentialStake + _addAmount;

uint256 updatedPotentialStake = stakes[msg.sender].potentialStake;
uint256 updatedCommittedStake = stakes[msg.sender].committedStake;

// Only update stake values if _addAmount is greater than 0
if (_addAmount > 0) {
updatedPotentialStake = stakes[msg.sender].potentialStake + _addAmount;
updatedCommittedStake = updatedPotentialStake / (OracleContract.currentPrice() * 2 ** _height);
}

stakes[msg.sender] = Stake({
overlay: _newOverlay,
committedStake: updatedCommittedStake,
potentialStake: updatedPotentialStake,
lastUpdatedBlockNumber: block.number
lastUpdatedBlockNumber: block.number,
height: _height
});

// Transfer tokens and emit event that stake has been updated
if (_addAmount > 0) {
if (!ERC20(bzzToken).transferFrom(msg.sender, address(this), _addAmount)) revert TransferFailed();
emit StakeUpdated(msg.sender, updatedCommittedStake, updatedPotentialStake, _newOverlay, block.number);
emit StakeUpdated(
msg.sender,
updatedCommittedStake,
updatedPotentialStake,
_newOverlay,
block.number,
_height
);
}

// Emit overlay change event
Expand All @@ -153,7 +171,7 @@ contract StakeRegistry is AccessControl, Pausable {
function withdrawFromStake() external {
uint256 _potentialStake = stakes[msg.sender].potentialStake;
uint256 _surplusStake = _potentialStake -
calculateEffectiveStake(stakes[msg.sender].committedStake, _potentialStake);
calculateEffectiveStake(stakes[msg.sender].committedStake, _potentialStake, stakes[msg.sender].height);

if (_surplusStake > 0) {
stakes[msg.sender].potentialStake -= _surplusStake;
Expand Down Expand Up @@ -250,7 +268,11 @@ contract StakeRegistry is AccessControl, Pausable {
function nodeEffectiveStake(address _owner) public view returns (uint256) {
return
addressNotFrozen(_owner)
? calculateEffectiveStake(stakes[_owner].committedStake, stakes[_owner].potentialStake)
? calculateEffectiveStake(
stakes[_owner].committedStake,
stakes[_owner].potentialStake,
stakes[_owner].height
)
: 0;
}

Expand All @@ -259,7 +281,9 @@ contract StakeRegistry is AccessControl, Pausable {
*/
function withdrawableStake() public view returns (uint256) {
uint256 _potentialStake = stakes[msg.sender].potentialStake;
return _potentialStake - calculateEffectiveStake(stakes[msg.sender].committedStake, _potentialStake);
return
_potentialStake -
calculateEffectiveStake(stakes[msg.sender].committedStake, _potentialStake, stakes[msg.sender].height);
}

/**
Expand All @@ -277,12 +301,21 @@ contract StakeRegistry is AccessControl, Pausable {
return stakes[_owner].overlay;
}

/**
* @dev Returns the currently height of the address.
* @param _owner address of node
*/
function heightOfAddress(address _owner) public view returns (uint8) {
return stakes[_owner].height;
}

function calculateEffectiveStake(
uint256 committedStake,
uint256 potentialStakeBalance
uint256 potentialStakeBalance,
uint8 height
) internal view returns (uint256) {
// Calculate the product of committedStake and unitPrice to get price in BZZ
uint256 committedStakeBzz = committedStake * OracleContract.currentPrice();
uint256 committedStakeBzz = (2 ** height) * committedStake * OracleContract.currentPrice();
0xCardinalError marked this conversation as resolved.
Show resolved Hide resolved

// Return the minimum value between committedStakeBzz and potentialStakeBalance
if (committedStakeBzz < potentialStakeBalance) {
Expand Down
Loading
Loading