The BondingManager.isActiveTranscoder() is used within the BondingManager to determine if an address is an active transcoder during a round.
In V1, the implementation of the function was:
function isActiveTranscoder(address _transcoder, uint256 _round) public view returns (bool) {
activeTranscoderSet[_round].isActive[_transcoder];
}
In Streamflow, the function signature is modified to drop the _round argument and the implementation is updated to:
function isActiveTranscoder(address _transcoder) public view returns (bool) {
Transcoder storage t = transcoders[_transcoder];
uint256 currentRound = roundsManager().currentRound();
return t.activationRound <= currentRound && currentRound < t.deactivationRound;
}
The Streamflow implementation of isActiveTranscoder() relies on the fact that in round N, if a transcoder is added to transcoderPool, then the transcoder's activationRound will be set to currentRound + 1 and the transcoder's deactivationRound will be set to MAX_FUTURE_ROUND. This is a problem during the transition from V1 to Streamflow after the BondingManager is upgraded for a number of reasons:
Existing active set members will be in transcoderPool, but they will not have non-zero values for activationRound and deactivationRound (since those fields did not exist before the upgrade!)
Existing active set members will have no way to set activationRound and deactivationRound because those values are only set if an address is not in transcoderPool
Any changes to the stake of an existing active set member will update the transcoder's position in transcoderPool, update the total active stake for the next round, update the transcoder's earnings pool stake for the next round and update the transcoder's last active stake update round. However, the total active stake for the next round will not be accurate because it only increases by the additional amount that the transcoder's stake increased by and it never incorporates the rest of the total stake of the transcoder. Additionally, while the transcoder will contribute to the total active stake it will not be able to call reward().
Proposals
Proposal 1
We can define an additional function activate() (along with its counterpart that accepts a hint for the list insertion position activateWithHint(). Anyone can call this function and specify an address to try to activate. The address must be a registered transcoder.
Below is a rough sketch of the function implementation for activateWithHint():
function activateWithHint(address _transcoder, address _newPosPrev, address _newPosNext) external {
require(isRegisteredTranscoder(_transcoder));
Transcoder storage t = transcoders[_transcoder];
uint256 totalStake = transcoderTotalStake(_transcoder);
uint256 activationRound = roundsManager().currentRound().add(1);
if (!transcoderPool.contains(_transcoder) {
// If _transcoder is not in the pool then we try to add it to the pool the normal way
// i.e. Check if pool is full, try to evict last transcoder and then add the new transcoder
tryToJoinActiveSet(_transcoder, totalStake, activationRound, _newPosPrev, _newPosNext);
} else if (t.activationRound == 0 && t.deactivationRound == 0) {
// If _transcoder is in the pool AND it has zero values for activationRound and deactivationRound then
// it must have been in the pool before the upgrade. So, instead of trying to add it to the
// the pool let's just update its activation related fields
// Set activationRound, deactivationRound, earnings pool stake, next round total active stake, etc.
activateTranscoder(_transcoder, totalStake, activationRound);
} else {
revert("transcoder cannot be activated");
}
}
We allow anyone to call this function so that the transcoder itself or anyone else (including other accounts owned by the transcoder) can call it to activate the transcoder for the next round either in the post BondingManager upgrade scenario mentioned above or in the scenario where someone exits the pool and its current stake (without any additional bonding) is sufficient to allow it to join the pool. So, after the BondingManager upgrade, a single call to this function by anyone will activate transcoder and correct the state update problems mentioned at the beginning of this post.
We'll also need to update the increaseTotalStake() such that if a transcoder is in transcoderPool, but it has zero values for activationRound and deactivationRound instead of doing:
if (transcoderPool.contains(_delegate) {
transcoderPool.updateKey();
nextRoundTotalActiveStake = nextRoundTotalActiveStake.add(_amount);
// Other operations
}
we can do
uint256 currStake = transcoderTotalStake(_delegate);
if (transcoderPool.contains(_delegate) {
uint256 stakeToBeActivated = _amount;
if (t.activationRound == 0 && t.deactivationRound == 0 {
// Existing stake never added to next round's total active stake so let's do that now
stakeToBeActivated = stakeToBeActivated.add(currStake);
}
transcoderPool.updateKey();
nextRoundTotalActiveStake = nextRoundTotalActiveStake.add(stakeToBeActivated);
}
We would do something similar for decreaseTotalStake().
Proposal 2
We can deprecate the transcoderPool state variable and declare an additional transcoderPoolStreamflow state variable.
All staking operations would point to the new transcoderPoolStreamflow state variable. A consequence of this is that after the upgrade, the transcoder pool would be empty and all transcoders would need to add themselves back to the pool via bond() or transcoder().
I'm currently leaning toward proposal 2 as that is the simpler approach.
Background
The
BondingManager.isActiveTranscoder()
is used within the BondingManager to determine if an address is an active transcoder during a round.In V1, the implementation of the function was:
In Streamflow, the function signature is modified to drop the
_round
argument and the implementation is updated to:The Streamflow implementation of
isActiveTranscoder()
relies on the fact that in round N, if a transcoder is added totranscoderPool
, then the transcoder'sactivationRound
will be set tocurrentRound + 1
and the transcoder'sdeactivationRound
will be set toMAX_FUTURE_ROUND
. This is a problem during the transition from V1 to Streamflow after the BondingManager is upgraded for a number of reasons:transcoderPool
, but they will not have non-zero values foractivationRound
anddeactivationRound
(since those fields did not exist before the upgrade!)activationRound
anddeactivationRound
because those values are only set if an address is not intranscoderPool
transcoderPool
, update the total active stake for the next round, update the transcoder's earnings pool stake for the next round and update the transcoder's last active stake update round. However, the total active stake for the next round will not be accurate because it only increases by the additional amount that the transcoder's stake increased by and it never incorporates the rest of the total stake of the transcoder. Additionally, while the transcoder will contribute to the total active stake it will not be able to callreward()
.Proposals
Proposal 1
We can define an additional function
activate()
(along with its counterpart that accepts a hint for the list insertion positionactivateWithHint()
. Anyone can call this function and specify an address to try to activate. The address must be a registered transcoder.Below is a rough sketch of the function implementation for
activateWithHint()
:We allow anyone to call this function so that the transcoder itself or anyone else (including other accounts owned by the transcoder) can call it to activate the transcoder for the next round either in the post BondingManager upgrade scenario mentioned above or in the scenario where someone exits the pool and its current stake (without any additional bonding) is sufficient to allow it to join the pool. So, after the BondingManager upgrade, a single call to this function by anyone will activate transcoder and correct the state update problems mentioned at the beginning of this post.
We'll also need to update the
increaseTotalStake()
such that if a transcoder is intranscoderPool
, but it has zero values foractivationRound
anddeactivationRound
instead of doing:we can do
We would do something similar for
decreaseTotalStake()
.Proposal 2
We can deprecate the
transcoderPool
state variable and declare an additionaltranscoderPoolStreamflow
state variable.All staking operations would point to the new
transcoderPoolStreamflow
state variable. A consequence of this is that after the upgrade, the transcoder pool would be empty and all transcoders would need to add themselves back to the pool viabond()
ortranscoder()
.I'm currently leaning toward proposal 2 as that is the simpler approach.