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

Lock invitee reward till age 10 #1133

Merged
merged 1 commit into from
Jul 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 api/dna_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func (api *DnaApi) GetCoinbaseAddr() common.Address {
type Balance struct {
Stake decimal.Decimal `json:"stake"`
ReplenishedStake decimal.Decimal `json:"replenishedStake"`
LockedStake decimal.Decimal `json:"lockedStake"`
Balance decimal.Decimal `json:"balance"`
Nonce uint32 `json:"nonce"`
MempoolNonce uint32 `json:"mempoolNonce"`
Expand All @@ -70,6 +71,7 @@ func (api *DnaApi) GetBalance(address common.Address) Balance {
return Balance{
Stake: blockchain.ConvertToFloat(state.State.GetStakeBalance(address)),
ReplenishedStake: blockchain.ConvertToFloat(state.State.GetReplenishedStakeBalance(address)),
LockedStake: blockchain.ConvertToFloat(state.State.GetLockedStake(address)),
Balance: blockchain.ConvertToFloat(state.State.GetBalance(address)),
Nonce: nonce,
MempoolNonce: state.NonceCache.GetNonce(address, currentEpoch),
Expand Down Expand Up @@ -289,6 +291,7 @@ type Identity struct {
ProfileHash string `json:"profileHash"`
Stake decimal.Decimal `json:"stake"`
ReplenishedStake decimal.Decimal `json:"replenishedStake"`
LockedStake decimal.Decimal `json:"lockedStake"`
Invites uint8 `json:"invites"`
Age uint16 `json:"age"`
State string `json:"state"`
Expand Down Expand Up @@ -496,6 +499,7 @@ func convertIdentity(currentEpoch uint16, address common.Address, data state.Ide
State: s,
Stake: blockchain.ConvertToFloat(data.Stake),
ReplenishedStake: blockchain.ConvertToFloat(data.ReplenishedStake()),
LockedStake: blockchain.ConvertToFloat(data.LockedStake()),
Age: age,
Invites: data.Invites,
ProfileHash: profileHash,
Expand Down
33 changes: 28 additions & 5 deletions blockchain/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,7 @@ func (chain *Blockchain) applyNewEpoch(appState *appstate.AppState, block *types
validationResult := chain.applyNewEpochFn(block.Height(), appState, statsCollector)
networkSize, validationResults, pools, failed := validationResult.IdentitiesCount, validationResult.ShardResults, validationResult.Pools, validationResult.Failed
totalInvitesCount := float32(networkSize) * chain.config.Consensus.InvitesPercent
totalNewbies, totalVerified, totalSuspended, newbiesByShard, verifiedByShard, suspendedByShard := setNewIdentitiesAttributes(appState, totalInvitesCount, networkSize, pools, failed, validationResults, statsCollector)
totalNewbies, totalVerified, totalSuspended, newbiesByShard, verifiedByShard, suspendedByShard := setNewIdentitiesAttributes(appState, chain.config.Consensus.UnlockStakeAge, totalInvitesCount, networkSize, pools, failed, validationResults, statsCollector)
epochBlock := appState.State.EpochBlock()
if !failed {
var epochDurations []uint32
Expand Down Expand Up @@ -821,7 +821,7 @@ func setInvites(appState *appstate.AppState, identitiesWithInvites []identityWit
collector.SetMinScoreForInvite(statsCollector, lastScore)
}

func setNewIdentitiesAttributes(appState *appstate.AppState, totalInvitesCount float32, networkSize int, pools map[common.Address]struct{}, validationFailed bool, validationResults map[common.ShardId]*types.ValidationResults, statsCollector collector.StatsCollector) (int, int, int, map[common.ShardId]int, map[common.ShardId]int, map[common.ShardId]int) {
func setNewIdentitiesAttributes(appState *appstate.AppState, unlockStakeAge uint8, totalInvitesCount float32, networkSize int, pools map[common.Address]struct{}, validationFailed bool, validationResults map[common.ShardId]*types.ValidationResults, statsCollector collector.StatsCollector) (int, int, int, map[common.ShardId]int, map[common.ShardId]int, map[common.ShardId]int) {
_, flips := common.NetworkParams(networkSize)
identityFlags := calculateNewIdentityStatusFlags(validationResults)

Expand Down Expand Up @@ -850,6 +850,9 @@ func setNewIdentitiesAttributes(appState *appstate.AppState, totalInvitesCount f
if undelegationEpoch := identity.UndelegationEpoch(); undelegationEpoch > 0 && epoch-undelegationEpoch >= 2 {
appState.State.SetUndelegationEpoch(addr, 0)
}
if !common.ZeroOrNil(identity.LockedStake()) && epoch+1-identity.Birthday >= uint16(unlockStakeAge) {
appState.State.SubLockedStake(addr, identity.LockedStake())
}
if !validationFailed {
switch identity.State {
case state.Verified, state.Human:
Expand Down Expand Up @@ -1569,9 +1572,16 @@ func (chain *Blockchain) applyTxOnState(tx *types.Transaction, context *txExecut

appState.IdentityState.Remove(sender)
stake := stateDB.GetStakeBalance(sender)
stakeToBurn := stateDB.GetLockedStake(sender)
stakeToBalance := new(big.Int).Set(stake)
if stakeToBurn.Sign() > 0 {
stakeToBalance.Sub(stakeToBalance, stakeToBurn)
collector.AddKilledBurntCoins(statsCollector, sender, stakeToBurn)
}
stateDB.SubStake(sender, stake)
stateDB.SubLockedStake(sender, stakeToBurn)
stateDB.SubReplenishedStake(sender, stateDB.GetReplenishedStakeBalance(sender))
stateDB.AddBalance(sender, stake)
stateDB.AddBalance(sender, stakeToBalance)
collector.AddKillTxStakeTransfer(statsCollector, tx, stake)
case types.KillInviteeTx:
collector.BeginTxBalanceUpdate(statsCollector, tx, appState)
Expand All @@ -1587,7 +1597,13 @@ func (chain *Blockchain) applyTxOnState(tx *types.Transaction, context *txExecut
keepDelegationNonce: true,
})
appState.IdentityState.Remove(*tx.To)
collector.AddKilledBurntCoins(statsCollector, *tx.To, stateDB.GetStakeBalance(*tx.To))
stake := stateDB.GetStakeBalance(*tx.To)
if chain.config.Consensus.EnableUpgrade12 {
stateDB.SubStake(*tx.To, stake)
stateDB.SubReplenishedStake(*tx.To, stateDB.GetReplenishedStakeBalance(*tx.To))
stateDB.SubLockedStake(*tx.To, stateDB.GetLockedStake(*tx.To))
}
collector.AddKilledBurntCoins(statsCollector, *tx.To, stake)
if sender != stateDB.GodAddress() && stateDB.GetIdentityState(sender).VerifiedOrBetter() {
stateDB.AddInvite(sender, 1)
}
Expand All @@ -1608,10 +1624,17 @@ func (chain *Blockchain) applyTxOnState(tx *types.Transaction, context *txExecut
stateDB.SetMetadata(*tx.To, identityUpdateHookCtx)
appState.IdentityState.Remove(*tx.To)
stake := stateDB.GetStakeBalance(*tx.To)
lockedStake := stateDB.GetLockedStake(*tx.To)
stateDB.SubStake(*tx.To, stake)
stateDB.SubReplenishedStake(*tx.To, stateDB.GetReplenishedStakeBalance(*tx.To))
stateDB.SubLockedStake(*tx.To, lockedStake)
if delegatorPrevState.VerifiedOrBetter() || chain.config.Consensus.EnableUpgrade11 && (delegatorPrevState == state.Suspended || delegatorPrevState == state.Zombie) {
stateDB.AddBalance(sender, stake)
stakeToBalance := new(big.Int).Set(stake)
if lockedStake.Sign() > 0 {
stakeToBalance.Sub(stakeToBalance, lockedStake)
collector.AddKilledBurntCoins(statsCollector, *tx.To, lockedStake)
}
stateDB.AddBalance(sender, stakeToBalance)
} else {
collector.AddKilledBurntCoins(statsCollector, *tx.To, stake)
}
Expand Down
85 changes: 77 additions & 8 deletions blockchain/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,13 +312,18 @@ func Test_ApplyKillTx(t *testing.T) {
sender := crypto.PubkeyToAddress(key.PublicKey)

balance := new(big.Int).Mul(big.NewInt(50), common.DnaBase)

stake := new(big.Int).Mul(big.NewInt(10000), common.DnaBase)
replenishedStake := new(big.Int).Mul(big.NewInt(1000), common.DnaBase)
lockedStake := new(big.Int).Mul(big.NewInt(200), common.DnaBase)

account := appState.State.GetOrNewAccountObject(sender)
account.SetBalance(balance)

id := appState.State.GetOrNewIdentityObject(sender)
id.SetStake(stake)
id.SetReplenishedStake(replenishedStake)
id.AddLockedStake(lockedStake)
id.SetState(state.Verified)

id.SetProfileHash([]byte{0x1})
Expand Down Expand Up @@ -370,8 +375,10 @@ func Test_ApplyKillTx(t *testing.T) {

require.Equal(big.NewInt(0), fee)
require.Equal(state.Killed, appState.State.GetIdentityState(sender))
require.Equal(new(big.Int).Add(balance, stake), appState.State.GetBalance(sender))
require.Equal(new(big.Int).Mul(big.NewInt(9850), common.DnaBase), appState.State.GetBalance(sender))
require.True(common.ZeroOrNil(appState.State.GetStakeBalance(sender)))
require.Zero(appState.State.GetReplenishedStakeBalance(sender).Sign())
require.Zero(appState.State.GetLockedStake(sender).Sign())

identity := appState.State.GetIdentity(sender)
require.NotNil(identity.Metadata())
Expand Down Expand Up @@ -1148,7 +1155,7 @@ func Test_setNewIdentitiesAttributes(t *testing.T) {
}
require.NoError(s.Commit(nil))

setNewIdentitiesAttributes(s, 12, 100, make(map[common.Address]struct{}), false, map[common.ShardId]*types.ValidationResults{}, nil)
setNewIdentitiesAttributes(s, chain.config.Consensus.UnlockStakeAge, 12, 100, make(map[common.Address]struct{}), false, map[common.ShardId]*types.ValidationResults{}, nil)

for _, item := range identities {
var addr common.Address
Expand All @@ -1167,13 +1174,13 @@ func Test_setNewIdentitiesAttributes(t *testing.T) {
require.Equal(uint8(1), s.State.GetInvites(common.Address{0x9}))

s.Reset()
setNewIdentitiesAttributes(s, 1, 100, make(map[common.Address]struct{}), false, map[common.ShardId]*types.ValidationResults{}, nil)
setNewIdentitiesAttributes(s, chain.config.Consensus.UnlockStakeAge, 1, 100, make(map[common.Address]struct{}), false, map[common.ShardId]*types.ValidationResults{}, nil)
require.Equal(uint8(1), s.State.GetInvites(common.Address{0x1}))
require.Equal(uint8(0), s.State.GetInvites(common.Address{0x7}))
require.Equal(uint8(0), s.State.GetInvites(common.Address{0x8}))

s.Reset()
setNewIdentitiesAttributes(s, 5, 100, make(map[common.Address]struct{}), false, map[common.ShardId]*types.ValidationResults{}, nil)
setNewIdentitiesAttributes(s, chain.config.Consensus.UnlockStakeAge, 5, 100, make(map[common.Address]struct{}), false, map[common.ShardId]*types.ValidationResults{}, nil)
require.Equal(uint8(2), s.State.GetInvites(common.Address{0x1}))
require.Equal(uint8(1), s.State.GetInvites(common.Address{0x5}))
require.Equal(uint8(1), s.State.GetInvites(common.Address{0x7}))
Expand All @@ -1182,7 +1189,7 @@ func Test_setNewIdentitiesAttributes(t *testing.T) {
require.Equal(uint8(0), s.State.GetInvites(common.Address{0x4}))

s.Reset()
setNewIdentitiesAttributes(s, 15, 100, make(map[common.Address]struct{}), false, map[common.ShardId]*types.ValidationResults{}, nil)
setNewIdentitiesAttributes(s, chain.config.Consensus.UnlockStakeAge, 15, 100, make(map[common.Address]struct{}), false, map[common.ShardId]*types.ValidationResults{}, nil)
require.Equal(uint8(2), s.State.GetInvites(common.Address{0x1}))
require.Equal(uint8(2), s.State.GetInvites(common.Address{0x5}))
require.Equal(uint8(1), s.State.GetInvites(common.Address{0x4}))
Expand All @@ -1193,7 +1200,7 @@ func Test_setNewIdentitiesAttributes(t *testing.T) {
require.Equal(uint8(1), s.State.GetInvites(common.Address{0xd}))

s.Reset()
setNewIdentitiesAttributes(s, 20, 100, make(map[common.Address]struct{}), false, map[common.ShardId]*types.ValidationResults{}, nil)
setNewIdentitiesAttributes(s, chain.config.Consensus.UnlockStakeAge, 20, 100, make(map[common.Address]struct{}), false, map[common.ShardId]*types.ValidationResults{}, nil)
require.Equal(uint8(2), s.State.GetInvites(common.Address{0x1}))
require.Equal(uint8(2), s.State.GetInvites(common.Address{0x5}))
require.Equal(uint8(1), s.State.GetInvites(common.Address{0x4}))
Expand All @@ -1204,7 +1211,7 @@ func Test_setNewIdentitiesAttributes(t *testing.T) {
require.Equal(uint8(1), s.State.GetInvites(common.Address{0xd}))

s.Reset()
setNewIdentitiesAttributes(s, 2, 100, make(map[common.Address]struct{}), false, map[common.ShardId]*types.ValidationResults{}, nil)
setNewIdentitiesAttributes(s, chain.config.Consensus.UnlockStakeAge, 2, 100, make(map[common.Address]struct{}), false, map[common.ShardId]*types.ValidationResults{}, nil)
require.Equal(uint8(1), s.State.GetInvites(common.Address{0x1}))
require.Equal(uint8(1), s.State.GetInvites(common.Address{0x7}))
require.Equal(uint8(1), s.State.GetInvites(common.Address{0x8}))
Expand Down Expand Up @@ -1243,7 +1250,15 @@ func Test_setNewIdentitiesAttributes(t *testing.T) {
s.State.IncEpoch()
}

totalNewbies, totalVerified, totalSuspended, newbiesByShard, verifiedByShard, suspendedByShard := setNewIdentitiesAttributes(s, 6, 100, make(map[common.Address]struct{}), false, map[common.ShardId]*types.ValidationResults{}, nil)
chain.config.Consensus.UnlockStakeAge = 3

s.State.SetBirthday(common.Address{0x1}, 1)
s.State.AddLockedStake(common.Address{0x1}, big.NewInt(1))

s.State.SetBirthday(common.Address{0x2}, 2)
s.State.AddLockedStake(common.Address{0x2}, big.NewInt(1))

totalNewbies, totalVerified, totalSuspended, newbiesByShard, verifiedByShard, suspendedByShard := setNewIdentitiesAttributes(s, chain.config.Consensus.UnlockStakeAge, 6, 100, make(map[common.Address]struct{}), false, map[common.ShardId]*types.ValidationResults{}, nil)
discriminationStakeThreshold := balanceShards(s, totalNewbies, totalVerified, totalSuspended, newbiesByShard, verifiedByShard, suspendedByShard)
require.Zero(discriminationStakeThreshold.Cmp(big.NewInt(8)))
applyDiscriminationStakeThreshold(s, discriminationStakeThreshold)
Expand Down Expand Up @@ -1294,6 +1309,9 @@ func Test_setNewIdentitiesAttributes(t *testing.T) {
identity = s.State.GetIdentity(common.Address{0xf})
require.True(identity.IsDiscriminated(s.State.DiscriminationStakeThreshold(), s.State.Epoch()))
require.True(identity.IsDiscriminatedStake(s.State.DiscriminationStakeThreshold()))

require.Zero(s.State.GetLockedStake(common.Address{0x1}).Sign())
require.Positive(s.State.GetLockedStake(common.Address{0x2}).Sign())
}

func Test_ClearDustAccounts(t *testing.T) {
Expand Down Expand Up @@ -1575,6 +1593,57 @@ func Test_Delegation(t *testing.T) {
require.False(t, appState.ValidatorsCache.IsOnlineIdentity(pool2))
}

func Test_ApplyKillDelegatorTx(t *testing.T) {
require := require.New(t)
chain, appState, _, _ := NewTestBlockchain(true, nil)
validation.SetAppConfig(chain.config)
defer chain.SecStore().Destroy()

key, _ := crypto.GenerateKey()
sender := crypto.PubkeyToAddress(key.PublicKey)
delegator := tests.GetRandAddr()

appState.State.GetOrNewAccountObject(sender).SetBalance(new(big.Int).Mul(big.NewInt(50), common.DnaBase))
delegatorIdentity := appState.State.GetOrNewIdentityObject(delegator)
delegatorIdentity.SetState(state.Verified)
delegatorIdentity.SetStake(new(big.Int).Mul(big.NewInt(10000), common.DnaBase))
delegatorIdentity.SetReplenishedStake(new(big.Int).Mul(big.NewInt(1000), common.DnaBase))
delegatorIdentity.AddLockedStake(new(big.Int).Mul(big.NewInt(200), common.DnaBase))
delegatorIdentity.SetDelegatee(sender)

chain.appState.State.SetFeePerGas(big.NewInt(1e+9))

tx := &types.Transaction{
Type: types.KillDelegatorTx,
To: &delegator,
AccountNonce: 1,
MaxFee: big.NewInt(1e+15),
}

signedTx, _ := types.SignTx(tx, key)

fee := fee2.CalculateFee(chain.appState.ValidatorsCache.NetworkSize(), chain.appState.State.FeePerGas(), tx)
require.NoError(validation.ValidateTx(chain.appState, signedTx, appState.State.FeePerGas(), validation.InBlockTx))
context := &txExecutionContext{
appState: chain.appState,
}
chain.applyTxOnState(signedTx, context)

require.Equal(state.Killed, appState.State.GetIdentityState(delegator))
require.Zero(appState.State.GetStakeBalance(delegator).Sign())
require.Zero(appState.State.GetReplenishedStakeBalance(delegator).Sign())
require.Zero(appState.State.GetLockedStake(delegator).Sign())
require.Zero(new(big.Int).Sub(new(big.Int).Mul(big.NewInt(9850), common.DnaBase), fee).Cmp(appState.State.GetBalance(sender)))

require.NoError(appState.Commit(nil))

require.Equal(state.Undefined, appState.State.GetIdentityState(delegator))
require.Zero(appState.State.GetStakeBalance(delegator).Sign())
require.Zero(appState.State.GetReplenishedStakeBalance(delegator).Sign())
require.Zero(appState.State.GetLockedStake(delegator).Sign())
require.Zero(new(big.Int).Sub(new(big.Int).Mul(big.NewInt(9850), common.DnaBase), fee).Cmp(appState.State.GetBalance(sender)))
}

func TestBalance_shards_reducing(t *testing.T) {
db := dbm.NewMemDB()
bus := eventbus.New()
Expand Down
3 changes: 3 additions & 0 deletions blockchain/rewards.go
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,9 @@ func addInvitationReward(appState *appstate.AppState, config *config.ConsensusCo
collector.BeginEpochRewardBalanceUpdate(statsCollector, addr, addr, appState)
appState.State.AddStake(addr, stake)
appState.State.AddReplenishedStake(addr, stake)
if config.EnableUpgrade12 {
appState.State.AddLockedStake(addr, stake)
}
collector.CompleteBalanceUpdate(statsCollector, appState)
collector.AddMintedCoins(statsCollector, stake)
collector.AddInviteeReward(statsCollector, addr, stake, age, txHash, epochHeight)
Expand Down
Loading